装有Kotlin协程的房间观察数据库变化

时间:2019-01-31 06:46:29

标签: android kotlin kotlinx.coroutines

与协同程序所以,我最近开始实验,我从Rxjava2切换到协同程序,我还没有得到它的把握尚未但尽管如此,我遇到了,我需要观察我的数据库的变化和更新相应UI的条件

RxJava过去曾向我提供Flowable,Completeable等,使我能够观察Db的变化。

    abstract fun insert(data: SomeData): Long

    @Query("SELECT * FROM somedata_table")
    abstract fun getData(): Flowable<List<SomeData>>

所以现在我在这里订阅了getData,并且总是用来观察更改

现在输入协程,我正在使用具有延迟结果的暂停函数返回我的回复

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract fun insert(data: SomeData): Long

    @Query("SELECT * FROM somedata_table")
    abstract fun getData(): List<SomeData>
suspend fun getAllSomeData():Deferred<List<SomeData>>{
        return GlobalScope.async (context= coroutineContext){
            database.myDao().getData()
        }
    }

现在我无法收听更新,协程中的频道可能是正确的答案?但我不确定如何在Room中使用它。

4 个答案:

答案 0 :(得分:5)

原始:

您应使用undefined method `replace' for #<Nokogiri::XML::NodeSet:0x00007f4e458bbb78> 代替LiveDataRxJavaFlowable等-> Single)。在您使用LiveData(正在更新与Room DB更改相对应的UI)的情况下,RxJava将为您完成相同的工作,只是它由 Google 创建并专为替代外部库而设计。它是建筑组件堆栈的一部分,与 Room 相同。

有关如何在 Room 中使用它的指导,请阅读herehere

在将协同程序 Room 集成时,目前似乎有no out-of-the-box solution providedit is being worked on。您可能可以使用this article


更新:

房间 2.1(Alpha)起,添加了对科特林 协程的支持。在this article中可以找到有关如何使用它的一些详细信息。

注意: Room尚不支持频道,因为它仍然是实验功能。


祝你好运:)

答案 1 :(得分:5)

目前,有两种不同的方法可以做到这一点。第一种是使用liveData构建器函数。要使此工作有效,您需要将生命周期更新为androidx.lifecycle:*:2.2.0-alpha01或任何更新的版本。 LiveData构建器函数将用于异步调用getData(),然后使用emit()发出结果。使用此方法,您可以将Room getData()函数修改为暂停函数,并使返回类型包装为LiveData,从而替换之前使用的Flowable。

@Query("SELECT * FROM somedata_table")
abstract suspend fun getData(): LiveData<List<SomeData>>

在视图模型中,创建一个引用Room数据库的liveData

val someData: LiveData<SomeData> = liveData {
    val data = database.myDao().getData() 
    emit(data)
}

第二种方法是从数据库中获取数据作为Flow。要使用此功能,您需要将Room更新为androidx.room:room-*:2.2.0-alpha02(当前为最新版本)或更高版本。此更新使@Query DAO方法的返回类型为Flow如果查询中的观察表无效,则返回的Flow将重新发射一组新值。声明具有Channel返回类型的DAO函数是错误的,Room鼓励您使用Flow,然后使用相邻的函数将Flow转换为Channel。

@Query("SELECT * FROM somedata_table")
abstract suspend fun getData(): Flow<List<SomeData>>

要获取和更新数据,我们将在Presenter / ViewModel中将接收到的冷数据流转换为热通道。最好在Android体系结构组件的ViewModel中执行此操作。下面给出的解决方案假定我们在具有提供的viewModelScope的ViewModel中进行此操作。

fun loadData(){
    val channel = database.myDao().getData().distinctUntilChanged().produceIn(viewModelScope)
    viewModelScope.launch {
        for(i in channel){
            withContext(Dispatchers.Main){ /** Update your obsevable data type with i **/ }
        }
    }

}

答案 2 :(得分:3)

使用 Room 2.2.0流程 kotlin协程。它有争议,但我不喜欢 LiveData ,因为它可以在UI线程上为您提供结果。如果必须执行任何数据解析,则必须将所有内容推回另一个IO线程。与直接使用频道相比,它更干净,因为每次您想收听事件时,您都必须执行额外的 openSubscription()。consumeEach {..} 呼叫。

流方法需要以下版本:

//此版本在其非实验版本中使用协程和流程

org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2
androidx.room:room-runtime:2.2.0
androidx.room:room-compiler:2.2.0

道:

@Dao
interface MyDao {
     @Query("SELECT * FROM somedata_table")
     fun getData(): Flow<List<SomeData>>
}

课堂观察:

launch {
   dao.getData().collect { data ->
    //handle data here
   }
}

如果您的调用类本身不是 CoroutineScope ,则必须在上下文上下文中调用launch。可以是 GlobalScope 或您创建的其他一些类。

GlobalScope.launch {
   dao.getData().collect { data ->
    //handle data here
   }
}

收集 lambda会像在Rx onNext 调用中一样,接收到表中的每个udpate。

答案 3 :(得分:2)

等级依赖性:

dependencies {
    compile group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-reactive', version: '1.1.1'
}

房间

@Dao
interface HistoryDao : BaseDao<HistoryEntity> {

    @Query("select * from History order by time desc")
    fun observe(): Flowable<List<HistoryEntity>>

    ...
}

Interactor(下面的 browserHistoryInteractor (位于dao和Fragment / Presenter之间的层)

// To get channel of List<HistoryEntity>:
import kotlinx.coroutines.reactive.openSubscription

fun observe() = historyDao.observe().openSubscription() // convert list to Coroutines channel

演示者/片段/活动(终点(在我的情况下,这是生命周期感知的演示者))

import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

private val compositeJob = Job() // somewhat equivalent "compositeDisposable" in rx

override fun onCreate() {
    super.onCreate()

    launch(compositeJob) { // start coroutine
        val channel = browserHistoryInteractor.observe() 
        for (items in channel) {  // waits for next list of items (suspended)
            showInView { view?.setItems(items) }
        }
    }
}

override fun onDestroy() {
    compositeJob.cancel() // as in rx you need to cancel all jobs
    super.onDestroy()
}

https://www.youtube.com/watch?v=lh2Vqt4DpHU&list=PLdb5m83JnoaBqMWF-qqhZY_01SNEhG5Qs&index=5在29:25