簡體   English   中英

使用協程訪問數據庫

[英]Using coroutines to access the database

到目前為止,每當我想訪問數據庫時,我一直在使用這種模式:

runBlocking {
    launch {
        // fetch something from the database and put it to some view
    }
}

現在我更深入地研究 Kotlin 協程,我越來越相信這是一個糟糕的模式。 本質上,我還不如只allowMainThreadQueries ,因為我的模式無論如何都會阻塞主線程。

不幸的是,我還沒有找到合適的模式。 如何有效地使用 Kotlins 協程訪問數據庫?

runBlocking是協程的唯一入口點嗎?

考慮這種情況:

override fun onCreate() {
    setContentView(someLayout)

    // concurrently fetch something from the database and put it in some view
    // onCreate may return before this has finished

    someButton.setOnClickListener {
        // concurrently insert or update something in the database
    }
}

永遠不應該在 Android 項目中使用runBlocking ,除非您將 Kotlin 協程代碼與一些無法使用協程的 Java 代碼混合在一起,並且它需要一種在其自己的后台線程上以阻塞方式調用某些協程的方法。 在這種情況下,您可能會使用runBlocking創建一個橋接函數以供 Java 代碼調用,但您永遠不會從 Kotlin 調用此函數,當然也永遠不會從主線程調用它。 在主線程上調用阻塞代碼會凍結 UI,這會使您的應用程序感覺卡頓並有可能觸發 ANR(應用程序無響應)錯誤。

啟動協程的正確方法是使用 CoroutineScope 來啟動你的協程。 Android Jetpack 框架已經為您提供了這些活動、片段和視圖模型。

在 Activity 中,使用lifecycleScope.launch 在 Fragment 中,您通常應該使用viewLifecycleOwner.lifecycleScope.launch 在 ViewModel 中,使用viewModelScope.launch

使用 CoroutineScope 代替runBlocking有什么作用? 它可以防止長時間運行的掛起操作(例如從磁盤讀取數據庫)阻塞主線程並凍結您的 UI。 並且在Activity/Fragment/ViewModel被拆除時會自動取消長時間運行的工作,從而防止內存泄漏和資源浪費。

假設您正在使用Room

runBlockingallowMainThreadQueries通常用於測試目的,你不應該在發布產品中使用它們。

allowMainThreadQueries所做的是授予您從主線程訪問數據庫的權限,這是您永遠不應該做的,因為它可能會凍結 UI。

使用viewModelScope.launchFragment/ActivitylifecycleScope.launchViewModel啟動協程,您可能需要顯式添加依賴項

def lifecycleVersion = '2.4.0'
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
// Lifecycles only (without ViewModel or LiveData)
implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion")

生命周期發行說明https://developer.android.com/jetpack/androidx/releases/lifecycle

您應該從ViewModel調用database操作以防止取消配置更改,如果用戶在操作進行時旋轉屏幕,它將被取消並且結果不會被緩存。

在活動/片段中

// read data from db
lifecycleScope.launch {
   viewModel.someData.collect { 
      //do some stuff
   }
}

// insert
someButton.setOnClickListener {
   viewModel.insertToDatabase(someData)
}

在視圖模型中

class MainViewModel (val dao: Dao) : ViewModel() {
  
  // you should expose your data as Observable
  val someData : Flow<List<SomeData>> = dao.getAllSomeData()
   
  fun insertToDatabase(data:SomeData) = viewModelScope.launch {
    dao.insert(data)
  }
}
// query
@Query("SELECT * FROM some_data_table")
abstract fun getAllSomeData(): Flow<List<SomeData>>

暫無
暫無

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

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