如何使用rxjava在存储库中提出改造请求,并使用LiveData将其传递给ViewModel?

时间:2018-09-19 15:32:10

标签: android kotlin rx-java android-livedata android-viewmodel

我正在尝试制作一个使用Google架构组件的Android应用。我正在使用Repository类中的改造和rxjava向TasteDive API发出请求。我面临的问题是我找不到使用LiveData将改造请求的结果传递给ViewModel的方法。我目前正在做的是,我有一个称为Outcome的密封类,用于跟踪请求的状态,在这个密封类中,我有一个数据类,用于保存成功请求的数据。但是,由于rxjava的observable是回调,所以我无法找到一种将请求结果分配给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返回null。

这是密封类的结果:

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 :(得分:0)

问题在于需要使用生命周期所有者来观察存储库中的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观察状态的问题,因为它本身不是Lifecycle实现,因此它无法轻松地从存储库观察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中处置订阅。
请注意,所有这些都是伪代码,应该比这更清洁。