Android P从assets复制数据库后出现SQLite: No Such Table Error问题求助
这不是Android P的已知bug,而是API 28(Android P)引入的SQLite默认行为变更导致的问题——从Android P开始,SQLite默认启用了WAL(Write-Ahead Logging)日志模式,而此前的Android版本默认使用的是DELETE模式。
具体为什么会出现"No such table"?
当你从assets复制数据库文件到应用目录后,SQLite会自动尝试创建对应的.db-wal(预写日志文件)和.db-shm(共享内存文件)。如果你的assets中的数据库在打包前没有被正确关闭(比如生成数据库时没断开连接,残留了WAL相关文件),或者你直接覆盖现有数据库文件时没有清理旧的WAL/shm文件,SQLite会优先读取WAL文件中的数据,而非你刚复制的主数据库文件,最终导致数据库加载异常,表现为找不到表。
解决方案
这里提供几个可行的解决办法,按优先级排序:
1. 复制前清理旧的数据库相关文件
在执行复制操作前,先删除应用数据库目录下已存在的主数据库、WAL和共享内存文件,避免残留数据干扰:
// 获取数据库文件路径 File dbFile = mContext.getDatabasePath(Utils.getDatabaseName()); // 对应WAL和shm文件路径 File walFile = new File(dbFile.getParent(), Utils.getDatabaseName() + "-wal"); File shmFile = new File(dbFile.getParent(), Utils.getDatabaseName() + "-shm"); // 删除旧文件 if (dbFile.exists()) dbFile.delete(); if (walFile.exists()) walFile.delete(); if (shmFile.exists()) shmFile.delete(); // 执行你的复制逻辑 inputStream = mContext.getAssets().open(Utils.getDatabaseName()); if(inputStream != null) { int mFileLength = inputStream.available(); String filePath = dbFile.getAbsolutePath(); output = new FileOutputStream(filePath); byte data[] = new byte[1024]; long total = 0; int count; while ((count = inputStream.read(data)) != -1) { total += count; if(mFileLength != -1) { publishProgress((int) (total * 100 / mFileLength)); } output.write(data, 0, count); } // 关闭流 inputStream.close(); output.close(); return true; }
2. 复制完成后强制禁用WAL模式(如果不需要WAL特性)
打开数据库后,强制切换回旧的DELETE日志模式,这样SQLite就不会依赖WAL文件:
// 复制完成后打开数据库 SQLiteDatabase db = SQLiteDatabase.openDatabase(filePath, null, SQLiteDatabase.OPEN_READWRITE); // 切换日志模式为DELETE db.execSQL("PRAGMA journal_mode=DELETE;"); // 关闭数据库,让变更生效 db.close();
3. 确保assets中的数据库是"干净"的
在打包数据库到assets前,确保数据库已经被正确关闭(比如在生成数据库的工具中,执行完所有操作后正常断开SQLite连接),并且只将主数据库文件(比如your_db.db)打包到assets,不要包含自动生成的.db-wal和.db-shm文件。
总结
这个问题完全是Android P对SQLite默认配置的变更导致的,只要处理好WAL相关的文件或者切换回旧的日志模式,就能在Android P及以上版本正常使用复制的数据库。
内容的提问来源于stack exchange,提问作者Michael J

