android - 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)
}
我应该怎么办 ?
解决方案
您在 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")
}
推荐阅读
- c++ - 将 UTF-8/16 写入文件,codecvt_utf16 被弃用后的替代方法是什么?
- c# - 如何检查一个元素是否存在以不将其复制到 C# 的控件集合中?
- php - 为什么 400030 没有给出大于 400000 的逻辑?
- python - PyCharm 终端无法在 Raspbian 操作系统上运行
- java - 在Java中分别显示阿拉伯/波斯字母/字符?
- ios - 在Watch App上勾选“Supports Running without iOS App Installation”会禁用WCSession运行
- php - Laravel - 如何在刀片中使用 foreach 循环合并重复行值?
- python - Python电报机器人ReplyKeyboardMarkup
- algorithm - 如何改进此代码以查找数组的 k 个最大元素?
- c - EXT2:了解 inode 位图