首页 > 解决方案 > MVVM RXAndroid 无法在后台线程上调用 setValue

问题描述

在我的应用程序中,我请求一个 json 来填充 recyclerview ,首先在 init kotlin viewmodel 中它总是成功但是当我想在 timer.schedule 方法中再次请求时,它会转到一次性订阅中的 onError 方法,这里是 Edittext 的 onTextChange绑定在 xml 视图中:

fun onSearchTextChanged(text: CharSequence) {
    if (text.isNotEmpty()) {
        if (myTimer != null) {
            myTimer!!.cancel()
        }

        myTimer = Timer()
        myTimer!!.schedule(object : TimerTask() {
            override fun run() {
                loadMovies(text.toString(), 1)
            }

        }, 1000)
    } else {
        if (myTimer != null) {
            myTimer!!.cancel()
        }
    }
}

这是我的请求方法:

fun loadMovies(searchText: String, pageNumber: Int) {
    subscription = searchApi.getSearch(API_KEY, searchText, pageNumber)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .doOnSubscribe { onRetrieveMoviesListStart() }
        .doOnTerminate { onRetrieveMoviesListFinish() }
        .subscribe(
            { result -> onRetrieveMoviesListSuccess(result) },
            { err -> onRetrieveMoviesListError(err) })
}

private fun onRetrieveMoviesListStart() {
    loadingVisibility.value = View.VISIBLE
    Timber.d("started")
}

private fun onRetrieveMoviesListFinish() {
    loadingVisibility.value = View.GONE
    Timber.d("finished")
}

private fun onRetrieveMoviesListSuccess(search: Search) {
    Timber.d(search.toString())
    movieListAdapter.updateMoviesList(search.Search)
    movieEvent.value = MovieEvent.ANIMATE_RECYCLER
}

private fun onRetrieveMoviesListError(err: Throwable) {
    Timber.e(err)
}

这是 viewModel 类声明:

@Inject
lateinit var searchApi: SearchApi

val movieListAdapter: MovieListAdapter = MovieListAdapter()
private var myTimer: Timer? = null

private val movieEvent = SingleLiveEvent<MovieEvent>()
private lateinit var subscription: Disposable
val loadingVisibility: MutableLiveData<Int> = MutableLiveData()

init {
    loadMovies("someText", 1)
}

我应该怎么办 ?

标签: androidretrofit2rx-android

解决方案


您在 ViewModel init 中对 loadMovies 的初始调用来自 main/ui 线程。timertask 对 loadMovies 的调用来自后台线程。现在我对 rx 没有太多经验,但问题似乎与 livedata 有关。

您正在主线程上观察,这对于成功和错误方法很好。您的问题似乎是从后台线程调用的 onRetrieveMoviesListStart() 和 onRetrieveMoviesListFinish() 。

livedata 值的设置器只能从主线程调用,要从后台线程设置值,您需要调用postValue

像这样更改您的两个方法/功能应该可以解决问题:

private fun onRetrieveMoviesListStart() { 
    loadingVisibility.postValue(View.VISIBLE)
    Timber.d("started")
} 
private fun onRetrieveMoviesListFinish() {
    loadingVisibility.postValue(View.GONE)
    Timber.d("finished")
}

推荐阅读