android -MutableLiveData не наблюдает за новыми данными

Я использую компонент архитектуры mvvm и android, я новичок в этой архитектуре.

в своем приложении я получаю данные из веб-службы и показываю их в recycleView, все работает нормально.

затем у меня есть кнопка для добавления новых данных, когда пользователь вводит данные, они переходят в веб-службу, затем мне нужно получить данные и снова обновить свой адаптер.

это мой код в действии:

 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 из активности, и он переходит в модель и отправляет ее в мою веб-службу, после того, как она была успешной, я вызываю метод getCatsOnline, чтобы снова получить данные из веб-службы.

когда я отлаживал, он получает данные, но не уведомляет о моей активности, я имею в виду, что наблюдатель не запускается в моей деятельности.

Как я могу это исправить ? что не так с моим кодом?


person Navid Abutorab    schedule 01.05.2019    source источник


Ответы (1)


Вы сделали несколько разных ошибок разной важности при использовании LiveData и RxJava, а также при разработке самого MVVM.


LiveData и RxJava

Обратите внимание, что LiveData и RxJava - это потоки. Они не используются один раз, поэтому вам нужно наблюдать за одним и тем же объектом LiveData и, что более важно, тот же объект LiveData необходимо обновить.

Если вы посмотрите на метод getCatsOnline(), каждый раз, когда метод вызывается, он создает совершенно новый экземпляр LiveData. Этот экземпляр отличается от предыдущего объекта LiveData, поэтому все, что слушает предыдущий объект LiveData, не получит уведомления о новом изменении.

И несколько дополнительных советов:

  • В getCatsOnline() вы подписываетесь на Observable внутри другого подписчика. Это частая ошибка новичков, которые воспринимают RxJava как обратный звонок. Это не обратный звонок, и вам нужно связать эти звонки.

  • Не subscribe на уровне Модель, потому что это прерывает поток, и вы не можете сказать, когда отказаться от подписки.

  • Нет смысла использовать AndroidSchedulers.mainThread(). Нет необходимости переключаться на основной поток на уровне Model, тем более что LiveData наблюдатели работают только в основном потоке.

  • Не подвергайте MutableLiveData другому слою. Просто вернитесь как LiveData.

И последнее, что я хочу отметить, это то, что вы используете RxJava и LiveData вместе. Поскольку вы новичок в обоих, я рекомендую вам придерживаться только одного из них. Если вам необходимо использовать оба, используйте LiveDataReactiveStreams для объединения этих двух правильно.


Дизайн

Как все это исправить? Я предполагаю, что вы пытаетесь:

(1) просмотр категории потребностей -> (2) получение категорий с сервера -> (3) создание / обновление наблюдаемого объекта list с новыми кошками и независимое сохранение результата в БД -> (4) экземпляр list должен уведомлять об активности автоматически.

Это сложно сделать правильно, потому что у вас есть этот list экземпляр, который вам нужно вручную создать и обновить. Вам также нужно позаботиться о том, где и как долго хранить этот list экземпляр.

Лучшим дизайном было бы:

(1) просмотр категории потребностей -> (2) получение LiveData из БД и наблюдение -> (3) получение новых категорий с сервера и обновление БД с ответом сервера -> (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())
        )
    }
}

Я рекомендую вам прочитать Руководство по архитектуре приложений и один из этих примеров liveata-mvvm. app от Google.

person Sanlok Lee    schedule 01.05.2019