Android备份/恢复:如何备份内部数据库?



2 Answers

在重新回顾我的问题之后,我看到了ConnectBot如何实现它的功能后就可以开始工作。 感谢Kenny和Jeffrey!

这实际上与添加一样简单:

FileBackupHelper hosts = new FileBackupHelper(this,
    "../databases/" + HostDatabase.DB_NAME);
addHelper(HostDatabase.DB_NAME, hosts);

到您的BackupAgentHelper

我缺少的一点是,你必须使用“ ../databases/ ”的相对路径。

不过,这绝不是一个完美的解决方案。 FileBackupHelper的文档中提到了例如:“ FileBackupHelper只能用于小配置文件,而不能用于大型二进制文件 ”,后者就是SQLite数据库的情况。

我希望得到更多的建议,深入了解我们的期望(什么是适当的解决方案)以及关于如何破解的建议。

Question

我已经使用提供的FileBackupHelper实现了BackupAgentHelper来备份和恢复我拥有的本地数据库。 这是您通常与ContentProviders一起使用的数据库,它位于/data/data/yourpackage/databases/

人们会认为这是一个常见的情况。 但是文档不清楚该怎么做: http://developer.android.com/guide/topics/data/backup.html : http://developer.android.com/guide/topics/data/backup.html这些典型的数据库没有专门的BackupHelper 因此,我使用FileBackupHelper ,将它指向“ /databases/ ”中的.db文件,在我的ContentProviders引入了任何数据库操作(例如db.insert )的锁,甚至尝试在onRestore()之前创建“ /databases/ ”目录onRestore()因为它在安装后不存在。

我以前在其他应用程序中成功实施了类似的SharedPreferences解决方案。 但是,当我在模拟器2.2中测试新的实现时,我看到从日志执行到LocalTransport的备份以及正在执行的恢复(并onRestore() )。 然而,db文件本身从未被创建。

请注意,这是在安装之后,以及在首次启动应用程序之前,在执行还原之后。 除此之外,我的测试策略基于http://developer.android.com/guide/topics/data/backup.html#Testing

另请注意,我不是在谈论一些我自己管理的sqlite数据库,也不是关于备份到SDcard,自己的服务器或其他地方。

我在文档中看到关于数据库建议使用自定义BackupAgent但它似乎并不相关:

但是,如果需要,可能需要直接扩展BackupAgent:*备份数据库中的数据。 如果您在用户重新安装应用程序时想要恢复SQLite数据库,则需要构建自定义的BackupAgent,以在备份操作期间读取适当的数据,然后创建表并在还原操作期间插入数据。

请澄清一下。

如果我真的需要自己做到SQL级别,那么我担心下列主题:

  • 打开数据库和事务。 我不知道如何从我的应用程序工作流程之外的单一课程中关闭它们。

  • 如何通知用户备份正在进行并且数据库已被锁定。 这可能需要很长时间,所以我可能需要显示进度条。

  • 如何在还原时执行相同的操作。 据我所知,恢复可能发生在用户已经开始使用应用程序(并将数据输入到数据库)时。 因此,您不能假定仅恢复备份的数据(删除空白或旧数据)。 你必须以某种方式加入它,对于任何非平凡的数据库来说,由于id的缘故,这是不可能的。

  • 如何在还原完成后刷新应用程序,而不会让用户卡在某些 - 现在无法访问的点。

  • 我可以确定数据库在备份或恢复时已经升级了吗? 否则,预期的模式可能不匹配。




使用FileBackupHelper备份/恢复sqlite数据库提出了一些严重的问题:
1.如果应用程序使用从ContentProvider.query()检索的游标并且备份代理尝试覆盖整个文件,会发生什么情况?
这个http://developer.android.com/guide/topics/data/backup.html#Testing是完美(低熵)测试的一个很好的例子。 您卸载应用程序,再次安装并且备份被恢复。 然而,生活可能是残酷的。 看看link 。 让我们来想象一下,用户购买新设备的场景。 由于它没有自己的集合,因此备份代理使用其他设备的集合。 该应用程序已安装,并且您的backupHelper将检索旧版本文件,其版本低于当前版本。 SQLiteOpenHelper使用默认实现调用onDowngrade

public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    throw new SQLiteException("Can't downgrade database from version " +
            oldVersion + " to " + newVersion);
}

无论用户做什么,他/她都无法在新设备上使用您的应用。

我建议使用ContentResolver获取数据 - >序列化(不带_id )备份和反序列化 - >插入数据进行还原。

注意:获取/插入数据是通过ContentResolver完成的,从而避免了cuncurrency问题。 序列化在您的backupAgent中完成。 如果你做自己的游标< - >对象映射序列化一个项目可以很简单,就像在表示实体的类上使用transient字段_id实现Serializable一样简单。

我还会使用批量插入,即ContentProviderOperation exampleCursorLoader.setUpdateThrottle以便在备份还原过程中应用程序不会因重新启动加载程序而导致数据更改。

如果您遇到降级情况,您可以选择中止还原数据,或者使用与降级版本相关的字段还原和更新ContentResolver。

我同意这个主题不容易,在文档中没有很好的解释,有些问题仍然像批量数据大小一样。

希望这可以帮助。




一种选择是将其构建在数据库上方的应用程序逻辑中。 它实际上为我想的这样的levell尖叫。 不知道你是否已经这样做了,但大多数人(尽管android内容管理器光标方法)会引入一些ORM映射 - 无论是自定义还是一些orm-lite方法。 而我在这种情况下宁愿做的是:

  1. 以确保您的应用程序在应用程序/数据在后台添加时正常工作,并在应用程序启动时添加/删除新数据
  2. 制作一些Java-> protobuf甚至是简单的java序列化映射,并编写自己的BackupHelper以从流中读取数据并将其简单地添加到数据库中....

所以在这种情况下,而不是在数据库级别执行它在应用程序级别。






Related