Как использовать rxjava, чтобы сделать запрос на модификацию в хранилище и передать его в ViewModel с помощью LiveData? - PullRequest
0 голосов
/ 19 сентября 2018

Я пытаюсь создать приложение для Android, которое использует компоненты архитектуры Google.Я делаю запрос к API TasteDive, используя retrofit и rxjava в классе репозитория.Проблема, с которой я сталкиваюсь, заключается в том, что я не могу найти способ передать результат запроса на модификацию в ViewModel с помощью LiveData.В настоящее время я занимаюсь запечатанным классом Outcome, который отслеживает состояние запроса, и в этом запечатанном классе у меня есть класс данных, в котором хранятся данные успешного запроса.Тем не менее, так как наблюдаемый rxjava является обратным вызовом, я не могу найти способ присвоить результат запроса LiveData и передать его ViewModel.Когда я пытаюсь присвоить результат LiveData, он возвращает null, что неудивительно, поскольку наблюдаемое является обратным вызовом.Может ли кто-нибудь из вас помочь мне найти способ сохранить результат запроса на модификацию в LiveData, чтобы передать его в ViewModel?Я искал по всему интернету решение этой проблемы и не нашел ничего полезного.Вот мой класс репозитория:

class GetSimilarDataRepository {
    private var mAdapter: TasteDiveAdapter? = null
    private lateinit var mResultsList: ArrayList<Result>

    private var observable: Observable<Response<TasteDive>>? = null

    private var liveData = MutableLiveData<Outcome<List<Result>>>()

    fun getSimilarData(map: LinkedHashMap<String, String>): LiveData<Outcome<List<Result>>> {
        mAdapter?.clear()

        val builder = Retrofit.Builder()
                .baseUrl("https://www.tastedive.com/api/")
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())

        val retrofit = builder.build()


        val client = retrofit.create(TasteDiveClient::class.java)

        observable = client.getSimilarData(map)

        observable?.filter { it.code() == 200 }
                ?.map { Observable.just(it.body()) }
                ?.subscribeOn(Schedulers.io())
                ?.observeOn(AndroidSchedulers.mainThread())
                ?.doOnNext {
                    liveData.value = Outcome.loading(true)
                }?.doOnError {
                    liveData.value = Outcome.failure(it)
                }?.subscribeBy (
                        onError = {
                            liveData.value = Outcome.failure(it)
                        },

                        onNext = {
                            it.subscribe {
                                var similar = it?.Similar
                                var results = similar?.Results
                                if(results!!.isEmpty()) {
                                    liveData.value = Outcome.failure(Throwable("No results for that request"))
                                } else {
                                    mResultsList = ArrayList(results)
                                    liveData.value = Outcome.success(mResultsList)
                                }
                            }
                        },

                        onComplete = { Log.v("onComplete", "onComplete")}
                )

        observable?.filter{ it.code() == 403 }
                ?.map { Observable.just(it.body()) }
                ?.subscribeBy(
                        onNext = {
                            liveData.value = Outcome.failure(Throwable("403 Response Code"))
                        },
                        onError = { Log.v("onError403", "onError403") },
                        onComplete = { Log.v("onComplete403", "onComplete403") }
                )

        observable?.filter{ it.code() == 404 }
                ?.map { Observable.just(it.body()) }
                ?.subscribeBy(
                        onNext = {
                            liveData.value = Outcome.failure(Throwable("404 Response Code"))
                        },
                        onError = { Log.v("onError404", "onError404") },
                        onComplete = { Log.v("onComplete404", "onComplete404") }
                )

        observable?.filter{ it.code() == 400 }
                ?.map { Observable.just(it.body()) }
                ?.subscribeBy(
                        onNext = {
                            liveData.value = Outcome.failure(Throwable("400 Response Code"))
                        },
                        onError = { Log.v("onError400", "onError400") },
                        onComplete = { Log.v("onComplete400", "onComplete400") }
                )

        observable?.filter{ it.code() == 500 }
                ?.map { Observable.just(it.body()) }
                ?.subscribeBy(
                        onNext = {
                            liveData.value = Outcome.failure(Throwable("500 Response Code"))
                        },
                        onError = { Log.v("onError500", "onError500") },
                        onComplete = { Log.v("onComplete500", "onComplete500") }
                )
        return liveData
    }
}

Запрос работает, потому что mResultsList дает мне правильные результаты, но LiveData возвращает ноль.

Вот запечатанный класс Outcome:

sealed class Outcome<T> {
    data class Progress<T>(var loading: Boolean) : Outcome<T>()
    data class Success<T>(var data: T) : Outcome<T>()
    data class Failure<T>(val e: Throwable) : Outcome<T>()

    companion object {
        fun <T> loading(isLoading: Boolean): Outcome<T> = Progress(isLoading)

        fun <T> success(data: T): Outcome<T> = Success(data)

        fun <T> failure(e: Throwable): Outcome<T> = Failure(e)
    }
}

Спасибо за ваше время.

1 Ответ

0 голосов
/ 18 января 2019

Проблема связана с необходимостью использования владельца жизненного цикла для наблюдений за LiveData в репозитории.

Во-первых, вы не хотите выполнять все сетевые действия внутри ViewModel.Я думаю, что у вас есть правильная идея с Repository, но вы должны помнить, что хранилище должно будет общаться только с ViewModel.В лучшем случае вы бы хотели, чтобы функция getSimilarData(...) выполняла что-то вроде этого:

Repository{
    val repositoryItems = BehaviorSubject.create<Outcome<List<Result>>>()

    fun observeRepositoryItems(): Observable<Outcome<List<Result>>> {
        return repositoryItems 
    }

    fun getSimilarData(map: LinkedHashMap<String, String>){
        // Pseudo code for actually items

        // Result goes into repositoryItems.onNext(...)
    }
}

Однако у вас возникнет проблема наблюдения за состоянием из ViewModel, поскольку она сама не является реализацией жизненного цикла, поэтомуон не может легко наблюдать LiveData из репозитория.

Мое предложение будет выглядеть примерно так:

Repository{
    val repositoryItems = BehaviorSubject.create<Outcome<List<Result>>>()

    fun observeRepositoryItems(): Observable<Outcome<List<Result>>> {
        return repositoryItems 
    }

    fun getSimilarData(map: LinkedHashMap<String, String>){
        // Pseudo code for actually items

        // Result goes into repositoryItems.onNext(...)
    }
}

ViewModel{
    val items: MutableLiveData<Outcome<List<Result>>>

    init{
        repository.observeRepositoryItems()
            .subscribe( items -> items.postValue(items ))
    }

    fun getData(){
        repository.getSimilarData(someMap)
    }
}

Fragment{
    viewModel.items.observe() // <-- Here you observe items loaded
}

Обратите внимание, что вам придется избавиться от подписки в ViewModel onCleared.
Обратите внимание, что все это псевдокод, и это должно быть сделано намного чище, чем это.

...