【问题标题】:How do I preserve a complex object across Activity restarts?如何在 Activity 重新启动时保留复杂对象?
【发布时间】:2010-12-10 19:59:45
【问题描述】:

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

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

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

更新

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

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

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

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

【问题讨论】:

  • 不能只使用Bundle吗?
  • 怎么样?如果您的活动通过 onSaveInstanceState(),即当它被运行时销毁时,您只能访问该包。
  • 太棒了!我用了你的方法,它有效。我唯一遇到的问题是 Android SDK 中的 Base64InputStream 没有一个具有一个参数的构造函数,它现在有一个额外的 int 标志参数。如果对象中有任何 url 信息,只需将其设置为 0 或 8。
  • 呸,Base64InputStream 仅在 Android 2.2 之后可用。 developer.android.com/reference/android/util/…
  • 是的。但是,您可以简单地使用许多免费的 Base64 实现之一。或者链接到 Apache commons-codec 并使用 ProGuard 删除未使用的类。

标签: android serialization android-activity lifecycle


【解决方案1】:

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

Android 的 API 实际上是相当合理的,主要是因为它是对 SQLite API 的精简包装,而 SQLite API 对于嵌入式数据库来说是相当合理的。此外,Android 确实通过SQLiteOpenHelper 为应用升级的架构升级提供帮助。

这比我预期的要快得多,而且 除非你的豆子携带大量 数据,它工作得很好。

我听说过很多开发人员因序列化而尖叫着逃跑,而我听说过的人在序列化方面取得了长期成功。就在过去的几天里,在 SO #android 上,我与一个拼命想从他的应用程序中彻底删除序列化的人进行了交流。

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

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

【讨论】:

  • 对不起,我一点也不同意。必须维护一个数据库来临时存储一个简单的对象对我来说似乎有点过分。另外,我知道 SQLiteOpenHelper 及其所有限制,我发现数据库迁移是一个主要的痛苦。另外,这里不需要维护,因为我可以在存储对象之前简单地 clear() 编辑器,而不管其结构如何。想想基于文档的数据库,没有模式。使用数据库,每当模型更改时,我都必须 DROP TABLE。
  • Matthias 方法适用于保存复杂对象并且不想为对象上的每个数据点定义所有列的麻烦。
【解决方案2】:

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

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

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

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

ObjectOutputStream.writeObject(yourPojo)

会为我们创造奇迹,(记得实现 Serializable 标记接口)

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

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

黑客愉快:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多