I have an ArrayList filled with some custom POJO's that I'd like to persist when the user switches screen orientation (onSaveInstanceState) and when e.g. the user hits the back button (onPause).
As far as I know, the SharedPreferences can only hold primitive data types and bundles can not hold references to generic ArrayLists.
Any tips on how to tackle this?
Regards,
Marcus
1- create a class and put everything you want to store for example arraylist of your POJO and make that class implement Serializable interface.
class MyBundle implements Serializable {
ArrayList<POJO> mPOJO;
MyBundle( ArrayList<POJO> pojo){
mPOJO= pojo;
}
}
2- write it to file:
ObjectOutputStream oos = null;
FileOutputStream fout = null;
try{
FileOutputStream fout = new FileOutputStream("Your file path");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(mb); // mb is an instance of MyBundle
} catch (Exception ex) {
e.printStackTrace();
}finally {
if(oos != null){
oos.close();
}
}
and to get back everything:
ObjectInputStream objectinputstream = null;
try {
streamIn = new FileInputStream("Your file address");
objectinputstream = new ObjectInputStream(streamIn);
MyBundle mb = (MyBundle) objectinputstream.readObject();
} catch (Exception e) {
e.printStackTrace();
}finally {
if(objectinputstream != null){
objectinputstream .close();
}
}
i don't know this is correct method or not but i handle this like this this always success while your app lost all cache data itself then also u can get back serializable object->
for generic ArrayLists always use serializable
just look at once http://developer.android.com/reference/java/io/Serializable.html
Try converting the List to Json using Gson or Jackson.
Store the string in the Shared preference. some thing like below code
String listString = gsonSD.toJson(list<object> instance);
SharedPreferences storeDataPref = getContext().getSharedPreferences("list_store_pref", Context.MODE_PRIVATE);
Editor storeDataEditor = storeDataPref.edit();
storeDataEditor.putString("liststringdata", listString).apply();
Related
I'm new serialization (and programming in general) so i don't really know what happens under the hood. I'm making an email client program and the class
FileHandlerObject is used to write and read email messages that i send. I maintain an ArrayList and it is read, updated with new email and then written again. I don't understand why it throws an InvalidClassException because the file contains an ArrayList so the casting should be no problem.
I'm maintaining an ArrayList because the writeObject() truncates the file every time it writes.(Let me know if im wrong here)
class FileHandlerObject implements MyFileHandler<EmailMessage>{
public void write(EmailMessage input){
try
{
FileInputStream fileInputStream=new FileInputStream("emails.ser");
ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);
#SuppressWarnings("unchecked")
ArrayList<EmailMessage> messagelist= (ArrayList<EmailMessage>) objectInputStream.readObject();
objectInputStream.close();
messagelist.add(input);
FileOutputStream fileOutputStream = new FileOutputStream("emails.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(messagelist);
objectOutputStream.flush();
objectOutputStream.close();
}
catch(FileNotFoundException f)
{
try{
FileOutputStream fileOutputStream = new FileOutputStream("emails.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
ArrayList<EmailMessage> messagelist=new ArrayList<EmailMessage>();
messagelist.add(input);
objectOutputStream.writeObject(messagelist);
objectOutputStream.flush();
objectOutputStream.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
catch(IOException e){
e.printStackTrace();
}
catch(ClassNotFoundException e)
{
e.printStackTrace();
}
}
public ArrayList<EmailMessage> read(){
try{
FileInputStream fileInputStream=new FileInputStream("emails.ser");
ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);
#SuppressWarnings("unchecked")
ArrayList<EmailMessage> messagelist= (ArrayList<EmailMessage>) objectInputStream.readObject();
objectInputStream.close();
return messagelist;
}
catch(IOException e)
{
e.printStackTrace();
}
catch(ClassNotFoundException c)
{
c.printStackTrace();
}
catch(ClassCastException c)
{
c.printStackTrace();
}
return null;
}
As the docs explain, this means one of a few things. The code you pasted does not give any insights about which of these mistakes you've made.
You saved some emails. Then, you edited the EmailMessage class, for example by adding a method or changing the name of a field, and then you ran the code again which ends up reading that file that was 'saved' with an old version of EmailMessage and which you are now trying to read with a new version. The java serialization mechanism can theoretically deal with versioning schemes but in practice it's a disaster. Ditch serialization, write your own protocol instead.
The EmailMessage class does not implement Serializable and does not have a no-args constructor. Give it a no-args constructor, and add implements Serializable to it.
Your classpath is incomplete; EmailMessage contains fields or methods that refer to some other class you wrote and it is not available on the classpath right now. Fix your classpath.
For my android studio project I need to serialize a User object. The idea is to save and load this object, so when the application is closes (gets destroyed) the objects and it's attributes are saved and stored in a file. My problem is that I'm getting an error with loading the object. It's says there is no such file or directory named 'save.bin', although I tried to create it. Here is my code:
public class SaveAndLoadManager{
public void save(Object object){
Log.d("Save", "saving object");
try{
File userInfo = new File("save.bin");
FileOutputStream fileOutputStream = new FileOutputStream(userInfo);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(object);
objectOutputStream.close();
Log.d("Save", "object saved");
}catch(FileNotFoundException e){
Log.e("Save", e.getMessage());
}catch(IOException e){
Log.e("Save", e.getMessage());
}
}
public Object load(){
Log.d("Load", "loading object");
Object object = null;
try{
FileInputStream fileInputStream = new FileInputStream("save.bin");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
object = objectInputStream.readObject();
objectInputStream.close();
Log.d("Load", "object loaded");
}catch(FileNotFoundException e){
Log.e("Load", e.getMessage());
}catch(StreamCorruptedException e){
Log.e("Load", e.getMessage());
}catch(Exception e){
Log.e("Load", e.getMessage());
}
return object;
}
}
How do I properly create a directory?
Assuming that User is implementing Serialazible
File userInfo = new File("save.bin");
you can think about the above line, like a file object, called "save.bin", stored in the root, (/) on the the filesystem. This location is not writable by your application. Your application has the permission two write locations, one stored under /data/data/yourapp, and the other stored on /sdcard/Android/data/yourapp. Both should be access through the public api. E.g. you can use the pair openFileOutput/openFileInput to retrieve the FileOutputStream/FileInputStream
FileOutputStream fileOutputStream = openFileOutput("save.bin", Context.MODE_PRIVATE)
FileInputStream fileInputStream = openFileInput("save.bin");
As mentioned by #androidLerner in his comment,
you can find more info here,
I have admin account which should be able to add many users to a .dat file. Then I want to retrieve all the objects from the .dat file into a list for further programming.
public class User implements Serializable { //get and set methods }
This is hwo I am writing each object to the .dat file
public void addNewUser() throws Exception {
User newUser=new User();
newUser.name="test";
newUser.position="admin";
FileOutputStream outStream = new FileOutputStream("Users.dat", true);
ObjectOutputStream objectOutputFile = new ObjectOutputStream(outStream);
// Write the object to the file.
objectOutputFile.writeObject(newUser);
// Close the file.
objectOutputFile.close();
}
How can retrieve all the objects from the .dat file into ArrayList??
public class displayUsers { **//what to do??** }
You can either write the list object and read it as list. But since you're writing user objects individually, you can do something like this -
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Users.dat"));
Object object = null;
while ((object = ois.readObject()) != null) {
if (object instanceof User) {
User user = (User) object;
list.add(user);
}
}
Of course, you would need to take care of exceptions (like EOFException).
Generally it is bad practice to concatenate individual ObjectOutputStreams in a file without adding any lengths or delimiters. So better write all objects in one pass (and use ObjectOutputStream.reset in case your process is long-running and you fear memory leaks (otherwise ObjectOutputStream will keep a reference to every object it serialized before) or add them to a List and write it.
If you have to write it in multiple passes, I'd suggest to write the individual objects to a ByteArrayOutputStream first, and then use DataOutputStream to write the array prefixed by its length. That way, you can use DataInputStream to get out the individual byte arrays and use ByteArrayInputStream to deserialize them.
In case this does not work, you can try this solution (depending on the lookahead used by ObjectInputStream, this might not work for more complex objects with custom serialization formats, though, so use at your own risk):
public static void displayUsers() throws Exception {
FileInputStream fiis = new FileInputStream("Users.dat");
InputStream fis = new FilterInputStream(fiis) {
#Override
public void close() throws IOException {
// ignore
}
};
try {
while (true) {
ObjectInputStream in = new ObjectInputStream(fis);
User user = (User) in.readObject();
in.close();
System.out.println(user.name + "/" + user.position);
}
} catch (EOFException ex) {
// done
}
fiis.close();
}
List<User> listOfUser = new ArrayList<User>();
ObjectInputStream input = null;
try {
while (true) {
input = new ObjectInputStream(new FileInputStream("Users.dat"));
listOfUser.add(input.readObject());
}
}
catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
input.close();
}
I have this serializable class, wich i use to store on a binary file an ArrayList of Strings.
public class SaveState implements Serializable{
public static ArrayList <String> favoriteBusStopNumbers = new ArrayList<String>();
public static SaveState instance=new SaveState();
}
I'm using this method to store the instance with the arrayList of strings once this array is full with the data i must store:
public static void saveData(){
ObjectOutput out;
try {
//primero comprobamos si existe el directorio, y si no, lo creamos.
File folder = new File(Environment.getExternalStorageDirectory() + DIRECTORY_NAME);
if(!folder.exists())
folder.mkdirs();
File outFile = new File(Environment.getExternalStorageDirectory(), DIRECTORY_NAME+"appSaveState.data");
out = new ObjectOutputStream(new FileOutputStream(outFile));
out.writeObject(SaveState.instance);
out.close();
} catch (Exception e) {e.printStackTrace();}
}
And finally, i use this method on the init of my app to load the file and fill my SaveState.instance variable with the previously stored data:
public static void loadData(){
ObjectInput in;
try {
File inFile = new File(Environment.getExternalStorageDirectory(), DIRECTORY_NAME+"appSaveState.data");
in = new ObjectInputStream(new FileInputStream(inFile));
SaveState.instance=(SaveState) in.readObject();
in.close();
} catch (Exception e) {e.printStackTrace();}
}
When i save the data, the file is being created correctly with the object fill of data, i know it because the file has more than 0KB of disc space. But something is going wrong here because when i start my app and load the data, my SaveState.instance variable gets an empty ArrayList of strings....... then ¿what is wrong in the code?
Thanks
Your arraylist is static. Static variables are associated with the class and not with the object. So they dont get serialized.
If you do want to serialize static variables, you have to override readObject and writeObject.
Here is some additional info -- http://java.sun.com/developer/technicalArticles/Programming/serialization/
Best solution here is to reconsider your data structure and make the arraylist non-static if its saving the state of an object.
EDIT: Alternatively, you could serialize just the arraylist ( as #Ted suggests below )
The problem is that variables marked as static will not be serialized except you implement
private void readObject(ObjectInputStream ois){}
and
private void writeObject(ObjectOutputStream oos){}
ObjectOutputStream - JavaDoc:
The default serialization mechanism for an object writes the class of
the object, the class signature, and the values of all non-transient
and non-static fields.
Since the only data kept in SaveState is a static array, you'll have more success just serializing and deserializing the array:
public static void saveData(){
ObjectOutput out;
try {
//primero comprobamos si existe el directorio, y si no, lo creamos.
File folder = new File(Environment.getExternalStorageDirectory() + DIRECTORY_NAME);
if(!folder.exists())
folder.mkdirs();
File outFile = new File(Environment.getExternalStorageDirectory(), DIRECTORY_NAME+"appSaveState.data");
out = new ObjectOutputStream(new FileOutputStream(outFile));
out.writeObject(SaveState.favoriteBusStopNumbers);
out.close();
} catch (Exception e) {e.printStackTrace();}
}
#SuppressWarnings("unchecked")
public static void loadData(){
ObjectInput in;
try {
File inFile = new File(Environment.getExternalStorageDirectory(), DIRECTORY_NAME+"appSaveState.data");
in = new ObjectInputStream(new FileInputStream(inFile));
SaveState.favoriteBusStopNumbers=(ArrayList<String>) in.readObject();
in.close();
} catch (Exception e) {e.printStackTrace();}
}
(You need to suppress the warning about casting to a generic type.)
You don't really need the SaveState.instance field at all.
I want to save ArrayList<CustomClass>-object to somewhere in Android storage to retrieve quickly and display data in it.
Is this possible or not? If yes, then which technique will be suitable, SQLite or external storage?
example.
public class MyClass implements Serializable
{
private static final long serialVersionUID = 1L;
public String title;
public String startTime;
public String endTime;
public String day;
public boolean classEnabled;
public MyClass(String title, String startTime, boolean enable) {
this.title = title;
this.startTime = startTime;
this.classEnabled = enable;
}
public boolean saveObject(MyClass obj) {
final File suspend_f=new File(SerializationTest.cacheDir, "test");
FileOutputStream fos = null;
ObjectOutputStream oos = null;
boolean keep = true;
try {
fos = new FileOutputStream(suspend_f);
oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
} catch (Exception e) {
keep = false;
} finally {
try {
if (oos != null) oos.close();
if (fos != null) fos.close();
if (keep == false) suspend_f.delete();
} catch (Exception e) { /* do nothing */ }
}
return keep;
}
public MyClass getObject(Context c) {
final File suspend_f=new File(SerializationTest.cacheDir, "test");
MyClass simpleClass= null;
FileInputStream fis = null;
ObjectInputStream is = null;
try {
fis = new FileInputStream(suspend_f);
is = new ObjectInputStream(fis);
simpleClass = (MyClass) is.readObject();
} catch(Exception e) {
String val= e.getMessage();
} finally {
try {
if (fis != null) fis.close();
if (is != null) is.close();
} catch (Exception e) { }
}
return simpleClass;
}
and calling from activity
if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"MyCustomObject");
else
cacheDir= getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
MyClass m = new MyClass("umer", "asif", true);
boolean result = m.saveObject(m);
if(result)
Toast.makeText(this, "Saved object", Toast.LENGTH_LONG).show();
else
Toast.makeText(this, "Error saving object", Toast.LENGTH_LONG).show();
MyClass m = new MyClass();
MyClass c = m.getObject(this);
if(c!= null)
Toast.makeText(this, "Retrieved object", Toast.LENGTH_LONG).show();
else
Toast.makeText(this, "Error retrieving object", Toast.LENGTH_LONG).show();
dont forget to use write_external_storage permissions in manifest file.
This problem isn't really android specific. I mean if you know how to serialize your data either via java.io.Serializable or you have a custom persistence format, you just need to know where to store it.
You can grab a file on the local device via
FileOutputStream stream = null;
try{
stream = mContext.openFileOutput("name.data", Context.MODE_PRIVATE);
ObjectOutputStream dout = new ObjectOutputStream(stream);
dout.writeObject(myArrayList);
dout.flush();
stream.getFD().sync();
} catch(IOException e) { //Do something intelligent
} finally {
stream.close();
}
You will have to use openFileInput() later to read the data back.
Or you can grab External Storage. This is similar however you have to guarantee that it even exists. Like is the external storage connected and even able to be written to. Since you are writing a data structure here and usually external storage is world readable I don't think this is a good idea for your intended purposes (just from what you have put so far).
If your data is structured and is going to be queried many times and it might be rather big to always load up then consider using the sql lite tools that are part of the OS. However I am assuming you don't need this either as a simple list is just that, a linear structures which you could probably seek through in a file (assuming it is less than 1MB of data :)
This issue can be solved by singleton class here in which you can set/get any object eg arrayList in any time. If you used Android Service then this solutuion is not suitable.
Perhaps the solution to your problem can be found here.