【问题标题】:Android Room SQLiteReadOnlyDatabaseExceptionAndroid Room SQLiteReadOnlyDatabaseException
【发布时间】:2021-11-04 13:52:19
【问题描述】:

我已将我的应用转换为使用 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 是读/写的?

【问题讨论】:

    标签: android kotlin android-sqlite android-room


    【解决方案1】:

    当数据库为只读时,我无法找到在极少数情况下捕获 SQLiteReadOnlyDatabaseException 的方法。或者有没有办法确保 ROOM Db 是读/写的?

    消息code 1032 SQLITE_READONLY_DBMOVED:-

    SQLITE_READONLY_DBMOVED 错误代码是 SQLITE_READONLY 的扩展错误代码。 SQLITE_READONLY_DBMOVED 错误代码表示无法修改数据库,因为数据库文件在打开后已被移动,因此任何修改数据库的尝试都可能在进程崩溃时导致数据库损坏,因为 strong>回滚日志不会被正确命名

    如果消息是可信的,那么数据库已被移动/重命名。从消息中可以看出,(正在处理的两个中的一个)数据库在打开时正在被重命名。

    在日志中,许多条目都是相似的,因此看起来好像正在管理两个数据库,即这是从资产创建数据库的阶段。

    这很可能是createFromAsset 处理的问题,我理解它不一定是坚如磐石的。例如目前有issues with the prePackagedDatabaseCallback

    因此,通过使用createFromAsset,您只能提出问题。

    我建议在将控制权交给 Room 之前绕过这个问题并自己预先复制资产。

    • 要进行复制,您无需将数据库作为文件打开。

    另一种选择可能是查看是否仅使用 WAL 模式可以解决问题。由于您正在禁用 WAL 模式,那么我猜您不希望这样做(因此为什么建议作为最后一个)。

    • 这不仅需要不禁用 WAL 模式,还需要在分发之前将资产设置为 WAL 模式。

    【讨论】:

    • 谢谢。我误解了,没有意识到 DAO 本身会在 Room 中创建表,而且我不需要 createFromAssets 行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-25
    • 2018-01-18
    • 2018-05-22
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    相关资源
    最近更新 更多