将 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<List<T>>
公开一个对象
@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<List<T>>
从 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
会很复杂,因为 setValue
和 postValue
方法是 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