下载的Room数据库需删除-wal、-shm才可读取,如何无需删除即可正常访问
问题根因
SQLite默认开启WAL(预写日志)模式时,会生成-wal(日志文件)和-shm(共享内存索引文件)两个辅助文件,这两个文件和主数据库文件是强关联的:
- 已生成的
-wal/-shm文件只匹配旧的本地数据库文件,你单独替换主数据库文件后,Room读取时会优先读取-wal中缓存的旧数据,就会出现表内容为空的问题。 - 你上传到Firebase的仅为主数据库文件,没有携带对应绑定的辅助文件,所以替换后本地旧的辅助文件和新主库不兼容。
可行解决方案
方案1:关闭Room的WAL模式(最简单,无额外文件生成)
直接禁用WAL模式,SQLite就不会生成-wal和-shm文件,替换主库后直接就能读取。在你的Room数据库实例构建时添加自定义Callback即可:
Room.databaseBuilder(context, MasterDatabase.class, "master_table") .addCallback(new RoomDatabase.Callback() { @Override public void onOpen(@NonNull SupportSQLiteDatabase db) { super.onOpen(db); // 禁用WAL模式 db.disableWriteAheadLogging(); } }) .build();
注:禁用WAL会略微降低并发读写性能,对绝大多数普通应用无感知。
方案2:上传/下载时携带完整的数据库文件组
你制作上传的数据库文件时,先确保源数据库的WAL已经合并到主库,或者直接将主库、-wal、-shm三个文件一起上传到Firebase,下载时三个文件同时覆盖到本地databases目录下,就不会出现不兼容问题。
方案3:下载完成后主动清理旧辅助文件(现有逻辑最小改动)
不需要手动访问目录删除,在下载成功的回调里直接通过代码删除旧的-wal和-shm文件即可,后续打开Room时会自动生成和新主库匹配的辅助文件:
修改你的下载代码的onSuccess回调部分:
private void downloadMasterDatabase(){ StorageReference pathReference = storageRef.child("master database" + "/master_table"); String masterDbPath = "/data/data/"+ getPackageName() + "/databases/master_table"; File masterDbFile = new File(masterDbPath); pathReference.getFile(masterDbFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() { @Override public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) { Toast.makeText(MainActivity.this, "download was successful" , Toast.LENGTH_SHORT).show(); // 新增:删除旧的wal和shm文件 File walFile = new File(masterDbPath + "-wal"); if (walFile.exists()) walFile.delete(); File shmFile = new File(masterDbPath + "-shm"); if (shmFile.exists()) shmFile.delete(); // 后续再初始化Room实例即可 } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Toast.makeText(MainActivity.this, "download was not successful", Toast.LENGTH_SHORT).show(); } }); }
注:执行删除前要确保所有已经打开的Room数据库实例都已关闭,避免文件占用导致删除失败。
通用注意事项:无论使用哪种方案,都需要保证下载的数据库版本号和你代码中Room注解定义的版本号一致,避免触发不必要的数据库迁移逻辑。
内容的提问来源于stack exchange,提问作者Edward Aarma
相关产品推荐
相关产品推荐

