• 修复方法简介
  • Repair Kit
    • 备份 Master 信息
    • 恢复损坏数据库
    • 选择性恢复
  • 备份和恢复
    • 备份
    • 恢复
    • 取消操作

    修复方法简介

    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