android - Rx Java - 仅创建视图层次结构的原始线程
问题描述
我对 Rx Java 很陌生,我已经看到这个问题在 SO 上回答了很多次,但是给出的答案似乎对我不起作用。
所以这是我的堆栈跟踪:
2018-12-04 10:51:03.072 17083-17227/com.doolin.gary.mybooks E/AndroidRuntime: FATAL EXCEPTION: RxCachedThreadScheduler-2
Process: com.test.test.mybooks, PID: 17083
io.reactivex.exceptions.OnErrorNotImplementedException: Only the original thread that created a view hierarchy can touch its views.
at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:704)
at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:701)
at io.reactivex.internal.observers.LambdaObserver.onError(LambdaObserver.java:77)
at io.reactivex.internal.observers.LambdaObserver.onNext(LambdaObserver.java:67)
at io.reactivex.internal.operators.observable.ObservableHide$HideDisposable.onNext(ObservableHide.java:67)
at io.reactivex.internal.util.NotificationLite.accept(NotificationLite.java:246)
at io.reactivex.subjects.BehaviorSubject$BehaviorDisposable.test(BehaviorSubject.java:570)
at io.reactivex.subjects.BehaviorSubject$BehaviorDisposable.emitNext(BehaviorSubject.java:565)
at io.reactivex.subjects.BehaviorSubject.onNext(BehaviorSubject.java:268)
at com.test.test.mybooks.viewmodel.BookSearchViewModel$getBookList$1.accept(BookSearchViewModel.kt:35)
at com.test.test.mybooks.viewmodel.BookSearchViewModel$getBookList$1.accept(BookSearchViewModel.kt:19)
at io.reactivex.internal.observers.DisposableLambdaObserver.onSubscribe(DisposableLambdaObserver.java:42)
at retrofit2.adapter.rxjava2.BodyObservable$BodyObserver.onSubscribe(BodyObservable.java:46)
at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:37)
at io.reactivex.Observable.subscribe(Observable.java:12030)
at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
at io.reactivex.Observable.subscribe(Observable.java:12030)
at io.reactivex.internal.operators.observable.ObservableDoOnLifecycle.subscribeActual(ObservableDoOnLifecycle.java:33)
at io.reactivex.Observable.subscribe(Observable.java:12030)
at io.reactivex.internal.operators.observable.ObservableObserveOn.subscribeActual(ObservableObserveOn.java:45)
at io.reactivex.Observable.subscribe(Observable.java:12030)
at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:579)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7753)
at android.view.ViewRootImpl.focusableViewAvailable(ViewRootImpl.java:3773)
at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:917)
at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:917)
at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:917)
at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:917)
at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:917)
at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:917)
at android.view.ViewGroup.focusableViewAvailable(ViewGroup.java:917)
at android.view.View.setFlags(View.java:14096)
at android.view.View.setVisibility(View.java:9992)
at com.test.test.mybooks.view.search.SearchFragment$observeScreenState$1.accept(SearchFragment.kt:91)
at com.test.test.mybooks.view.search.SearchFragment$observeScreenState$1.accept(SearchFragment.kt:28)
at io.reactivex.internal.observers.LambdaObserver.onNext(LambdaObserver.java:63)
2018-12-04 10:51:03.072 17083-17227/com.test.test.mybooks E/AndroidRuntime: ... 26 more
我有一个通过我的视图模型检索的书籍列表:
class BookSearchViewModel @Inject constructor(private val app: Application,
private val bookRepository: BookRepository,
@Named(RxModule.IO_THREAD) private val ioThread: Scheduler,
@Named(RxModule.MAIN_THREAD) private val mainThread: Scheduler):
AndroidViewModel(app) {
private var disposables = CompositeDisposable()
private val screenState = BehaviorSubject.create<ScreenState>()
private val bookList = PublishSubject.create<List<BookSummary>>()
fun observeBookList(): Observable<List<BookSummary>> = bookList.hide()
fun observeScreenState(): Observable<ScreenState> = screenState.hide()
fun getBookList() {
disposables.add(bookRepository
.fetchBookList()
.doOnSubscribe { screenState.onNext(ScreenState.Loading("")) }
.observeOn(mainThread)
.subscribeOn(ioThread)
.subscribe(this::handleBookListSuccess, this::handleBookListError))
}
override fun onCleared() {
disposables.dispose()
super.onCleared()
}
private fun handleBookListSuccess(bookList: List<BookSummary>) {
screenState.onNext(ScreenState.Success)
this.bookList.onNext(bookList)
}
private fun handleBookListError(error: Throwable) {
screenState.onNext(ScreenState.Error(getErrorMessage(error)))
}
private fun getErrorMessage(error: Throwable): String {
return if (error.message.isNullOrEmpty()) {
app.getString(R.string.book_list_error)
} else {
error.message!!
}
}
}
我在我的片段中观察到的效果非常好,如下所示:
private fun addUIObservers() {
disposables.add(viewModel.observeBookList()
.subscribe {
bookAdapter.loadBooks(it)
})
}
private fun observeScreenState() {
disposables.add (viewModel
.observeScreenState()
.subscribe { screenState ->
when (screenState) {
is ScreenState.Success -> progress_layout.visibility = GONE
is ScreenState.Loading -> progress_layout.visibility = VISIBLE
is ScreenState.Error -> handleError(screenState)
}
})
}
因此,当用户单击书籍项目时,我会打开书籍详细信息活动,并通过它的视图模型使用相同的模式检索书籍,使用 rx 并在活动中观察。
当我回击并返回上一个活动/片段时,如果我拉刷新,我会收到上述错误。这仅在从详细信息活动返回时发生。正如我所说,我对 RX 很陌生,所以请放轻松,但如果有人有任何提示。请不要将此标记为已回答,因为这些答案对我不起作用,我正在观察和订阅正确的线程。
解决方案
只需在.observeOn(AndroidSchedulers.mainThread())
之后添加.observeScreenState()
(先用 导入import rx.android.schedulers.AndroidSchedulers;
)
所有 UI 逻辑都必须在主线程上执行。因此,如果您有任何与 UI 相关的内容,则必须在其之前subscribe
添加。.observeOn(AndroidSchedulers.mainThread())
可能还有另一种情况,特别是当您在自定义可观察对象中调用 UI 逻辑或使用RxBinding
执行此操作的框架时,您需要添加.subscribeOn(AndroidSchedulers.mainThread())
. 您的“拉动刷新”功能可能就是这种情况。
推荐阅读
- java - 如何测试受保护的 Spring Controller
- javascript - setInterval 求和随机量
- azure-storage - 访问 Azure 存储的随机 ECONNREFUSED 错误
- html - 响应式设计:有没有办法自动将我的网站放大和缩小到高度而不是窗口的宽度?
- vue.js - 错误“无法读取未定义的属性 'indexOf'”绑定 n-api 模块
- c# - 为什么我总是有无效的视图模型?
- ios - 如何从纯 Swift 项目访问纯 Swift 静态库?
- python - 如何在 python 中将我的结果写入 .csv 文件的两行?
- r - Rstudio中的SIR模型闪亮
- angularjs - 将函数注入 angularjs 模块的最简单方法是什么?