升级SQLCipher至v4.5.6后,应用被杀重启报错‘file is not a database’
问题分析与解决方案
核心原因
你遇到的问题本质是SQLCipher 4.x与3.x的兼容配置未持续生效:虽然首次启动执行了cipher_migrate完成迁移,但后续启动时未设置必要的兼容参数,导致杀进程后重新打开数据库时,SQLCipher 4.x用默认的兼容级别(4)读取迁移后的数据库,无法识别格式,抛出“file is not a database”错误。
修复方案
不要区分首次/后续启动的构造函数,每次打开数据库都必须设置兼容参数——重复执行cipher_migrate不会有问题,SQLCipher会自动判断是否需要迁移。
- 定义公共的数据库Hook,统一处理兼容配置:
private static final SQLiteDatabaseHook COMMON_DB_HOOK = new SQLiteDatabaseHook() { @Override public void preKey(SQLiteConnection connection) {} @Override public void postKey(SQLiteConnection connection) { // 强制以3.x兼容模式读取数据库,确保4.x驱动能识别 connection.executeRaw("PRAGMA cipher_compatibility=3;", null, null); // 自动判断并执行迁移(仅首次需要,重复执行无副作用) connection.executeRaw("PRAGMA cipher_migrate;", null, null); // 保留原有的KDF迭代配置(如果业务需要) connection.executeRaw("PRAGMA kdf_iter=1000;", null, null); connection.executeRaw("PRAGMA cipher_default_kdf_iter=1000;", null, null); } };
- 修改所有构造函数,统一使用这个公共Hook:
private DbHelper(Context context) { super(context, databaseName, password, null, databaseVersion, 1, null, COMMON_DB_HOOK, false); } private DbHelper(Context context, String secondLaunchCase) { super(context, databaseName, password, null, databaseVersion, 1, null, COMMON_DB_HOOK, false); }
额外排查点
- 验证数据库完整性:用SQLCipher命令行工具打开数据库文件,确认文件未损坏(命令:
sqlcipher your_database.db,输入密码后执行.tables看是否正常)。 - 确认密码一致性:排查杀进程后密码解密逻辑是否异常,确保每次传入的密码完全相同。
- 检查数据库版本号:确认
databaseVersion设置正确,避免触发不必要的迁移导致数据库损坏。
内容的提问来源于stack exchange,提问作者ashish
相关产品推荐
相关产品推荐

