修复方法简介
Android 接口支持三种修复方法,如下:
修复方法 | 简介 | 相关接口 |
---|---|---|
Repair Kit | 解析 B-tree 修复 | RepairKit |
备份恢复 | 压缩备份完整数据,使用备份数据恢复 | BackupKit RecoverKit |
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,几毫秒 ~ 几十毫秒,但如果你有非常非常多的表和索引(万数量级),这个过程可能会有点慢,建议放在子线程完成。
- public class DBHelper extends SQLiteOpenHelper {
- public DBHelper(Context context) {
- super(context, DATABASE_NAME, PASSPHRASE, CIPHER_SPEC, null,
- DATABASE_VERSION, ERROR_HANDLER);
- }
- @Override
- public void onCreate(SQLiteDatabase db) {
- // 执行 CREATE TABLE 创建 Schema
- db.execSQL("CREATE TABLE t1(a,b);");
- db.execSQL("CREATE TABLE t2(c,d);");
- // ......
- // 备份 Master 信息
- RepairKit.MasterInfo.save(db, db.getPath() + "-mbak", BACKUP_PASSPHRASE);
- }
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- // 执行升级
- db.execSQL("ALTER TABLE t1 ADD COLUMN x TEXT;");
- // 备份 Master 信息
- RepairKit.MasterInfo.save(db, db.getPath() + "-mbak", BACKUP_PASSPHRASE);
- }
- }
恢复损坏数据库
恢复损坏数据库,首先加载之前备份的 Master 信息(如果有)。
- RepairKit.MasterInfo master = RepairKit.MasterInfo.load('/path/to/database.db-mbak',
- BACKUP_PASSPHRASE, null);
- if (master == null) {
- // 加载不成功,可能是不存在或者损坏
- }
使用 RepairKit
打开损坏的数据库,使用 SQLiteDatabase
打开新的数据库,调用 output(…)
即可将损坏数据库的内容转移到新数据库。
- RepairKit repair = new RepairKit(
- "/path/to/corrupted.db" // 损坏的数据库文件
- PASSPHRASE, // 数据库密钥(不是备份文件密钥)
- CIPHER_SPEC, // 加密描述,与打开DB时一样
- master // 之前加载的 Master 信息
- );
- SQLiteDatabase newDb = SQLiteDatabase.openOrCreateDatabase(...);
- // 打开新DB用于承载恢复数据,是否加密没所谓
- boolean result = repair.output(newDb, 0);
- // 输出恢复数据到新DB
- if (!result) {
- // 恢复失败
- }
- repair.release();
- // 最后要 release 释放资源
恢复的过程需时较长,请务必在子线程完成,如数据库较大请考虑持有 Wake Lock。
选择性恢复
Repair Kit 可以只恢复一部分表,只需要在 MasterInfo.load(…)
或者 MasterInfo.make(…)
里指定白名单即可。
- // 白名单,只有白名单里列到的表才会恢复,表对应的索引也会相应恢复
- String[] tables = new String[] {
- "t1", "t2" // 只恢复 t1 和 t2 两个表
- };
- RepairKit.MasterInfo master = RepairKit.MasterInfo.load('/path/to/database.db-mbak',
- BACKUP_PASSPHRASE, tables);
备份和恢复
备份完整数据,损坏后使用备份恢复的方案,如没有备份则无法恢复。由于是备份数据本身而不是 Schema,备份本身需要经常更新。备份和恢复操作都非常耗时,请勿在主线程操作,备份大数据库和恢复时可以考虑持有 Wake Lock。
备份
- BackupKit backup = new BackupKit(
- db, // 要备份的 DB
- db.getPath() + "-backup", // 备份文件
- BACKUP_PASSPHRASE, // 加密备份文件的密钥,非 DB 密钥
- 0, null);
- int result = backup.run();
- switch (result) {
- case BackupKit.RESULT_OK: /* 成功 */ break;
- case BackupKit.RESULT_CANCELED: /* 取消操作 */ break;
- case BackupKit.RESULT_FAILED: /* 失败 */ break;
- }
- backup.release();
恢复
- RecoverKit recover = new RecoverKit(
- db, // 要恢复到的目标 DB
- db.getPath() + "-backup", // 备份文件
- BACKUP_PASSPHRASE // 加密备份文件的密钥,非 DB 密钥
- );
- int result = recover.run(false); // fatal 参数传 false 表示遇到错误忽略并继续,
- // 若传 true 遇到错误则中止并返回 FAILED
- switch (result) {
- case RecoverKit.RESULT_OK: /* 成功 */ break;
- case RecoverKit.RESULT_CANCELED: /* 取消操作 */ break;
- case RecoverKit.RESULT_FAILED: /* 失败 */ break;
- }
- recover.release();
取消操作
由于备份和恢复都比较耗时,WCDB 提供接口中止备份或恢复操作。只需要在另外的线程调用BackupKit.cancel()
或 RecoverKit.cancel()
即可通知备份或恢复线程中止并尽快返回,返回码为 RESULT_CANCELED
。