簡體   English   中英

房間數據庫使用協程異步返回 Null

[英]Room Database Returns Null Using Coroutine Async

我目前正在嘗試在不使用 ViewModel 的情況下從我的房間數據庫中獲取數據。 這是因為我正在開發一個 NotificationHandler,它可以在任何時候由警報管理器觸發。

以下是我到目前為止的代碼。 下面的代碼從另一個 class 調用sortNotification開始。 sortNotification然后調用launchAsyncTask ,后者又通過調用getQuotesFromDatabase進入數據庫。 然后我等待結果(我相信),將數據庫中的數據分配給listOfQuotes變量,然后調用displayNotification來使用它。 我的問題是,當我嘗試使用它時, listOfQuotes總是 null displayNotification

現在我知道數據庫有內容,當我打開我的應用程序和 go 到具有 ViewModel 的 Activity 時,數據已成功檢索。 我認為我的問題可能與異步任務未正確完成或我的 coroutineScope 有關。 當代碼進入displayNotification時,我只需要listOfQuotes來獲取數據。 任何幫助將不勝感激。 提前致謝。

private var job = Job()
private val ioScope = CoroutineScope(Dispatchers.IO + job)
private lateinit var listOfQuotes: LiveData<List<DefaultQuote>>

fun sortNotification() {
    launchAsyncTask()
}

private fun launchAsyncTask() = CoroutineScope(Dispatchers.IO).launch {
    val asyncTask = ioScope.async {
        getQuotesFromDatabase()
    }
    listOfQuotes = asyncTask.await()
    displayNotification()
}

private suspend fun getQuotesFromDatabase(): LiveData<List<DefaultQuote>> {
    return withContext(Dispatchers.IO) {
        val defaultQuoteDao = QuoteDatabase.getDatabase(context, this).defaultQuoteDao()
        val defaultQuoteRepository = DefaultQuoteRepository(defaultQuoteDao)
        defaultQuoteRepository.allQuotes
    }
}

private fun displayNotification() {
    val quote = listOfQuotes.value?.let {
        val size = it.size
        val randomIndex = (0..size).random()
        it[randomIndex]
    } ?: throw NullPointerException("Quotes not found")

// ... then do notification stuff

我還從我的 DAO 中添加了代碼:

@Dao
interface DefaultQuoteDao {
    @Query("SELECT * FROM $DEFAULT_TABLE_NAME")
    fun getAllQuotes(): LiveData<List<DefaultQuote>>
}

以及我的存儲庫中的代碼:

class DefaultQuoteRepository(private val defaultQuoteDao: DefaultQuoteDao) {
    val allQuotes: LiveData<List<DefaultQuote>> = defaultQuoteDao.getAllQuotes()
}

以及QuoteDatabase.getDatabase(Context, CoroutineScope)的代碼:

fun getDatabase(context: Context, scope: CoroutineScope): QuoteDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    QuoteDatabase::class.java,
                    DATABASE_NAME
                )
                    .fallbackToDestructiveMigration()
                    .addCallback(QuoteDatabaseCallback(scope))
                    .build()
                INSTANCE = instance
                return instance
            }
        }

具體問題是您永遠不會觀察listOfQuotes LiveData 的值。 這是啟動從數據庫中提取所必需的。

總的來說,你正在以一種奇怪的方式做到這一點。 您應該使用協程或 LiveData。 它們都允許您觀察數據庫中的數據,但您不需要兩者。 這就像在內部包裝異步調用和異步調用,然后必須將它們都解開。 您應該:

  1. 移除協程並同步返回 LiveData 並觀察它。
  2. 使用 Flow 從您的 dao function getAllQuotes返回Flow<List<DefaultQuote>>

我建議 2. 如果您希望您的應用程序變得中型或復雜。 Flow允許您以更簡潔靈活的方式map或組合數據。

然后,您的 function sortNotification將變為:


// Ideally this should be injected into the class, but for a Service that's a little difficult. 
// At a minimum you should initialize it once for the class
val defaultQuoteRepository: DefaultQuoteRepository by lazy { 
    DefaultQuoteRepository(QuoteDatabase.getDatabase(context, this).defaultQuoteDao())
}

fun sortNotification() {
    defaultQuoteRepository.allQuotes
        .map { listOfQuotes ->
            listOfQuotes.random()
        }
        .flowOn(Dispatchers.IO)
        .onEach { randomQuote ->
            displayNotification(randomQuote)
        }
        // This makes the above onEach lambda run on the main thread so you're safe to show notifications. 
        // Ideally you should have a specific scope defined but tbh if you're not testing it's not that important
        .launchIn(GlobalScope)

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM