Home > database >  Writing to database with Room causing app to crash
Writing to database with Room causing app to crash

Time:05-05

I've spent the day learning about how to write to databases using Room and have been attempting to write to one in my app. After completing an exercise, the user gets a score and can then press a button if they want to save the score. The button calls saveScore (see below). Logs show that it builds the database but then crashes after that with the error: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

What am I doing wrong?

    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()
        }
    }
    }

Notes:

  • I tried to suspend addScore in ExamScoreDao but that caused a separate error and the app wouldn't compile (Suspend function 'addScore' should be called only from a coroutine or another suspend function)
  • There is code to build the database in two places: in the saveScore function and also in ExamScoreDatabase.kt - I wasn't sure where to add it: some code I found online put it in the database class, but Android didn't recognise the function if I added it here. So I tried within addScore and it worked. Any advice on what is correct would also be appreciated. Thanks.

CodePudding user response:

As the documentation states Room doesn't allow access to the database from the main thread.

So making your function a suspend function is one way to solve this. Assuming your saveScore function is inside your Activity you can do something like:

lifecycleScope.launch {
    examscoreDao.addScore(newScore)
}
  • Related