0

I have a custom object class Record that implements Parcelable and I'm creating a ListView via ArrayAdapter<Record> I want to be able to save that list so that it automatically loads the next time the user opens the app. The list is populated dynamically and I'm calling my save method everytime a record is added. Then I have a SharedPreference with a boolean value that I set to true so that I know the user has saved some data to load the next time the app is open. Here are my save and load methods:

public void writeRecordsToFile(ArrayAdapter<Record> records) {

    String fileName = Environment.getExternalStorageDirectory().getPath() + "/records.dat";
    try {
        File file = new File(fileName);
        if(!file.exists()){
            file.createNewFile();
        }
        ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(file));
        stream.writeObject(records);
        stream.flush();
        stream.close();
    }
    catch (IOException e){
        Log.e("MyApp","IO Exception: " + e);
    }

    writeSavedState();
}

the writeSavedState() is for my SP

public void readRecordsList() {
    String fileName = Environment.getExternalStorageDirectory().getPath() + "/records.dat";

    try {
        ObjectInputStream inputStream = new ObjectInputStream(getApplicationContext().openFileInput(fileName));
        adapter = (ArrayAdapter<Record>)inputStream.readObject();
        inputStream.close();
    }
    catch (Exception e){
        Log.e("MyApp" , "File Not Found: " + e);
    }
}

When I first open the app I get a message:

E/MyApp﹕ File Not Found: java.lang.IllegalArgumentException: File /storage/emulated/0/records.dat contains a path separator

and then when I add a Record to my list I get the message:

E/MyApp﹕ IO Exception: java.io.IOException: open failed: EACCES (Permission denied)

The second message I'm assuming I'm getting because of the first message. This is my first time working with I/O in Android so any help would be appreciated!

EDIT

After adding the permissions to the manifest I'm now only getting an error:

E/MyApp﹕ IO Exception: java.io.NotSerializableException: android.widget.ArrayAdapter

As I said, my custom object is Parcelable and the rest of this is being done in my MainActivity. Do I need to make a new class that is Serializable to build my ArrayAdapter?

Ryan Sayles
  • 3,389
  • 11
  • 56
  • 79

1 Answers1

2

I would suggest to save the records in internal storage in private mode,which can be accessed by your app only.If you store it in External storage, there is no guarantee that it will be available next time you load your app.

Also, you should save array of record objects rather than ArrayAdapter object.

Parcel and Parcelable are fantastically quick, but its documentation says you must not use it for general-purpose serialization to storage, since the implementation varies with different versions of Android (i.e. an OS update could break an app which relied on it). So use Serializable in this case instead of Parcalable (from this SO thread)

You can use global variables to pass data from one activity to another. Also you can read/ write records when app starts using global class which extends Applicaion class.

You can try following code,

public class GlobalClass extends Application {
public static Object objectToBePassed; // global variable
final static private RECORDS_FILENAME = "myRecords.txt"

@Override
    public void onCreate(){
        super.onCreate();
        readRecordsFromFile();  // read records when app starts
}

    public boolean writeRecordsToFile(ArrayList<Record> records){
                FileOutputStream fos;
                ObjectOutputStream oos=null;
                try{
                    fos = getApplicationContext().openFileOutput(RECORDS_FILENAME, Context.MODE_PRIVATE);
                    oos = new ObjectOutputStream(fos);   
                    oos.writeObject(records);
                    oos.close();
                    return true;
                }catch(Exception e){            
                    Log.e(getClassName(), "Cant save records"+e.getMessage());
                    return false;
                }
                finally{
                    if(oos!=null)
                        try{
                            oos.close();
                        }catch(Exception e){
                            Log.e(getClassName(), "Error while closing stream "+e.getMessage());
                        }
                }
            }

    private boolean readRecordsFromFile(){
            FileInputStream fin;
            ObjectInputStream ois=null;
            try{
                fin = getApplicationContext().openFileInput(RECORDS_FILENAME);
                ois = new ObjectInputStream(fin);   
                ArrayList<Record> records = (ArrayList<Record>) ois.readObject();
                ois.close();
                Log.v(getClassName(), "Records read successfully");
                return true;
                }catch(Exception e){
                    Log.e(getClassName(), "Cant read saved records"+e.getMessage());
                    return false;
                }
            finally{
                if(ois!=null)
                    try{
                        ois.close();
                    }catch(Exception e){
                        Log.e(getClassName(), "Error in closing stream while reading records"+e.getMessage());
                    }
            }
        }
}

So to pass any object from activity A to activity B, use following code in activity A ,

Intent intent = new Intent(this,B.class);
GlobalClass.objectToBePassed = obj;
startActivity(intent);

in activity B,

MyClass object = (MyClass) GlobalClass.objectToBePassed;

so to pass a Record class object, replace MyClass with Record.

Community
  • 1
  • 1
akshay7692
  • 601
  • 1
  • 8
  • 19
  • This worked a little better, I'm still getting an error when reading/writing saying that `Record` is not serializable. Is there an easy fix to get this to work with `Parcelable`? I'm using that because I need to pass data between `Activities` and `Serializable` did not work for that – Ryan Sayles Jul 23 '14 at 14:14
  • I would suggest using serializable since parcelable is not recommanded (from http://developer.android.com/reference/android/os/Parcel.html) to store data in persistent storage.So you can use global variable to pass objects from one activity to another. I am updating my answer with this solution. – akshay7692 Jul 23 '14 at 14:27