使用 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()
}
}
}
备注:
- 我试图在 ExamScoreDao 中挂起
addScore
,但这导致了一个单独的错误,应用程序无法编译(挂起函数 'addScore' 只能从协程或另一个挂起函数中调用)
- 在两个地方有构建数据库的代码:在
saveScore
函数和 ExamScoreDatabase.kt
中 - 我不确定在哪里添加它:我在网上找到的一些代码把它在数据库 class 中,但 Android 无法识别我在此处添加的函数。所以我在 addScore
内尝试过,它成功了。任何关于什么是正确的建议也将不胜感激。谢谢
正如 documentation 所述 Room 不允许从主线程访问数据库。
所以让你的函数成为一个挂起函数是解决这个问题的一种方法。假设你的 saveScore
函数在你的 Activity 中,你可以这样做:
lifecycleScope.launch {
examscoreDao.addScore(newScore)
}
我花了一天时间学习如何使用 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()
}
}
}
备注:
- 我试图在 ExamScoreDao 中挂起
addScore
,但这导致了一个单独的错误,应用程序无法编译(挂起函数 'addScore' 只能从协程或另一个挂起函数中调用) - 在两个地方有构建数据库的代码:在
saveScore
函数和ExamScoreDatabase.kt
中 - 我不确定在哪里添加它:我在网上找到的一些代码把它在数据库 class 中,但 Android 无法识别我在此处添加的函数。所以我在addScore
内尝试过,它成功了。任何关于什么是正确的建议也将不胜感激。谢谢
正如 documentation 所述 Room 不允许从主线程访问数据库。
所以让你的函数成为一个挂起函数是解决这个问题的一种方法。假设你的 saveScore
函数在你的 Activity 中,你可以这样做:
lifecycleScope.launch {
examscoreDao.addScore(newScore)
}