修复方法简介

Android 接口支持三种修复方法,如下:

修复方法简介相关接口
Repair Kit解析 B-tree 修复RepairKit
备份恢复压缩备份完整数据,使用备份数据恢复BackupKitRecoverKit
Dump.dump 命令,已废弃DBDumpUtil

关于不同修复方法的详细介绍,请参照 《公众号文章》

Repair Kit

使用 Repair Kit 可以直接从损坏的数据库里尽量读出未损坏的数据,不需要事先准备,但是先备份 Master 信息可以大大增加恢复成功率。 如果有意使用 Repair Kit 恢复数据库,建议备份 Master 信息。

Repair Kit 使用范例,请参照 sample-repairdb

备份 Master 信息

Master 信息保存了数据库的 Schema,建议每次执行完数据库创建或升级时执行备份,可以保证备份是最新的。不修改 Schema 的话 Master 信息不会改变。如果你使用 SQLiteOpenHelper,最佳实践是在 SQLiteOpenHelper.onCreate(…)SQLiteOpenHelper.onUpgrade(…) 的最后进行备份。

备份 Master 信息只需要调用 RepairKit.MasterInfo.save(…) 即可。备份 Master 信息典型消耗为几kB ~ 几十kB,几毫秒 ~ 几十毫秒,但如果你有非常非常多的表和索引(万数量级),这个过程可能会有点慢,建议放在子线程完成。

  1. public class DBHelper extends SQLiteOpenHelper {
  2.  
  3. public DBHelper(Context context) {
  4. super(context, DATABASE_NAME, PASSPHRASE, CIPHER_SPEC, null,
  5. DATABASE_VERSION, ERROR_HANDLER);
  6. }
  7.  
  8. @Override
  9. public void onCreate(SQLiteDatabase db) {
  10. // 执行 CREATE TABLE 创建 Schema
  11. db.execSQL("CREATE TABLE t1(a,b);");
  12. db.execSQL("CREATE TABLE t2(c,d);");
  13. // ......
  14.  
  15. // 备份 Master 信息
  16. RepairKit.MasterInfo.save(db, db.getPath() + "-mbak", BACKUP_PASSPHRASE);
  17. }
  18.  
  19. @Override
  20. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  21. // 执行升级
  22. db.execSQL("ALTER TABLE t1 ADD COLUMN x TEXT;");
  23.  
  24. // 备份 Master 信息
  25. RepairKit.MasterInfo.save(db, db.getPath() + "-mbak", BACKUP_PASSPHRASE);
  26. }
  27. }

恢复损坏数据库

恢复损坏数据库,首先加载之前备份的 Master 信息(如果有)。

  1. RepairKit.MasterInfo master = RepairKit.MasterInfo.load('/path/to/database.db-mbak',
  2. BACKUP_PASSPHRASE, null);
  3. if (master == null) {
  4. // 加载不成功,可能是不存在或者损坏
  5. }

使用 RepairKit 打开损坏的数据库,使用 SQLiteDatabase 打开新的数据库,调用 output(…)即可将损坏数据库的内容转移到新数据库。

  1. RepairKit repair = new RepairKit(
  2. "/path/to/corrupted.db" // 损坏的数据库文件
  3. PASSPHRASE, // 数据库密钥(不是备份文件密钥)
  4. CIPHER_SPEC, // 加密描述,与打开DB时一样
  5. master // 之前加载的 Master 信息
  6. );
  7.  
  8. SQLiteDatabase newDb = SQLiteDatabase.openOrCreateDatabase(...);
  9. // 打开新DB用于承载恢复数据,是否加密没所谓
  10.  
  11. boolean result = repair.output(newDb, 0);
  12. // 输出恢复数据到新DB
  13.  
  14. if (!result) {
  15. // 恢复失败
  16. }
  17.  
  18. repair.release();
  19. // 最后要 release 释放资源

恢复的过程需时较长,请务必在子线程完成,如数据库较大请考虑持有 Wake Lock。

选择性恢复

Repair Kit 可以只恢复一部分表,只需要在 MasterInfo.load(…) 或者 MasterInfo.make(…)里指定白名单即可。

  1. // 白名单,只有白名单里列到的表才会恢复,表对应的索引也会相应恢复
  2. String[] tables = new String[] {
  3. "t1", "t2" // 只恢复 t1 和 t2 两个表
  4. };
  5. RepairKit.MasterInfo master = RepairKit.MasterInfo.load('/path/to/database.db-mbak',
  6. BACKUP_PASSPHRASE, tables);

备份和恢复

备份完整数据,损坏后使用备份恢复的方案,如没有备份则无法恢复。由于是备份数据本身而不是 Schema,备份本身需要经常更新。备份和恢复操作都非常耗时,请勿在主线程操作,备份大数据库和恢复时可以考虑持有 Wake Lock。

备份

  1. BackupKit backup = new BackupKit(
  2. db, // 要备份的 DB
  3. db.getPath() + "-backup", // 备份文件
  4. BACKUP_PASSPHRASE, // 加密备份文件的密钥,非 DB 密钥
  5. 0, null);
  6.  
  7. int result = backup.run();
  8. switch (result) {
  9. case BackupKit.RESULT_OK: /* 成功 */ break;
  10. case BackupKit.RESULT_CANCELED: /* 取消操作 */ break;
  11. case BackupKit.RESULT_FAILED: /* 失败 */ break;
  12. }
  13.  
  14. backup.release();

恢复

  1. RecoverKit recover = new RecoverKit(
  2. db, // 要恢复到的目标 DB
  3. db.getPath() + "-backup", // 备份文件
  4. BACKUP_PASSPHRASE // 加密备份文件的密钥,非 DB 密钥
  5. );
  6.  
  7. int result = recover.run(false); // fatal 参数传 false 表示遇到错误忽略并继续,
  8. // 若传 true 遇到错误则中止并返回 FAILED
  9. switch (result) {
  10. case RecoverKit.RESULT_OK: /* 成功 */ break;
  11. case RecoverKit.RESULT_CANCELED: /* 取消操作 */ break;
  12. case RecoverKit.RESULT_FAILED: /* 失败 */ break;
  13. }
  14.  
  15. recover.release();

取消操作

由于备份和恢复都比较耗时,WCDB 提供接口中止备份或恢复操作。只需要在另外的线程调用BackupKit.cancel()RecoverKit.cancel() 即可通知备份或恢复线程中止并尽快返回,返回码为 RESULT_CANCELED