1

I am currently working on an Android app (I am a beginner in developing Android apps) and I am implementing a very simple class which is able to handle a location (latitude, longitude) and the corresponding address (by the use of the Geocoder API). I want my app to be able to transfer one instance of my class between two activities and also to get the result back, such result being also an instance of that class.

I have written the declaration of my class to implement the Parcelable class, like this (my class name is OnePlace):

public class OnePlace implements Parcelable {

    private LatLng position;
    private String address;
    …
}

I have implemented the "OnePlace" class with the methods to be able to implement a Parcelable as it is recommended on the Android developper online documentation: https://developer.android.com/reference/android/os/Parcelable

Everything works fine and I am able to transfer one instance of my class from one activity to another and to get another instance back as a result.

Here is the snippet code in which I add the instance of my class as a data sent along with the intent:

OnePlace item = new OnePlace(); // create an instance of the class with default values
Intent mapIntent = new Intent(adapterView.getContext(), MapsActivity.class);
mapIntent.putExtra("place", item); // add the item as extra data to the intent
startActivityForResult(mapIntent, 35);

In the called activity (here MapsActivity), I am able to rebuild an exact copy of the class instance.

Anyway, this works and I am happy with that.

Now, I would like to store the instance of my class in a file that will be read when I start my app the next time (a way to have permanent storage).

For that, I use the following code snippet inside the finish() method of my activity (just before leaving my app):

FileOutputStream outStream = openFileOutput("myFile.tmp", Context.MODE_PRIVATE);
ObjectOutputStream objectStream = new ObjectOutputStream(outStream);

objectStream.writeObject(item);

objectStream.close();
outStream.close();

As you can see, I use an ObjectOutputStream class with a FileOutputStream class to be able to write into the file. The item object is one instance of my OnePlace class. It is that instance that I want to permanently store into a file to be able to get it back next time.

When I call the writeObject(…) method, the call ends with an exception "Object not serializable". After reading some online documentation, it is clearly stated that the class I want to "serialize" shall implement the Serializable interface (this is mandatory!).

Consequently, I updated the definition of my class like this:

public class OnePlace implements Parcelable, Serializable {
    private LatLng position;
    private String address;
    …

I slightly modify the definition of my class to implement both Parcelable and Serializable interfaces. I do not know if it the best way to achieve my needs…

When I want to call the intent.putExtra("key", item);, I get an error. In fact, the Java compiler complains about the fact that it does not know which putExtra interface it shall implement: the one with the Parcelable or the one with the Serializable interface ? Its reaction seems to be logical and I think that if I have been a human compiler, I probably would have asked the same question.

So, now, how can I do to solve that issue ? Anybody has an idea ? Maybe, the way I want to achieve my two goals (transfer class instance to another activity and permanently store an instance) is not the best way ? Do you have some suggestions ?

I thank you a lot in advance for your answers.

Charles

Charles
  • 21
  • 3
  • I'd probably serialize either to text via GSON, or to a database (Room, Realm, ObjectBox, whatever) – EpicPandaForce Aug 03 '18 at 15:29
  • Possible duplicate of [Android: Difference between Parcelable and Serializable?](https://stackoverflow.com/questions/3323074/android-difference-between-parcelable-and-serializable) – Saurabh Bhandari Aug 03 '18 at 15:32
  • I have read that there are some possibilities to "export" a class instance into a JSON object. I think I will give it a try… – Charles Aug 03 '18 at 15:33

2 Answers2

0

Finally, I managed to find an elegant solution that I find convenient. I just wanted to post how I did it if it can be of any interest for other people looking for serializing objects.

In the declaration of my OnePlace class, I finally only implements the Parcelable interface in order to be able to transfer instances of that class between two activities (I do not implement the Serialize interface any more).

public class OnePlace implements Parcelable {
    private LatLng position;
    private String address;
    …
};

Then, I added the following new method into my class:

public String toJSON() {
    JSONObject jsonObj = new JSONObject();
    try {

        jsonObj.put("address", address);
        jsonObj.put("latitude", position.latitude);
        jsonObj.put("longitude", position.longitude);
        return jsonObj.toString();

    } catch (Exception e) {
      e.printStackTrace();
    }
    return "";
}

That method creates a JSON object containing position information (address, latitude and longitude) and then returns the equivalent string (this will be used for further export of the position information).

Then, in my MainActivity.finish() method, I build an array list of String, each string containing the String representation of the JSON object associated with a OnePlace class instance. When this is done, I am then able to serialize my ArrayList which is then serializable (the Java compiler does not complain!). This gives me a long string that I write in my app preferences.

Here is the code:

public void finish() {
  ArrayList<String> listInstances = new ArrayList<String>();
  for(OnePlace item: listOfPlaces) {
    listInstances.add(item.toJSON());
  }

  try {

    ObjectSerializer objSerializer = new ObjectSerializer();
    String serializedStr = objSerializer.serialize(listInstances);
    myPrefs.edit().putString("places", serializedStr).apply();

  } catch (Exception e) {
    e.printStackTrace();
  }
}

Now, in my MainActivity.onCreate() method, I read back the string from the app preference and I convert it back to OneClass instances, like this:

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  // This is an array list that will contain all OnePlace instances
  listOfPlaces = new ArrayList<OnePlace>();

  myPreferences = this.getSharedPreferences("…", MODE_PRIVATE);

  // I read back the String containing the serialized ArrayList
  String placesStr = myPreferences.getString("places");

  // An array list that will contain the list of JSONObjects, one object representing one instance on the OnePlace class
  ArrayList<String> jsonObjList;

  // I create a new ObjectSerializer object
  ObjectSerializer objSerializer = new ObjectSerializer();

  // Then, I deserialize into a list of JSONObjects
  try {

    // The myList object contains a list of JSONObjects      
    jsonObjList = (ArrayList<String>)objSerializer.deserialize(placesStr);

    // I loop through the list of JSONObjects
    for(String str: jsonObjList) {
      // I create a JSONObject with one single string
      JSONObject myJson = new JSONObject(str);

      // Then, I get the values back and I use them to create a OnePlace class instance
      LatLng position = new LatLng(myJson.getDouble("latitude"), myJson.getDouble("longitude"));
      OnePlace myPlace = new OnePlace(position, myJson.getString("address");

      // And finally, I add that OnePlace instance into my list of places
      listOfPlaces.add(myPlace);
    }

  } catch (Exception e) {
    e.printStackTrace();
  };

So, I summarize here how I did the operations:

For the saving process, I do the following:

  1. export each instance of my OnePlace class into a JSONObject string representation
  2. add each string into an array list of Strings
  3. when done, the array list is serialized
  4. the serialized version of the array list is saved into the app preferences

For the restoring process, I do the following:

  1. read the string from the app preferences
  2. deserialize that string into an array list of Strings
  3. each string from that array list is used to create a JSONObject
  4. from the JSONObject, the location informations are extracted
  5. the location informations are used to create an instance of the OnePlace class
  6. each new instance is added into the list of places

And it works fine!

Charles
  • 21
  • 3
0

I recommend you to use Gson serialization when passing data between Activity, Fragments. It is very easy and lightweight library. Example for your app;

Gson gson = new  Gson();
OnePlace item = new OnePlace(); // create an instance of the class with default values
Intent mapIntent = new Intent(adapterView.getContext(), MapsActivity.class);
mapIntent.putExtra("place", gson.toJson(item)); // add the item as extra data to the intent
startActivityForResult(mapIntent, 35);

In MapsActivity.class

Gson gson = new  Gson();
String jsonPlace = getIntent().getStringExtra("place");
Place passedItem = gson.fromJson(jsonPlace, Place.class);
misman
  • 1,172
  • 2
  • 20
  • 35
  • Sounds interesting. But is the OnePlace class committed to be serializable to be allowed to call gson.toJson(item) ? I mean do I have to implement any serializable interface in my class like Serializable or Parcelable, or can I fully remove them from my class declaration ? – Charles Aug 12 '18 at 07:08
  • you can fully remove them from your class declaration – misman Aug 12 '18 at 10:56