SQLite主数据库文件未更新,-wal文件更新,导出无法获取新增数据
问题原因
你遇到的问题源于SQLite的WAL(Write-Ahead Log)写入模式:
- Android默认给SQLite启用WAL模式,新增/修改的数据会先写入
database-wal日志文件,而非直接更新主database.db文件。 - 只有当数据库空闲、日志达到阈值等特定条件触发时,SQLite才会自动把WAL里的数据合并到主DB文件。直接拷贝主DB文件,自然会漏掉这些未合并的新增数据。
解决方案
要导出完整数据,推荐以下两种方案:
方案1:强制合并WAL数据到主DB后再拷贝
在拷贝主DB文件前,执行SQLite检查点命令,强制将WAL中的所有数据合并到主数据库,之后拷贝的database.db就会包含所有数据。
修改后的代码如下:
protected Boolean exportarBD(Context context, Activity activity) throws IOException { Boolean exported = false; if (!Permisos.validarWriteExternalStorage(context, activity)){ return exported; } File backupDir = new File(context.getExternalFilesDir(null), "mydatabase/backup"); if (!backupDir.exists()) { backupDir.mkdirs(); // 创建目录(如果不存在) } // 定义导出文件名 String timestamp = new Date().toString(); File exportFile = new File(backupDir, "DB_exported_" + timestamp + ".db"); // 先执行WAL检查点,合并数据到主DB SQLiteDatabase db = null; try { db = SQLiteDatabase.openDatabase( context.getDatabasePath("database.db").getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE ); // 强制执行完整检查点,将WAL数据合并到主DB db.execSQL("PRAGMA wal_checkpoint(FULL);"); } catch (SQLiteException e) { e.printStackTrace(); return exported; } finally { if (db != null && db.isOpen()) { db.close(); } } File dbFile = new File(context.getApplicationContext().getDatabasePath("database.db").getAbsolutePath()); if (dbFile.exists()) { // 拷贝主DB文件 FileInputStream fis = new FileInputStream(dbFile); FileOutputStream fos = new FileOutputStream(exportFile); byte[] buffer = new byte[1024]; int length; while ((length = fis.read(buffer)) > 0) { fos.write(buffer, 0, length); } fos.flush(); fos.close(); fis.close(); exported = true; } else { exported = false; } return exported; }
方案2:同时拷贝WAL和SHM文件
如果不想合并数据,可以同时导出database.db、database-wal和database-shm三个文件,后续恢复时将这三个文件放在同一目录下,SQLite会自动读取WAL中的数据。
需在代码中添加对这两个文件的拷贝逻辑:
// 拷贝WAL文件 File walFile = new File(context.getDatabasePath("database.db").getParent(), "database-wal"); if (walFile.exists()) { String timestamp = new Date().toString(); File exportWal = new File(backupDir, "DB_exported_" + timestamp + ".wal"); copyFile(walFile, exportWal); } // 拷贝SHM文件 File shmFile = new File(context.getDatabasePath("database.db").getParent(), "database-shm"); if (shmFile.exists()) { String timestamp = new Date().toString(); File exportShm = new File(backupDir, "DB_exported_" + timestamp + ".shm"); copyFile(shmFile, exportShm); } // 新增拷贝工具方法 private void copyFile(File source, File dest) throws IOException { FileInputStream fis = new FileInputStream(source); FileOutputStream fos = new FileOutputStream(dest); byte[] buffer = new byte[1024]; int length; while ((length = fis.read(buffer)) > 0) { fos.write(buffer, 0, length); } fos.flush(); fos.close(); fis.close(); }
注意事项
- 执行检查点时,要确保没有其他线程对数据库进行写操作,避免数据不一致。
- 方案1导出的单个DB文件可直接用于其他SQLite环境;方案2需要三个文件同时存在才能读取完整数据。
内容的提问来源于stack exchange,提问作者Luis M. Rios
相关产品推荐
相关产品推荐

