【问题标题】:Android Room slow migration race conditionAndroid Room 缓慢迁移竞争条件
【发布时间】:2019-10-09 17:16:58
【问题描述】:

房间版2.2.0-rc01 Android版本7.18.1

我有一个有点大的迁移。添加几个表、索引等。当它运行时,它会锁定数据库。由于需要一段时间,它会在应用程序的所有其他部分导致android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

有没有办法在尝试使用数据库之前等待迁移完成?

缩写迁移:

  try {
            // database.beginTransaction()

            database.execSQL("ALTER TABLE DeviceItem ADD COLUMN #### INTEGER NOT NULL DEFAULT 0")
            database.execSQL("ALTER TABLE DeviceItem ADD COLUMN #### TEXT NOT NULL DEFAULT ''")

            database.execSQL("CREATE INDEX IF NOT EXISTS ### ON #### (###, ###)")
           //helper method that just creates the table
            createLocalityTable(database)
            //database.setTransactionSuccessful()


        } catch (e: SQLiteException) {
            Crashlytics.logException(e)
            throw e
        } catch (e: Throwable) {
            Crashlytics.logException(e)
            throw e
        } finally {
            // database.endTransaction()
            database.close()
        }

这是我的 appDatabase 单例

package com.company.app.data

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters

@Database(entities = [someClass::class, otherClass::class, etc::class], version = 3)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun somethingDao(): somethingDao

    @Synchronized
    override fun close() {
        if (INSTANCE != null) {
            super.close()
            INSTANCE = null
        }
    }
    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        var canGo = false
        fun getAppDatabase(context: Context): AppDatabase? {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance
            }

            synchronized(this) {
                val instance = Room.databaseBuilder(
                        context.applicationContext,
                        AppDatabase::class.java,
                        "myDb"
                ).addMigrations(MIGRATION_X_X).build()

                INSTANCE = instance
                return instance
                }
        }
    }
}

【问题讨论】:

    标签: android kotlin android-sqlite android-room


    【解决方案1】:

    我相信您的问题是您不应该尝试关闭数据库,因为当调用迁移时,数据库本身已经被 Room 打开(因此它如何知道版本以及是否调用迁移)。

    Room 不希望在迁移完成后关闭数据库,因此它认为其他东西已经关闭/访问了数据库。

    此外,无需使用事务,因为 Room 在事务本身中调用 migrate 方法。

    此方法已在事务内部调用,并且该事务实际上可能是所有必要迁移的复合事务。

    Migration

    我建议您的迁移代码应该是:-

            database.execSQL("ALTER TABLE DeviceItem ADD COLUMN #### INTEGER NOT NULL DEFAULT 0")
            database.execSQL("ALTER TABLE DeviceItem ADD COLUMN #### TEXT NOT NULL DEFAULT ''")
            database.execSQL("CREATE INDEX IF NOT EXISTS ### ON #### (###, ###)")
            //helper method that just creates the table
            createLocalityTable(database)
    
    • createLocalityTable 方法也不应该关闭数据库。

    【讨论】:

      【解决方案2】:

      您可以尝试阻塞主线程,直到数据库解锁。

      在您的 appDatabase 单例中,添加此函数,然后在 MainActivity 的 onCreate() 函数中调用它,高于其他任何内容。您可能希望在启动画面后执行此操作。

              /**
               * Blocks app until Database is not locked or in a transaction.
               * Should only need to be called once when running a large migration
               */
              fun blockUntilDbIsAccessible(context: Context) {
                  //Timber.d("Accessing Database...")
                  val dbStartTime = System.currentTimeMillis()
                  runBlocking {
                      val database = getAppDatabase(context)
                      var isLocked = true
      
                      while(isLocked) {
                          try {
                              isLocked = database.inTransaction()
                          } catch(ex: SQLiteDatabaseLockedException) {
                              delay(500)
                          }
                      }
                  }
                  val dbEndTime = System.currentTimeMillis()
                  //Timber.d("Accessing Database took ${dbEndTime - dbStartTime}ms")
              }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-12-14
        • 1970-01-01
        • 2015-12-02
        • 1970-01-01
        • 1970-01-01
        • 2022-01-23
        • 2018-10-08
        • 1970-01-01
        相关资源
        最近更新 更多