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

Android backup/restore: how to backup an internal database?

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

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

我过去在不同的应用程序中成功地为SharedPreferences实现了类似的解决方案。但是,当我在模拟器2.2中测试我的新实现时,我看到从日志执行了LocalTransport的备份,以及正在执行的恢复(以及onRestore()调用)。然而,db文件本身永远不会被创建。

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

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

我确实在文档中提到了有关建议使用自定义BackupAgent的数据库,但它似乎并不相关:

However, you might want to extend
BackupAgent directly if you need to:
* Back up data in a database. If you have an SQLite database that you
want to restore when the user
re-installs your application, you need
to build a custom BackupAgent that
reads the appropriate data during a
backup operation, then create your
table and insert the data during a
restore operation.

请清楚一点。

如果我真的需要自己完成SQL级别,那么我担心以下主题:

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

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

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

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

  • 我可以确定数据库是否已在备份或还原时升级?否则,预期的架构可能不匹配。


在重新审视我的问题之后,在查看ConnectBot的工作方式后,我能够让它工作。谢谢肯尼和杰弗里!

它实际上就像添加一样简单:

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

到你的BackupAgentHelper

我遗漏的一点是你必须使用"../databases/"的相对路径。

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

我想得到更多的建议,对我们的期望有什么见解(什么是正确的解决方案),以及关于如何破坏的建议。


这是将数据库备份为文件的更简洁方法。没有硬编码的路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyBackupAgent extends BackupAgentHelper{
   private static final String DB_NAME ="my_db";

   @Override
   public void onCreate(){
      FileBackupHelper dbs = new FileBackupHelper(this, DB_NAME);
      addHelper("dbs", dbs);
   }

   @Override
   public File getFilesDir(){
      File path = getDatabasePath(DB_NAME);
      return path.getParentFile();
   }
}

注意:它会覆盖getFilesDir,以便FileBackupHelper在数据库目录中工作,而不是文件目录。

另一个提示:您还可以使用databaseList将此列表中的所有数据库和提要名称(无父路径)提取到FileBackupHelper中。然后所有应用程序的数据库都将保存在备份中。


更简洁的方法是创建自定义BackupHelper

1
2
3
4
5
6
public class DbBackupHelper extends FileBackupHelper {

    public DbBackupHelper(Context ctx, String dbName) {
        super(ctx, ctx.getDatabasePath(dbName).getAbsolutePath());
    }
}

然后将其添加到BackupAgentHelper

1
2
3
public void onCreate() {
    addHelper(DATABASE, new DbBackupHelper(this, DB.FILE));
}


使用FileBackupHelper备份/恢复sqlite db引发了一些严重的问题:

1.如果应用程序使用从ContentProvider.query()检索到的游标并且备份代理尝试覆盖整个文件,会发生什么?
这个链接是完美(低熵)测试的一个很好的例子。您卸载应用程序,再次安装它并恢复备份。然而,生活可能是残酷的。看看链接。让我们想象一下用户购买新设备的情况。由于它没有自己的设置,因此备份代理使用其他设备的设置。安装了该应用程序,您的backupHelper将检索db版本模式低于当前版本的旧文件。 SQLiteOpenHelper使用默认实现调用onDowngrade

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

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

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

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

我还使用批量插入,即ContentProviderOperation示例和CursorLoader.setUpdateThrottle,以便应用程序在备份恢复过程中不会因数据更改而重新启动加载程序。

如果您确实处于降级状态,您可以选择中止还原数据,或者使用与降级版本相关的字段还原和更新ContentResolver。

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

希望这可以帮助。


从Android M开始,现在可以为应用程序提供全数据备份/恢复API。 这个新API在应用程序清单中包含一个基于XML的规范,允许开发人员以直接语义方式描述要备份的文件:'备份名为"mydata.db"的数据库。 这个新的API对于开发人员来说更容易使用 - 您不必跟踪差异或明确请求备份传递,并且要备份哪些文件的XML描述意味着您通常不需要编写任何代码 一点都不

(例如,您甚至可以参与完整数据备份/恢复操作以在恢复发生时获得回调。这样就很灵活。)

有关如何使用新API的说明,请参阅developer.android.com上的"为应用程序配置自动备份"部分。


一种选择是在数据库上方的应用程序逻辑中构建它。我觉得它实际上是为了这样的事情而尖叫。
不确定你是否已经这样做但是大多数人(尽管安卓内容管理器游标方法)将引入一些ORM映射 - 自定义或一些orm-lite方法。在这种情况下我宁愿做的是:

  • 确保您的应用程序正常运行
    添加app / data时很好
    新数据的背景
    在应用程序中添加/删除
    已经开始了
  • 做一些
    Java-> protobuf甚至只是java
    序列化映射并编写您的
    拥有BackupHelper来读取数据
    从流中,只需将其添加到
    数据库....
  • 所以在这种情况下,而不是在数据库级别上执行它在应用程序级别上执行它。