[英]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
,
runBlocking
和allowMainThreadQueries
通常用於測試目的,你不應該在發布產品中使用它們。
allowMainThreadQueries
所做的是授予您從主線程訪問數據庫的權限,這是您永遠不應該做的,因為它可能會凍結 UI。
使用viewModelScope.launch
從Fragment/Activity
或lifecycleScope.launch
從ViewModel
啟動協程,您可能需要顯式添加依賴項
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.