Java Serializable与Android Parcelable

时间:2018-08-03 15:26:46

标签: android parcelable serializable

我目前正在开发Android应用程序(我是开发Android应用程序的初学者),并且正在实现一个非常简单的类,该类能够处理位置(纬度,经度)和相应的地址(通过使用Geocoder API)。 我希望我的应用程序能够在两个活动之间传递我的类的一个实例,并且还可以返回结果,这样的结果也是该类的一个实例。

我已经写了我的类的声明来实现 Parcelable 类,就像这样(我的类名称是 OnePlace ):

public class OnePlace implements Parcelable {

    private LatLng position;
    private String address;
    …
}

我已经使用能够实现 Parcelable 的方法实现了“ OnePlace”类,如Android开发者在线文档中所建议的那样: https://developer.android.com/reference/android/os/Parcelable

一切正常,我能够将班级的一个实例从一个活动转移到另一个活动,并因此将另一个实例取回。

这是片段代码,在其中我将类的实例添加为与意图一起发送的数据:

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);

在所调用的活动(此处为MapsActivity)中,我能够重建类实例的精确副本。

无论如何,这行得通,我对此感到满意。

现在,我想将类的实例存储在一个文件中,该文件将在下次启动我的应用程序时读取(一种永久存储的方式)。

为此,我在活动的 finish()方法内使用以下代码段(就在离开应用程序之前):

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

objectStream.writeObject(item);

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

如您所见,我将 ObjectOutputStream 类与 FileOutputStream 类一起使用,以便能够写入文件。 item 对象是我的 OnePlace 类的一个实例。我想将该实例永久存储到文件中,以备下次使用。

当我调用 writeObject(...)方法时,调用以“对象不可序列化”异常结束。在阅读了一些在线文档之后,明确指出我要“序列化”的类应实现 Serializable 接口(这是强制性的!)。

因此,我像这样更新了类的定义:

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

我稍微修改了类的定义,以实现 Parcelable Serializable 接口。我不知道这是否是满足我的需求的最佳方法……

当我要调用 intent.putExtra(“ key”,item); 时,出现错误。实际上,Java编译器抱怨这样一个事实,即它不知道应实现哪个 putExtra 接口:具有Parcelable的接口还是具有Seri​​alizable接口的接口?它的反应似乎是合乎逻辑的,我认为如果我是一个人工编译器,我可能会问同样的问题。

那么,现在,我该如何解决该问题?有人有主意吗?也许,我要实现两个目标(将类实例转移到另一个活动并永久存储一个实例)的方法不是最好的方法吗?你有什么建议吗?

我非常感谢您的回答。

查尔斯

2 个答案:

答案 0 :(得分:0)

最后,我设法找到了一个方便的优雅解决方案。如果其他寻求序列化对象的人可能对它感兴趣,我只是想发布我的操作方法。

在我的 OnePlace 类的声明中,我最终仅实现了 Parcelable 接口,以便能够在两个活动之间传递该类的实例(我没有再实现 Serialize 接口)。

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

然后,我在类中添加了以下新方法:

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 "";
}

该方法会创建一个包含位置信息(地址,纬度和经度)的JSON对象,然后返回等效的字符串(将用于进一步导出位置信息)。

然后,在我的 MainActivity.finish()方法中,构建一个String的数组列表,每个字符串包含与OnePlace类实例相关联的JSON对象的String表示形式。 完成此操作后,我便可以序列化我的 ArrayList ,然后将其序列化(Java编译器不会抱怨!)。这给了我我在应用偏好设置中写的长字符串。

代码如下:

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();
  }
}

现在,在我的 MainActivity.onCreate()方法中,我从应用程序首选项中读取字符串,并将其转换回OneClass实例,如下所示:

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();
  };

所以,我在这里总结一下我是如何进行操作的:

对于保存过程,我执行以下操作:

  1. 将OnePlace类的每个实例导出为JSONObject字符串表示形式
  2. 将每个字符串添加到字符串数组列表中
  3. 完成后,数组列表将被序列化
  4. 阵列列表的序列化版本保存到应用程序首选项中

对于还原过程,我执行以下操作:

  1. 从应用偏好中读取字符串
  2. 将该字符串反序列化为字符串数组列表
  3. 该数组列表中的每个字符串都用于创建JSONObject
  4. 从JSONObject中提取位置信息
  5. 位置信息用于创建OnePlace类的实例
  6. 每个新实例都添加到地点列表中

效果很好!

答案 1 :(得分:0)

我建议您在“活动”,“片段”之间传递数据时使用Gson序列化。这是非常容易和轻量级的库。您的应用示例;

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);

在MapsActivity.class中

Gson gson = new  Gson();
String jsonPlace = getIntent().getStringExtra("place");
Place passedItem = gson.fromJson(jsonPlace, Place.class);