【问题标题】:problem with use Mvvm and Retrofit and Rxjava live data in kotlin在 kotlin 中使用 Mvvm 和 Retrofit 和 Rxjava 实时数据的问题
【发布时间】:2020-05-24 12:12:29
【问题描述】:

我是 kotlin 的新手,我正在尝试使用 Rxjava 和 MVVM 架构中的实时数据进行改造。 我配置改造,并在 ViewModel 中使用 observable 和 subscribe 来制作 observable 变量以在活动绑定布局中使用。 我的视图中有一个按钮,当我单击它时,请求方法开始启动并订阅写入自己的数据日志。但是我的变量一开始和几秒钟后变为空,当改造完成其任务时,我的变量获取数据但日志值不更新。

这是我的改造初始化类

class ApiService {

private val INSTANCE =
    Retrofit.Builder()
        .baseUrl("http://www.janbarar.ir/App/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()
        .create(IRetrofitMethods::class.java)


private fun <T> callBack(iDataTransfer: IDataTransfer<T>) =

    object : Callback<T> {

        override fun onResponse(call: Call<T>, response: Response<T>) {

            val data = response.body()
            if (data != null)
                iDataTransfer.onSuccess(data)
            else
                try {
                    throw Exception("data is empty")
                } catch (ex: Exception) {
                    iDataTransfer.onError(ex)
                }
        }

        override fun onFailure(call: Call<T>, t: Throwable) {
            iDataTransfer.onError(t)
        }
    }

fun getCategories(iDataTransfer: IDataTransfer<List<Category>>) =
        INSTANCE.getCategories().enqueue(callBack(iDataTransfer))

这是一个改造界面

@GET("GetCategories")
fun getCategories(): Call<List<Category>>

这是我的模型课。我认为问题就在这里。因为 observable 在改造完成之前发送空数据。

fun getCategories(): Observable<ArrayList<Category>> {

        val result = arrayListOf<Category>()

        api.getCategories(object : IDataTransfer<List<Category>> {

            override fun onSuccess(data: List<Category>) {

                result.addAll(data)
            }

            override fun onError(throwable: Throwable) {
                Log.e("Model", throwable.message!!)
            }
        })

        return Observable.just(result)
    }

这也是我的 ViewModel 类

class ProductViewModel(private val model: ProductModel) : ViewModel() {

    var isLoading = ObservableField(false)
    var categoryList = MutableLiveData<ArrayList<Category>>()
    private var compositeDisposable = CompositeDisposable()

    fun getCategories() {

        isLoading.set(true)
        compositeDisposable +=
            model.getCategories()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({

                    categoryList.value = it

                }, {

                    Log.e("ViewModel", it.message.toString())
                })
        isLoading.set(false)
    }

最后,这是我的活动

lateinit var binding: ActivityMainBinding
 private val vm: ProductViewModel by viewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.vm = vm

        vm.categoryList.observe(this, Observer {

            if (it != null)
                Log.e("activity", it.toString())
        })   
    }

【问题讨论】:

  • 您忘记将 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 添加到 Retrofit 中,因此它会立即为您提供 Single / Observable,而无需您像现在这样手动进行转换

标签: kotlin mvvm observable retrofit2 rx-java2


【解决方案1】:

正如 ExpensiveBelly 在评论中提到的,Retrofit 为 RxJava 提供了一个调用适配器,因此您可以让您的 API 直接返回 Observable&lt;List&lt;Category&gt;&gt;。为此,您需要将 RxJava 调用适配器依赖项添加到您的应用模块的build.gradle

implementation 'com.squareup.retrofit2:adapter-rxjava2:(version)'

在构建Retrofit 实例时添加调用适配器工厂:

private val INSTANCE =
    Retrofit.Builder()
        .baseUrl("http://www.janbarar.ir/App/")
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // add this
        .addConverterFactory(GsonConverterFactory.create())
        .build()
        .create(IRetrofitMethods::class.java)

然后让你的服务直接返回一个 Observable:

@GET("GetCategories")
fun getCategories(): Observable<List<Category>>

如果 ApiService 需要在应用程序的其余部分得到响应之前进行一些处理,您可以使用 RxJava 运算符,例如 map


看看为什么您的代码不起作用以及如何修复它,这将是说明性的。当您调用api.getCategories(someCallback) 时,您的回调方法之一将在将来的某个时间点被调用。同时,model.getCategories() 方法会立即返回。

当您订阅返回的Observable 时,它会发出result 变量,该变量当前是一个空列表。 result 最终会在其中包含一些数据,但您的代码根本不会被告知这一点。

您真正想做的是在类别列表可用时发出它。从回调 API 获取 Observable 的标准方法是使用 Observable.create

fun getCategories(): Observable<ArrayList<Category>> {

    return Observable.create { emitter -> 

        api.getCategories(object : IDataTransfer<List<Category>> {

            override fun onSuccess(data: List<Category>) {

                emitter.onNext(data)
                emitter.onComplete()
            }

            override fun onError(throwable: Throwable) {
                emitter.onError(throwable)
            }
        })
    }
}

当然,如果可能的话,最好只使用RxJava2CallAdapterFactory,因为这项工作已经在那里完成了。

【讨论】:

  • 如果他们使用call.execute() 而不是call.enqueue,那么他们可以只使用Observable.fromCallable 而不必使用Observable.create()
  • 非常感谢您的帮助。我尝试了这两种方法并想出了答案。
猜你喜欢
  • 2014-10-31
  • 2020-04-15
  • 2023-03-15
  • 2019-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多