I need to save a java object to internal memory (a high-level collection of different server responses if to be specific). Now I use this code:
public void write(Context context) {
FileOutputStream fos = null;
try {
fos = context.openFileOutput(BACKSTACK_FILENAME, Context.MODE_PRIVATE);
} catch (FileNotFoundException e) { e.printStackTrace(); }
try {
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(backStack);
} catch (IOException e) {
L.t("failed to write backstack: " + e.toString()); e.printStackTrace();
} finally {
try { if (fos != null) fos.close(); } catch (IOException e) { }
}
}
public void read(Context context) {
FileInputStream fis = null;
try {
fis = context.openFileInput(BACKSTACK_FILENAME);
} catch (FileNotFoundException e) { e.printStackTrace();}
try {
ObjectInputStream ois = new ObjectInputStream(fis);
Deque<FragmentInfo> list = (Deque<FragmentInfo>) ois.readObject();
L.t(list.toString());
backStack = list;
} catch (IOException | ClassNotFoundException e) {
L.t("failed to read backstack" + e.toString()); e.printStackTrace();
} finally {
try { if (fis != null) fis.close(); } catch (IOException e) { }
}
}
Since we have context here, UI thread hangs(lags) white operation is performed.
And the larger object becomes, the worse it looks. So the question is:
Is there any way to save java object to internal memory asynchroneously?
Please don't recommend to use a database for such a simple task.
Saving to internal memory won't help. The real problem is that you are doing too much work on the event listener thread. You will have the same problem if you save lots of stuff to a file, a database, "internal memory" .... or to anything else. Serialization is relatively expensive no matter how you do it, and no matter where you save the results of the serialization.
The solution is to do the work using an AsyncTask.
Related
for now i´ve got this two arrays that i want to save in a file
int var[][] = new int var[6][3]
int var_2[][] = new int var[7][5];
So whats the best way to write this into a file?
I´ve seen some examples like this
try {
ObjectOutputStream objOut = new ObjectOutputStream(new
FileOutputStream("data.dat"));
objOut.writeObject(var_1);
objOut.writeObject(var_2);
objOut.close();
}
catch (Exception e) {
e.printStackTrace();
}
Am i doing this right? And if i am, how then do i read it right to my variables?
You're doing it right in concept, but there's room for improvement. First, you should use the Android logging facility (android.util.Log) rather than e.printStackTrace(). Also, if your minSdkLevel is 19 or more, I recommend that you use the try-with-resources statement:
try (ObjectOutputStream objOut = new ObjectOutputStream(new
FileOutputStream("data.dat")))
{
objOut.writeObject(var_1);
objOut.writeObject(var_2);
} catch (Exception e) {
Log.e(LOG_TAG, "Could not save data", e);
}
(LOG_TAG is just some String you define to identify your class's output.) If you can't use try-with-resources, I recommend using a finally clause to close the stream:
ObjectOutputStream objOut = null;
try {
objOut = new ObjectOutputStream(new FileOutputStream("data.dat"))
objOut.writeObject(var_1);
objOut.writeObject(var_2);
} catch (Exception e) {
Log.e(LOG_TAG, "Could not save data", e);
} finally {
if (objOut != null) {
try {
objOut.close();
} catch (Exception e) {
Log.e(LOG_TAG, "Could not close output file", e);
}
}
}
This ensures that the output stream gets closed even if an exception is thrown in the calls to writeObject.
To read the data back in, just open an ObjectInputStream to the same file and read the data in the same order in which you wrote them.
I made my own data structure called a User. I am trying to store Users in a .bin file using FileInputStream and FileOutputStream. I am successfully storing a single user when a button is pressed, but then when I close the application and go to read my Users and recognize login info, the .bin file turns blank.
I know that the .bin file is not empty after the program closes, but then when I try to read the file I get a EOFException. Here is my code to read objects from the file:
public void loadUsers() {
try {
ObjectInputStream ois = new ObjectInputStream(fi);
boolean endOfFile = false;
while (!endOfFile) {
try {
System.out.println("loaded a user!");
User person = (User) ois.readObject();
Users.addUser(person);
} catch (EOFException e) {
endOfFile = true;
ois.close();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Here is my code to write objects to the file:
public void storeUser(User person) {
boolean endOfFile = false;
try {
new ObjectInputStream(fi).readObject();
} catch (EOFException e) {
endOfFile = true;
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
if (!endOfFile) {
System.out.println("Appending!");
try {
AppendingObjectOutputStream aos = new AppendingObjectOutputStream(fo);
aos.writeObject(person);
aos.flush();
aos.close();
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("Writing a new file!");
try {
ObjectOutputStream oos = new ObjectOutputStream(fo);
oos.writeObject(person);
oos.flush();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
AppendingObjectOutputStream is just a small subclass of ObjectOutputStream I made that overwrites writeStreamHeader() so that there is no header placed in my file when try to write more users. I don't think this is related to the problem because I have yet to write a User onto a file with Users already written on it.
I have a class PDF which implements an interface fileReader.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class PDF implements fileReader {
#Override
public byte[] readFile(File pdfDoc) {
if (!pdfDoc.exists()) {
System.out.println("Could not find" + pdfDoc.getName() + " on the specified path");
return null;
}
FileInputStream fin = null;
try {
fin = new FileInputStream(pdfDoc);
} catch (FileNotFoundException e) {
System.out.println("");
e.printStackTrace();
}
byte fileContent[] = new byte[(int) pdfDoc.length()];
try {
fin.read(fileContent);
} catch (IOException e) {
e.printStackTrace();
}
return fileContent;
}
}
import java.io.File;
public interface fileReader {
<T> T readFile(File fileObject);
}
I notice that there are scope issues for variables fin.
Another implementation I made was:
public byte[] readFile1(File pdfDoc) {
if (!pdfDoc.exists()) {
System.out.println("Could not find" + pdfDoc.getName() + " on the specified path");
return null;
}
FileInputStream fin = null;
try {
fin = new FileInputStream(pdfDoc);
byte fileContent[] = new byte[(int) pdfDoc.length()];
try {
fin.read(fileContent);
} catch (IOException e) {
System.out.println("");
e.printStackTrace();
}
} catch (FileNotFoundException e) {
System.out.println("");
e.printStackTrace();
}
return fileContent;
}
But now I could not access fileContent.
How can I combine the try-catches so that I don't have scope problems?
Can there be a better design approach to this problem? I have to make functions for reading three different types of file.
Since Java 7 you can combine the try-catch as follows:
FileInputStream fin = null;
try {
fin = new FileInputStream(pdfDoc);
byte fileContent[] = new byte[(int) pdfDoc.length()];
fin.read(fileContent);
} catch (IOException | FileNotFoundException e) {
System.out.println("");
e.printStackTrace();
}
Which, in my opinion, makes the code cleaner and variable scopes more obvious.
You can nest the try catch statements:
try {
FileInputStream fin = new FileInputStream(pdfDoc);
byte fileContent[] = new byte[(int) pdfDoc.length()];
try {
fin.read(fileContent);
return fileContent;
} catch (IOException e) {
e.printStackTrace();
} finally {
fin.close();
}
} catch (FileNotFoundException e) {
System.out.println("");
e.printStackTrace();
}
return null;
Note that I added a close() in a finally clause to clean up. And also returning null is probably not what you want in case of error, but that's application specific.
You can have one try with multiple catch blocks.
try {
//do stuff
}
catch (FileNotFoundException e) {
System.out.println("");
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
You can modify this part:
FileInputStream fin = null;
try {
fin = new FileInputStream(pdfDoc);
} catch (FileNotFoundException e) {
System.out.println("");
e.printStackTrace();
}
byte fileContent[] = new byte[(int) pdfDoc.length()];
try {
fin.read(fileContent);
} catch (IOException e) {
e.printStackTrace();
}
By
{
......
FileInputStream fin = null;
byte fileContent[]=null;
try {
fin = new FileInputStream(pdfDoc);
fileContent = new byte[(int) pdfDoc.length()];
fin.read(fileContent);
} catch (FileNotFoundException e) {
System.out.println("");
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
return fileContent
}
I would write like this:
public byte[] readFile(File pdfDoc) {
if (!pdfDoc.exists()) {
System.out.println("Could not find" + pdfDoc.getName() + " on the specified path");
return null;
}
FileInputStream fin = null;
byte fileContent[] = new byte[(int) pdfDoc.length()];
try {
fin = new FileInputStream(pdfDoc);
fin.read(fileContent);
} catch (FileNotFoundException e) {
System.out.println("");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != fin) {
fin.close();
}
}
return fileContent;
}
Since Java 7, there is a nice utility methods for reading the entire content of a file:
return Files.readAllBytes(pdfFile.toPath());
This method will open and close the FileInputStream for you, so you don't need to do this yourself. It throws an IOException if something goes wrong. Usually, it's best to let this exception propagate to the caller, but if you really want to return null in that case, you can accomplish this as follows:
try {
return Files.readAllBytes(pdfFile.toPath());
} catch (IOException e) {
e.printStackTrace();
return null;
}
This also has the nice advantage that the value returned in that case is explicit - or did you really mean to return an array filled with 0 values if the file could no longer be found, as your current code does?
Note that since NoSuchFileException is a subclass of IOException, the catch block will handle both. If you want to handle it differently you can write a separate catch block for the NoSuchFileException:
try {
return Files.readAllBytes(pdfFile.toPath());
} catch (NoSuchFileException e) {
System.err.println("Oh no, the file has disappeared.");
e.printStackTrace();
return null;
} catch (IOException e) {
System.err.println("The file exists, but could not be read.");
e.printStackTrace();
return null;
}
Finally, I should probably mention that your file reading code is incorrect, as InputStream.read() does not necessarily read the entire file at once. That's why it returns the number of bytes read so you can invoke it again for the rest of the file. But as I said, since Java 7 you don't need to use such low level APIs (unless the file is too big to fit into memory, of course).
This is my code:
FileOutputStream fos = null;
DataOutputStream dos = null;
try {
fos = new FileOutputStream(file);
dos = new DataOutputStream(fos);
.... writing to file ....
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (dos != null) {
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
I read that you only need to close DataOutpustStream and it will close all other streams, is this correct? Does it do any harm if i close FileOutputStream anyways? What is the best way to close streams?
No, it doesn't do any harm
You need to close them in the same order that you opened them - think of them as wrappers around each-other. If you create a FileOutputStream and then wrap that in a DataOutputStream, you should close the DataOutputStream first.
But you should use try-with-resources:
try (
final FileOutputStream fos = new FileOutputStream(file);
final DataOutputStream dos = new DataOutputStream(fos);
) {
....writing to file....
} catch (IOException e) {
e.printStackTrace();
}
And if you handle the two exceptions in the same way, you can catch the more general IOException only.
The best way to close streams is when you're using try-resource-blocks.
In most cases streams are closed in a cascading manner. For the answer you have to take a closer look into the API.
I have two methods, one that serialize the Object, and it works ok:
public void record()throws RecordingException
{
ObjectOutputStream outputStream = null;
try
{
outputStream = new ObjectOutputStream(new FileOutputStream("src/data/employee.dat"));
outputStream.writeObject(this);
} catch (FileNotFoundException ex)
{
ex.printStackTrace();
throw new RecordingException(ex);
} catch (IOException ex)
{
ex.printStackTrace();
throw new RecordingException(ex);
}finally
{
try
{
if (outputStream != null) outputStream.close();
} catch (IOException ex){}
}
}
The problem here when deserializing the object, I get EOFException!:
public final User loadObject(UserType usertype) throws InvalidLoadObjectException
{
ObjectInputStream istream = null;
String path = null;
if (usertype == UserType.EMPLOYEE)
{
path = "data/employee.dat";
}else if (usertype == UserType.CUSTOMER)
{
path = "data/customer.dat";
}else
throw new InvalidLoadObjectException("Object is not a sub class of User");
try
{
istream = new ObjectInputStream(ObjectLoader.class.getClassLoader().getResourceAsStream(path));
User u = loadObject(istream);
istream.close();
return u;
}catch (EOFException ex)
{
System.out.println(ex.getMessage());
return null;
}catch(Exception ex)
{
ex.printStackTrace();
throw new InvalidLoadObjectException(ex);
}
}
private User loadObject(ObjectInputStream stream) throws InvalidLoadObjectException
{
try
{
return (User) stream.readObject();
} catch (IOException ex)
{
ex.printStackTrace();
throw new InvalidLoadObjectException(ex);
} catch (ClassNotFoundException ex)
{
ex.printStackTrace();
throw new InvalidLoadObjectException(ex);
}
}
I don't know if this is the cause of your problem, but the code that writes the file has a subtle flaw. In the finally block, you close the stream and ignore any exceptions. If the close() method performs a final flush(), then any exceptions thrown in the flush will go unreported.
Try outputStream.flush() before closing your stream in serialization object.
The file was empty, or didn't contain the full serialization of the object.