将 LiveData 转换为 MutableLiveData

Posted

技术标签:

【中文标题】将 LiveData 转换为 MutableLiveData【英文标题】:Convert LiveData to MutableLiveData 【发布时间】:2018-11-29 07:59:55 【问题描述】:

显然,Room 无法处理 MutableLiveData,我们必须坚持使用 LiveData,因为它返回以下错误:

error: Not sure how to convert a Cursor to this method's return type

我以这种方式在我的数据库助手中创建了一个“自定义”MutableLiveData:

class ProfileRepository @Inject internal constructor(private val profileDao: ProfileDao): ProfileRepo

    override fun insertProfile(profile: Profile)
        profileDao.insertProfile(profile)
    

    val mutableLiveData by lazy  MutableProfileLiveData() 
    override fun loadMutableProfileLiveData(): MutableLiveData<Profile> = mutableLiveData

    inner class MutableProfileLiveData: MutableLiveData<Profile>()

        override fun postValue(value: Profile?) 
            value?.let  insertProfile(it) 
            super.postValue(value)
        

        override fun setValue(value: Profile?) 
            value?.let  insertProfile(it) 
            super.setValue(value)
        

        override fun getValue(): Profile? 
            return profileDao.loadProfileLiveData().getValue()
        
    

这样,我从 DB 获取更新,可以保存 Profile 对象,但我不能修改属性。

例如: mutableLiveData.value = Profile() 会工作。 mutableLiveData.value.userName = "name" 会调用 getValue() 而不是 postValue() 并且不会工作。

有人找到解决办法了吗?

【问题讨论】:

【参考方案1】:

说我疯了,但 AFAIK 没有理由为您从 DAO 收到的对象使用 MutableLiveData。

这个想法是你可以通过LiveData&lt;List&lt;T&gt;&gt;公开一个对象

@Dao
public interface ProfileDao 
    @Query("SELECT * FROM PROFILE")
    LiveData<List<Profile>> getProfiles();

现在你可以观察它们了:

profilesLiveData.observe(this, (profiles) -> 
    if(profiles == null) return;

    // you now have access to profiles, can even save them to the side and stuff
    this.profiles = profiles;
);

因此,如果您想让这些实时数据“发出新数据并对其进行修改”,则需要将配置文件插入数据库。写入将重新评估此查询,并在新的配置文件值写入 db 后发出。

dao.insert(profile); // this will make LiveData emit again

所以没有理由使用getValue/setValue,只需写入您的数据库即可。

【讨论】:

我明白你的意思。现在的解决方案与拥有 LiveData 对象并保存在数据库中没有区别。这里的想法是能够独立修改数据库中的一些属性。 当您独立于数据库修改对象,然后从网络获取对象,保存到数据库时会发生什么,现在您有一个“来自网络的新状态”和一个对象是本地修改的吗? @EpicPandaForce 面临类似问题的 db 更新未反映在实时数据上。这就是为什么我试图发布到可变的实时数据对象 如果你通过 DAO 插入 DB,那么如果 LiveData&lt;List&lt;T&gt;&gt; 从 DAO 暴露并且你观察它,那么你收到新数据。【参考方案2】:

如果你真的需要,那么你可以使用中介技巧。

在您的 ViewModel 中

 val sourceProduct: LiveData<Product>() = repository.productFromDao()
 val product = MutableLiveData<Product>()
    
 val mediator = MediatorLiveData<Unit>()

 init 
      mediator.addSource(sourceProduct,  product.value = it )
 

在片段/活动中

observe(mediator, )
observe(product,  /* handle product */ )

【讨论】:

使用这种技术,对源产品的任何更改都会在中介 livedata 中自动触发?因此我只需要观察产品还是我需要同时观察产品和中介的实时数据? @Kolaaa 是正确的 - 你只需要观察中介 livedata【参考方案3】:

由于 Room 不支持 MutableLiveData 并且仅支持 LiveData,因此您创建包装器的方法是我能想到的最佳方法。 Google 支持 MutableLiveData 会很复杂,因为 setValuepostValue 方法是 public。至于LiveData,它们是protected,这提供了更多控制权。

【讨论】:

【参考方案4】:

LiveData 转换为MutableLiveData 的Kotlin 扩展:

/**
 * Transforms a [LiveData] into [MutableLiveData]
 *
 * @param T type
 * @return [MutableLiveData] emitting the same values
 */
fun <T> LiveData<T>.toMutableLiveData(): MutableLiveData<T> 
    val mediatorLiveData = MediatorLiveData<T>()
    mediatorLiveData.addSource(this) 
        mediatorLiveData.value = it
    
    return mediatorLiveData

【讨论】:

【参考方案5】:

在您的存储库中,您可以获得LiveData 并将其转换为MutableLivedata

var data= dao.getAsLiveData()
return MutableLiveData<T>(data.value)

【讨论】:

这是错误的方式,目前没有人保证livedata中有数据。 如果我们之前检查 null 怎么办?

以上是关于将 LiveData 转换为 MutableLiveData的主要内容,如果未能解决你的问题,请参考以下文章

未解决的参考:asLiveData 同时将 Flow 转换为 LiveData

Activity 无法转换为 LifecycleOwner

如何处理以前的请求并停止观察以前的 LiveData

将多个 LiveData 值映射为一个

Google 宣布废弃 LiveData.observe 方法

Google 宣布废弃 LiveData.observe 方法