expo-sqlite在Android与iOS上的差异及备份恢复异常问题
Expo SQLite 备份恢复在 Android 端仅恢复初始数据库的解决方案
问题根源
在 Android 平台上,SQLite 默认启用 Write-Ahead Log(WAL) 模式,expo-sqlite 会在FileSystem.documentDirectory + 'SQLite/'目录下生成三个关联文件:
db.db:主数据库文件db.db-wal:日志文件,存储最新的数据变更db.db-shm:共享内存文件,配合 WAL 机制工作
如果备份时仅导出db.db,实际最新数据仍保留在db.db-wal中,恢复后自然只能看到初始数据库;而 iOS 上 SQLite 默认日志模式不同,因此无此问题。
解决步骤
1. 备份前合并 WAL 到主数据库
导出数据库前,执行 SQLite 的 checkpoint 命令,将 WAL 文件中的数据合并到主db.db,只需备份单个文件即可:
import * as SQLite from 'expo-sqlite'; import * as FileSystem from 'expo-file-system'; const db = SQLite.openDatabase('db.db'); // 合并WAL日志到主数据库 const mergeWalData = () => { return new Promise((resolve, reject) => { db.transaction(tx => { tx.executeSql( 'PRAGMA wal_checkpoint(FULL);', [], () => resolve(), (_, err) => reject(err) ); }); }); }; // 完整备份流程 const backupDatabase = async () => { await mergeWalData(); const sourcePath = `${FileSystem.documentDirectory}SQLite/db.db`; const backupPath = `${FileSystem.documentDirectory}backup/db.db`; // 替换为你的外部存储路径(如iCloud) await FileSystem.copyAsync({ from: sourcePath, to: backupPath }); };
2. 恢复时清理所有关联文件
恢复前必须删除 Android 端 SQLite 目录下的三个文件,避免残留的 WAL/shm 文件覆盖导入的数据:
const restoreDatabase = async (backupFilePath) => { const sqliteDir = `${FileSystem.documentDirectory}SQLite/`; // 删除所有关联文件(idempotent设为true避免文件不存在时报错) await Promise.all([ FileSystem.deleteAsync(`${sqliteDir}db.db`, { idempotent: true }), FileSystem.deleteAsync(`${sqliteDir}db.db-wal`, { idempotent: true }), FileSystem.deleteAsync(`${sqliteDir}db.db-shm`, { idempotent: true }) ]); // 导入备份的数据库文件 await FileSystem.copyAsync({ from: backupFilePath, to: `${sqliteDir}db.db` }); // Android需彻底杀掉应用进程后重启,确保数据库重新加载 };
3. 调试验证目录文件
可打印 SQLite 目录下的所有文件,确认 WAL 和 shm 文件存在:
const checkSqliteFiles = async () => { const files = await FileSystem.readDirectoryAsync(`${FileSystem.documentDirectory}SQLite/`); console.log('SQLite目录文件:', files); };
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

