简体   繁体   English

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

[英]How do i use rxjava to make a retrofit request in a repository and pass it to a ViewModel using LiveData?

I'm trying to make an Android app that uses Google's architecture components. 我正在尝试制作一个使用Google架构组件的Android应用。 I'm making a request to the TasteDive API using retrofit and rxjava in a Repository class. 我正在使用Repository类中的改造和rxjava向TasteDive API发出请求。 The problem that I'm facing is that I can't find a way to pass the outcome of the retrofit request to the ViewModel using LiveData. 我面临的问题是我找不到使用LiveData将改造请求的结果传递给ViewModel的方法。 What I'm currently doing is I have a sealed class called Outcome that keeps track of the state of the request and in this sealed class i have a data class that holds the data of a successful request. 我目前正在做的是,我有一个称为Outcome的密封类,用于跟踪请求的状态,在这个密封类中,我有一个数据类,用于保存成功请求的数据。 However, since rxjava's observable is a callback, I can't figure out a way to assign the outcome of the request to LiveData and pass it to the ViewModel. 但是,由于rxjava的observable是回调,所以我无法找到一种将请求结果分配给LiveData并将其传递给ViewModel的方法。 When I try to assign the outcome to LiveData, it returns null, unsurprisingly, since the observable is a callback. 当我尝试将结果分配给LiveData时,由于可以观察到的是回调,因此毫不奇怪,它会返回null。 Can any of you help me figure out a way to store the outcome of the retrofit request into LiveData in order to pass it to the ViewModel? 你们中的任何人都可以帮助我找出一种方法来将改装请求的结果存储到LiveData中,以便将其传递给ViewModel吗? I have looked all over the internet for a solution to this and have not found anything helpful. 我已经在整个互联网上寻找了解决方案,但没有发现任何帮助。 Here's my repository class: 这是我的存储库类:

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
    }
}

The request is working, because mResultsList is giving me the correct results, but LiveData is returning null. 该请求有效,因为mResultsList给了我正确的结果,但LiveData返回null。

Here is the sealed class 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)
    }
}

Thanks for your time. 谢谢你的时间。

The issue is with the need for a Lifecycle Owner used for observations of LiveData in the Repository. 问题在于需要使用生命周期所有者来观察存储库中的LiveData。

First, you don't want to do all networking actions inside the ViewModel. 首先,您不想在ViewModel中执行所有联网操作。 I think you have the right idea with the Repository , but you have to remember that the Repository will have to communicate only with the ViewModel . 我认为您对Repository有一个正确的想法,但是您必须记住,存储库仅必须与ViewModel进行通信。 Best case, you would want the function getSimilarData(...) do some something like this: 最好的情况是,您希望函数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(...)
    }
}

However, you will have the issue of observing the status from the ViewModel as it itself is not a Lifecycle implementation, so it cannot easily observe the LiveData from the Repository. 但是,您将遇到从ViewModel观察状态的问题,因为它本身不是Lifecycle实现,因此它无法轻松地从Repository观察LiveData。

My suggestion would be something like this: 我的建议是这样的:

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
}

Note that you will have to dispose the subscription in the ViewModel onCleared. 请注意,您将必须在ViewModel onCleared中处置订阅。
Note that all of this is pseudo code and it should be done a lot cleaner than this. 请注意,所有这些都是伪代码,应该比这更干净。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 ViewModel 和 LiveData 进行改造 API 调用 - How to make retrofit API call using ViewModel and LiveData ViewModel中的LiveData如何使用转换观察存储库中的Livedata? - How can LiveData in ViewModel observe the Livedata in Repository using Transformations? 使用 LiveData 和 Retrofit 设置 ViewModel 的正确方法 - Proper way to setup ViewModel with LiveData and Repository with Retrofit 如何使用 viewmodel + livedata 进行单元测试改造 api 调用? - How to do unit testing retrofit api calls with viewmodel + livedata? 如何将WearableActivity与LiveData和ViewModel结合使用 - How can I use WearableActivity with LiveData and ViewModel 如何使用 Retrofit 向 traccar API 发出请求? - how do i make a request to the traccar API using Retrofit? 如何使用Retrofit和RxJava / RxAndroid处理响应错误? - How do I handle response errors using with Retrofit and RxJava/RxAndroid? 如何使用 viewmodel、repository 和 livedata 访问 room db - How to access room db using viewmodel, repository, and livedata ViewModel,Fragment,liveData,改造 - ViewModel , Fragment , liveData , Retrofit 如何使用 viewmodel 和 livedata 将数据从 recyclerview 传递到 android 中的片段 - How can i pass the data from recyclerview to fragment in android using viewmodel and livedata
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM