實戰 | 在 Room 中使用 Flow

Room 中對 Flow 的支持

Jetpack Room 對協程的支持越來越豐富: Room 2.1 版本增加了對協程的支持,並加入了一次性 (one-shot) 的讀寫操作,Room 2.2 我們通過 Flow 爲讀操作加入了可觀察性,當數據庫中的數據有變化時它可以使您收到通知。

Room 支持異步 query 操作

  • Flow

    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/

Flow 實戰

假設我們有一個記錄小狗信息的數據庫,它的 name 字段是主鍵 (primary key),所以在數據庫中不可能同時有兩個 name 字段相同的數據,也就是每隻小狗都是唯一的。

@Entity
data class Dog (
    @PrimaryKey val name: String,
    val cuteness: Int,
    val barkingVolume: Int
)

爲了從數據中獲取一個包含所有小狗信息的總表,我們在 DAO 中編寫如下 query 語句:

@Query("SELECT * FROM Dog")
fun getAllDogs(): List<Dog>
  • DAO

    https://developer.android.google.cn/training/data-storage/room/accessing-data

因爲小狗的叫聲,也就是字段 barkingVolume 會隨着時間變化,並且我們想確保 UI 展示的內容是最新的。因此我們希望,當數據庫中的數據有變化時,可以通知到我們: 比如新增,刪除,或者是更新了數據。

爲了實現這個功能,我們通過更新 query 操作返回一個 Flow 對象。

@Query("SELECT * FROM Dog")
fun getAllDogs(): Flow<List<Dog>>

就像這樣,每當數據庫中的數據有更新時,會重新派發存有小狗信息的總表。例如,假設我們的數據庫中存有如下數據:

(Frida, 11, 3)
(Bandit, 12, 5)

第一次調用 getAllDogs 時 Flow 派發的數據如下:

[(Frida, 11, 3), (Bandit, 12, 5)]

如果小狗 Bandit 比較興奮,那它的叫聲也會變大,也就是字段 barkingVolume 更新爲 6: (Bandit,12,6),這時候 Flow 會重新派發最新數據,所以整個列表被更新爲:

[(Frida, 11, 3), (Bandit, 12, 6)]

現在我們來看一下獲取單隻小狗詳細信息的操作,爲了能夠實時地獲取小狗的最新數據,我們返回 Flow:

@Query("SELECT * FROM Dog WHERE name = :name")
fun getDog(name: String): Flow<Dog>

如果我們調用 getDog("Frida"),Flow 會返回一個對象: (Frida, 11, 3)。

只要是數據庫中的任意一個數據有更新,無論是哪一行數據的更改,那就重新執行 query 操作並再次派發 Flow,因此當小狗 Frida 有更新時我們會收到最新的數據。同樣道理,如果一個不相關的數據,比如小狗 Bandit 有更新時我們的 Flow 也會被派發,而且會收到與之前相同的數據: (Frida, 11, 3)。

這是因爲 SQLite 數據庫的內容更新通知功能是以表 (Table) 數據爲單位,而不是以行 (Row) 數據爲單位,因此只要是表中的數據有更新,它就觸發內容更新通知。Room 不知道表中有更新的數據是哪一個,因此它會重新觸發 DAO 中定義的 query 操作。您可以使用 Flow 的操作符,比如 distinctUntilChanged 來確保只有在當您關心的數據有更新時纔會收到通知。

@Dao
abstract class DoggosDao {
    @Query("SELECT * FROM Dog WHERE name = :name")
    abstract fun getDog(name: String): Flow<Dog>
    fun getDogDistinctUntilChanged(name:String) =   
           getDog(name).distinctUntilChanged()
}
  • distinctUntilChanged

    https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/distinct-until-changed.html

推薦您通過 Flow 進行可觀察的讀操作,以獲取數據庫中數據更新的通知!您可以在您的整個應用中使用協程 (Coroutine) 和 Flow,而且還可使用 Jetpack 庫中支持的其他協程功能,比如: 生命週期感知型協程範圍 (lifecycle-aware coroutine scopes) 、掛起生命週期感知型協程 (suspend lifecycle-aware coroutines),也包括 Flow 轉 LiveData 的操作。

  • lifecycle-aware coroutine scopes

    https://developer.android.google.cn/topic/libraries/architecture/coroutines#lifecycle-aware

  • suspend lifecycle-aware coroutines

    https://developer.android.google.cn/topic/libraries/architecture/coroutines#suspend

  • Flow 轉 LiveData

    https://developer.android.google.cn/reference/kotlin/androidx/lifecycle/package-summary#aslivedata

查看更多使用 Flow 的案例,可參考我們之前發佈的一篇基於 Android 開發者峯會應用的最佳實踐的文章。


推薦閱讀




 點擊屏末  | 查看 Android 官方中文文檔 —— 使用 Kotlin 更快地編寫更出色的 Android 應用


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章