android -MutableLiveData无法在新数据上观察

时间:2019-05-01 07:41:18

标签: android android-architecture-components android-livedata android-mvvm

我正在使用mvvm和android体系结构组件,我是该体系结构的新手。

在我的应用程序中,我从Web服务中获取了一些数据,并在recycleView中显示了它们,效果很好。

然后有一个用于添加新数据的按钮,当用户输入数据时,它将进入Web服务,然后我必须获取数据并再次更新适配器。

这是我的活动代码:

 private fun getUserCats() {
    vm.getCats().observe(this, Observer {
        if(it!=null) {
            rc_cats.visibility= View.VISIBLE
            pb.visibility=View.GONE
            catAdapter.reloadData(it)

        }
    })
}

这是视图模型:

class CategoryViewModel(private val model:CategoryModel): ViewModel() {

private lateinit var catsLiveData:MutableLiveData<MutableList<Cat>>

fun getCats():MutableLiveData<MutableList<Cat>>{
    if(!::catsLiveData.isInitialized){
        catsLiveData=model.getCats()
    }
    return catsLiveData;
}

fun addCat(catName:String){
    model.addCat(catName)
}

}

这是我的模型课:

class CategoryModel(
    private val netManager: NetManager,
    private val sharedPrefManager: SharedPrefManager) {

private lateinit var categoryDao: CategoryDao
private lateinit var dbConnection: DbConnection
private lateinit var lastUpdate: LastUpdate

fun getCats(): MutableLiveData<MutableList<Cat>> {
    dbConnection = DbConnection.getInstance(MyApp.INSTANCE)!!
    categoryDao = dbConnection.CategoryDao()
    lastUpdate = LastUpdate(MyApp.INSTANCE)

    if (netManager.isConnected!!) {
        return getCatsOnline();
    } else {
        return getCatsOffline();
    }
}

fun addCat(catName: String) {
    val Category = ApiConnection.client.create(Category::class.java)
    Category.newCategory(catName, sharedPrefManager.getUid())
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                    { success ->
                        getCatsOnline()
                    }, { error ->
                Log.v("this", "ErrorNewCat " + error.localizedMessage)
            }
            )
}

private fun getCatsOnline(): MutableLiveData<MutableList<Cat>> {
    Log.v("this", "online ");
    var list: MutableLiveData<MutableList<Cat>> = MutableLiveData()
    list = getCatsOffline()

    val getCats = ApiConnection.client.create(Category::class.java)
    getCats.getCats(sharedPrefManager.getUid(), lastUpdate.getLastCatDate())
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                    { success ->
                        list += success.cats
                        lastUpdate.setLastCatDate()

                        Observable.just(DbConnection)
                                .subscribeOn(Schedulers.io())
                                .subscribe({ db ->
                                    categoryDao.insert(success.cats)
                                })

                    }, { error ->
                Log.v("this", "ErrorGetCats " + error.localizedMessage);
            }
            )

    return list;
}

我从活动中调用getCat,它将进入模型并将其发送到我的Web服务,成功后,我将调用getCatsOnline方法以再次从webservice中获取数据。

在我调试时,它获取数据,但不通知我的活动,这意味着观察者未在我的活动中触发。

我该如何解决?我的代码有什么问题?

1 个答案:

答案 0 :(得分:0)

您在LiveDataRxJava的使用以及MVVM设计本身上犯了几个重要性不同的错误。


LiveData和RxJava

请注意,LiveDataRxJava是流。它们不是一次性使用的,因此您需要观察相同的LiveData对象,更重要的是,需要更新相同的LiveData对象。

如果您查看getCatsOnline()方法,则每次调用该方法时,它都会创建一个全新的LiveData实例。该实例与先前的LiveData对象不同,因此侦听先前的LiveData对象的任何内容都不会收到新更改的通知。

一些其他提示:

  • getCatsOnline()中,您正在另一订户内部预订Observable。这是将RxJava视为回叫的初学者的常见错误。这不是回叫,您需要链接这些呼叫。

  • 请勿在 Model 层中subscribe,因为它会中断流,并且您无法确定何时取消订阅。

  • 永远不要使用AndroidSchedulers.mainThread()。无需切换到 Model 层中的主线程,尤其是因为LiveData观察者仅在主线程上运行。

  • 请勿将MutableLiveData暴露给其他层。只需返回LiveData

我要指出的最后一件事是您同时使用RxJavaLiveData。由于您都不熟悉这两者,因此我建议您只使用其中之一。如果您必须同时使用两者,请使用LiveDataReactiveStreams来正确地将两者桥接。


设计

如何解决所有这些问题?我猜您正在尝试执行以下操作:

(1)视图需要类别->(2)从服务器获取类别->(3)使用新的目录创建/更新可观察的list对象,并将结果独立保存在数据库中->( 4)list实例应自动通知活动。

很难正确实现这一点,因为您必须手动创建和更新此list实例。您还需要担心list实例的保留时间和保留时间。

更好的设计是:

(1)视图需要类别->(2)从数据库获取LiveData并观察->(3)从服务器获取新类别并使用服务器响应来更新DB->(4)视图为自动通知,因为它一直在观察数据库!

这很容易实现,因为它具有以下一种依赖性:视图->数据库->服务器

示例CategoryModel:

class CategoryModel(
    private val netManager: NetManager,
    private val sharedPrefManager: SharedPrefManager) {

    private val categoryDao: CategoryDao
    private val dbConnection: DbConnection
    private var lastUpdate: LastUpdate // Maybe store this value in more persistent place..


    fun getInstance(netManager: NetManager, sharedPrefManager: SharedPrefManager) {
        // ... singleton
    }


    fun getCats(): Observable<List<Cat>> {
        return getCatsOffline();
    }

    // Notice this method returns just Completable. Any new data should be observed through `getCats()` method.
    fun refreshCats(): Completable {
        val getCats = ApiConnection.client.create(Category::class.java)

        // getCats method may return a Single
        return getCats.getCats(sharedPrefManager.getUid(), lastUpdate.getLastCatDate())
            .flatMap { success -> categoryDao.insert(success.cats) } // insert to db
            .doOnSuccess { lastUpdate.setLastCatDate() }
            .ignoreElement()
            .subscribeOn(Schedulers.io())
    }


    fun addCat(catName: String): Completable {
         val Category = ApiConnection.client.create(Category::class.java)

         // newCategory may return a Single
         return Category.newCategory(catName, sharedPrefManager.getUid())
             .ignoreElement()
             .andThen(refreshCats())
             .subscribeOn(Schedulers.io())
        )
    }
}

我建议您通读Google的Guide to App Architecture和其中一个livedata-mvvm示例app