android - Android Room SQLiteReadOnlyDatabaseException
问题描述
我已将我的应用程序转换为使用 Android Room for SQLite DB。我的实现在不同的设备上发生了一些崩溃。
Fatal Exception: android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 1032 SQLITE_READONLY_DBMOVED)
at android.database.sqlite.SQLiteConnection.nativeExecute(SQLiteConnection.java)
at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:707)
at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:473)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:261)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:205)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:505)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:206)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:198)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:918)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:898)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:762)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:751)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:373)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:145)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:106)
at androidx.room.SQLiteCopyOpenHelper.getWritableDatabase(SQLiteCopyOpenHelper.java:97)
at androidx.room.RoomDatabase.internalBeginTransaction(RoomDatabase.java:482)
at androidx.room.RoomDatabase.beginTransaction(RoomDatabase.java:471)
at com.luzeon.MyApp.sqlite.ViewLogDao_Impl$5.call(ViewLogDao_Impl.java:94)
at com.luzeon.MyApp.sqlite.ViewLogDao_Impl$5.call(ViewLogDao_Impl.java:91)
at androidx.room.CoroutinesRoom$Companion$execute$2.invokeSuspend(CoroutinesRoom.kt:61)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at androidx.room.TransactionExecutor$1.run(TransactionExecutor.java:47)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
我已经创建了应用程序 Room DB
@Database(entities = [ViewLogModel::class], version = 4)
abstract class MyAppDatabase : RoomDatabase() {
abstract fun viewLogDao(): ViewLogDao
companion object {
// For Singleton Instance
@Volatile
private var INSTANCE: MyAppDatabase? = null
fun getAppDataBase(context: Context): MyAppDatabase {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: Room.databaseBuilder(context.applicationContext, MyAppDatabase::class.java, "MyAppDatabase")
.createFromAsset(“myapp.db")
.setJournalMode(RoomDatabase.JournalMode.TRUNCATE) // disable WAL
.fallbackToDestructiveMigration()
.build()
}
}
fun destroyDataBase(){
INSTANCE = null
}
}
}
并有一个 DB Helper 类
class MyAppDatabaseHelper(private val context: Context, private val coroutineScope: CoroutineScope) {
fun updateViewLog(viewLogModel: ViewLogModel) {
try {
// get the database
val db = MyAppDatabase.getAppDataBase(context)
coroutineScope.launch {
// store in db
db.viewLogDao().insertOrUpdateViewLog(viewLogModel)
}
} catch (e: Exception) {}
}
suspend fun getViewLog(memberId: Int): JSONArray {
try {
val jsonArray = JSONArray()
// get the database
val db = MyAppDatabase.getAppDataBase(context)
val viewLog = db.viewLogDao().getViewLog(memberId)
for (view in viewLog) {
// create the object
val jsonObject = JSONObject()
try {
jsonObject.put("mid", view.mid)
} catch (e: JSONException) {
}
try {
jsonObject.put("uts", view.uts)
} catch (e: JSONException) {
}
jsonArray.put(jsonObject)
}
// clear log (current user records or records older than 24hrs)
db.viewLogDao().deleteViewLog(memberId, Utilities.getUtsYesterday().toFloat())
// return the array
return jsonArray
} catch (e: Exception) {
return JSONArray()
}
}
}
使用 ViewLogDao
@Dao
interface ViewLogDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertOrUpdateViewLog(viewLog: ViewLogModel)
@Update
suspend fun updateViewLog(viewLog: ViewLogModel)
@Delete
suspend fun deleteAllViewLog(viewLog: ViewLogModel)
@Query("DELETE FROM viewlog WHERE VID= :vid OR UTS < :uts")
suspend fun deleteViewLog(vid: Int, uts: Float)
@Query("SELECT * FROM viewLog")
suspend fun getAll(): List<ViewLogModel>
@Query("SELECT * FROM viewLog WHERE vid = :vid")
suspend fun getViewLog(vid: Int): List<ViewLogModel>
}
和 ViewLogModel
@Entity(tableName = "viewLog")
data class ViewLogModel(
@ColumnInfo(name = "VID") val vid: Int,
@ColumnInfo(name = "UTS") val uts: Float,
@ColumnInfo(name = "MID") @PrimaryKey val mid: Int)
SQLiteReadOnlyDatabaseException
当数据库为只读时,我无法找到如何在罕见的情况下捕获。或者有没有办法确保 ROOM Db 是读/写的?
解决方案
当数据库为只读时,我无法找到在极少数情况下捕获 SQLiteReadOnlyDatabaseException 的方法。或者有没有办法确保 ROOM Db 是读/写的?
消息code 1032 SQLITE_READONLY_DBMOVED
:-
SQLITE_READONLY_DBMOVED 错误代码是 SQLITE_READONLY 的扩展错误代码。SQLITE_READONLY_DBMOVED 错误代码表示无法修改数据库,因为数据库文件在打开后已被移动,因此如果由于回滚日志没有正确命名而导致进程崩溃,则任何修改数据库的尝试都可能导致数据库损坏。
如果要相信该消息,则该数据库已被移动/重命名。从消息中可以看出,(正在处理的两个之一)数据库在打开时正在被重命名。
在日志中,许多条目都是相似的,因此看起来好像正在管理两个数据库,即这是从资产创建数据库的阶段。
这很可能是createFromAsset
处理的问题,我理解它不一定是坚如磐石的。例如,目前prePackagedDatabaseCallback存在问题。
因此,通过使用createFromAsset
它,您只能提出问题。
我建议在将控制权交给 Room 之前绕过这个问题并自己预先复制资产。
- 要进行复制,您不需要将数据库作为文件打开。
另一种选择可能是查看是否仅使用 WAL 模式可以解决问题。当您禁用 WAL 模式时,我猜您不希望这样做(因此为什么建议作为最后一个)。
- 这不仅需要不禁用 WAL 模式,而且还需要在分发之前将资产设置为 WAL 模式。
推荐阅读
- google-apps-script - 警报显示许多不必要的信息
- php - 我有这个雄辩的文件,但我收到未定义的变量
- c# - 如何使用基于构建配置的模式包含文件?
- c# - 如何使用 jquery 将 @html.raw div HTML 保存并获取到数据库中并获取
- powershell - 在 Windows 上使用内置命令从 CMD 或 powershell 设置显示分辨率
- apache-kafka - 如何通过 docker-compose 文件为 kafka 挂载卷?
- android - 无法从 Android 调用 Web 服务,但它可以从 UWP 工作
- android - 如何确保应用程序正在读取它写入的相同文件?
- python - 如何使用 psycopg2 更新 postgresql 中多行上的多个列
- java - 计算字符串中的元音