使用 Room 写入数据库导致应用程序崩溃

Writing to database with Room causing app to crash

我花了一天时间学习如何使用 Room 写入数据库,并一直在尝试在我的应用程序中写入一个数据库。完成练习后,用户会得到一个分数,如果他们想保存分数,可以按一个按钮。该按钮调用 saveScore(见下文)。日志显示它构建了数据库,但随后崩溃并出现错误:java.lang.IllegalStateException: 无法在主线程上访问数据库,因为它可能会长时间锁定 UI时间.

我做错了什么?

    private fun saveScore(activityName: String, score: Int) {
        Log.v("AbdominalExamNew.kt", "saveScore button pressed")
        val dateAdded = Date()
        val newScore = ExamScore(0, activityName, dateAdded, score)
        val db = Room.databaseBuilder(applicationContext, ExamScoreDatabase::class.java, "examscore_database").build()
        Log.v("AbdominalExamew.kt","Database built")
        val examscoreDao = db.examscoreDao()
        examscoreDao.addScore(newScore)
    }

ExamScore.kt

package com.example.clinicalskills.database

import androidx.room.*

@Entity
data class ExamScore(
    @PrimaryKey(autoGenerate = true) val uid: Int,
    @ColumnInfo(name="exam") val currentExam: String?,
    @ColumnInfo(name="date") val dateAdded: java.util.Date,
    @ColumnInfo(name="score") val attemptScore: Int?
)

ExamScoreDao.kt

package com.example.clinicalskills.database

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query

@Dao
interface ExamScoreDao {
    @Query("SELECT * FROM examscore")
    fun getAll(): List<ExamScore>

    @Query("SELECT * FROM examscore WHERE uid IN (:attemptRecord)")
    fun loadAllByIds(attemptRecord: IntArray): List<ExamScore>

    @Query("SELECT * FROM examscore WHERE exam = :selectedExam")
    fun viewScoresForExam(selectedExam: String): List<ExamScore>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun addScore(examScore: ExamScore)
}

ExamScoreDatabase.kt

package com.example.clinicalskills.database

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

@Database(entities = [ExamScore::class], version = 1, exportSchema = false)
@TypeConverters(TimeConverters::class)
abstract class ExamScoreDatabase : RoomDatabase() {
    abstract fun examscoreDao(): ExamScoreDao

    companion object {
        @Volatile
        private var INSTANCE: ExamScoreDatabase? = null

        fun getDatabase(context: Context): ExamScoreDatabase {
            Log.v("ExamScoreDatabase.kt", "getDatabase run")
            if (INSTANCE == null) {
                synchronized(this) {
                    INSTANCE = buildDatabase(context)
                }
            }
            return INSTANCE!!
        }

        private fun buildDatabase(context: Context): ExamScoreDatabase? {
            Log.v("ExamcoreDatabase.kt", "buildDatabase run")
            return Room.databaseBuilder(
                context.applicationContext,
                ExamScoreDatabase::class.java,
                "examscore_database"
                    ).build()
        }
    }
    }

备注:

正如 documentation 所述 Room 不允许从主线程访问数据库。

所以让你的函数成为一个挂起函数是解决这个问题的一种方法。假设你的 saveScore 函数在你的 Activity 中,你可以这样做:

lifecycleScope.launch {
    examscoreDao.addScore(newScore)
}