Android 如何在 Activity 重新启动时保留复杂对象?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1636623/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 03:23:24  来源:igfitidea点击:

How do I preserve a complex object across Activity restarts?

androidserializationandroid-activitylifecycle

提问by Matthias

Say I have a Java Bean object which is serializable. I want to store it away safely when an Activity goes through onDestroy() on purpose(i.e. onSaveInstanceState() is notcalled).

假设我有一个可序列化的 Java Bean 对象。当 Activity故意通过 onDestroy()(即调用onSaveInstanceState() )我想将它安全地存储起来。

I am looking for a way which doesn't involve creating a database and write the object to that (mostly since a) Android's DB API is horrible and b) since databases make application updates a nightmare, because there is no decent support for applying migrations).

我正在寻找一种不涉及创建数据库并将对象写入该数据库的方法(主要是因为 a)Android 的 DB API 很糟糕 b)因为数据库使应用程序更新成为一场噩梦,因为没有对应用迁移的体面支持)。

I thought about serializing the object to a ByteArrayOutputStream, base64 encode that and write it to a SharedPreferences file as a string. Or is that too far off?

我考虑将对象序列化为 ByteArrayOutputStream,base64 对其进行编码并将其作为字符串写入 SharedPreferences 文件。还是离得太远了?

UPDATE

更新

Maybe that serialize-to-string idea wasn't that bad after all, seems to work out pretty well. Here's what I'm doing now:

也许序列化到字符串的想法毕竟不是那么糟糕,似乎效果很好。这是我现在正在做的事情:

    public static String objectToString(Serializable object) {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    try {
        new ObjectOutputStream(out).writeObject(object);
        byte[] data = out.toByteArray();
        out.close();

        out = new ByteArrayOutputStream();
        Base64OutputStream b64 = new Base64OutputStream(out);
        b64.write(data);
        b64.close();
        out.close();

        return new String(out.toByteArray());
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

public static Object stringToObject(String encodedObject) {
    try {
        return new ObjectInputStream(new Base64InputStream(
                new ByteArrayInputStream(encodedObject.getBytes()))).readObject();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

in onDestroy() I can then simply write the Base64 string to a preference file, where it's safe until I read it again during the next activity launch. It's a lot faster than I expected and unless your beans carry huge amounts of data, it works pretty well. And even better, you don't have to maintain a DB schema.

在 onDestroy() 中,我可以简单地将 Base64 字符串写入首选项文件,在下一次活动启动期间再次读取它之前,它是安全的。它比我预期的要快得多,除非您的 bean 携带大量数据,否则它运行良好。更好的是,您不必维护数据库架构。

Still, I'm curious about how others do this.

不过,我很好奇其他人是如何做到这一点的。

回答by CommonsWare

I am looking for a way which doesn't involve creating a database and write the object to that (mostly since a) Android's DB API is horrible and b) since databases make application updates a nightmare, because there is no decent support for applying migrations).

我正在寻找一种不涉及创建数据库并将对象写入该数据库的方法(主要是因为 a)Android 的 DB API 很糟糕 b)因为数据库使应用程序更新成为一场噩梦,因为没有对应用迁移的体面支持)。

Android's API is actually fairly reasonable, mostly because it's a thin wrapper over the SQLite API, and the SQLite API is fairly reasonable for an embedded database. Moreover, Android does provide assistance for schema upgrades on app upgrades, via SQLiteOpenHelper.

Android 的 API 实际上相当合理,主要是因为它是 SQLite API 的瘦包装器,而 SQLite API 对于嵌入式数据库来说相当合理。此外,Android 确实通过SQLiteOpenHelper.

It's a lot faster than I expected and unless your beans carry huge amounts of data, it works pretty well.

它比我预期的要快得多,除非您的 bean 携带大量数据,否则它运行良好。

I have heard of many more developers running away screaming from serialization than I have heard of people having long term success with it. Just within the past few days, here on SO #android, I had an exchange with somebody trying desperately to rip serialization out of his app by the roots.

我听说过更多的开发人员因为序列化而尖叫着逃跑,而不是我听说过使用序列化取得长期成功的人。就在过去的几天里,在 SO #android 上,我与某人进行了交流,他们拼命试图从他的应用程序中根除序列化。

And even better, you don't have to maintain a DB schema.

更好的是,您不必维护数据库架构。

Oh yes you do. What do you think is going to happen when you update your application and your class is modified? Doing the bookkeepingto figure out how to deserialize old versions of the class from a new version of a class is a chore and is one of the reasons developers abandon serialization. Also, do not forget that serialization is not transactional, whereas SQLite is.

哦,是的,你知道。当您更新您的应用程序并修改您的类时,您认为会发生什么?进行簿记以弄清楚如何从类的新版本反序列化旧版本的类是一件苦差事,也是开发人员放弃序列化的原因之一。另外,不要忘记序列化不是事务性的,而 SQLite 是。

回答by comeGetSome

I was also looking for a nice approach of un/marshalling any beans or activity states. We all know how much Activity's onStoreInstanceState() and onRestoreInstanceState() is a pain.

我也在寻找一种很好的方法来取消/编组任何 bean 或活动状态。我们都知道 Activity 的 onStoreInstanceState() 和 onRestoreInstanceState() 是多么痛苦。

My acitivities simply store their states in onPause() and restore them in onCreate() lifecycle hooks via direct object serialization.

我的活动只是将它们的状态存储在 onPause() 中,并通过直接对象序列化在 onCreate() 生命周期挂钩中恢复它们。

Serialization via a String like you do, is of course possible but less suitable for big data and causes a lot of overhead. Moreover Preferences are actually there to store preferences, not data :) Unfortunately, the Parcelable / Parcel what we could use for this purpose, does not recommend to store to persistent storage.

像您一样通过 String 进行序列化当然是可能的,但不太适合大数据并会导致大量开销。此外,Preferences 实际上是用来存储首选项的,而不是数据 :) 不幸的是,我们可以为此目的使用的 Parcelable / Parcel 不建议存储到持久存储中。

So what's left over is a simple object serialization - fortunately android SDK has implementation of the ObjectInputStream and ObjectOutputStream classes with all drawbacks and benefits - like we would also do in a non-android Java world, a simple:

所以剩下的是一个简单的对象序列化 - 幸运的是,android SDK 实现了 ObjectInputStream 和 ObjectOutputStream 类,并具有所有缺点和优点 - 就像我们在非 android Java 世界中所做的一样,一个简单的:

ObjectOutputStream.writeObject(yourPojo)

would do the magic for us, (remember to implement the Serializable marker-interface)

会为我们做魔术,(记得实现可序列化的标记接口)

Also, you may want to look in following APIs of a Context - ContextWrapper - Activity, which are very useful to cache local data (such as images) etc:

此外,您可能需要查看上下文的以下 API - ContextWrapper - Activity,它们对于缓存本地数据(例如图像)等非常有用:

.getCacheDir()
.getDir()
.openFileInput()
.openFileOutput()

happy hacking :)

快乐黑客:)