我对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还是很陌生,所以请放轻松,但是如果有人有任何建议。请不要将其标记为已回答,因为这些答案对我不起作用,我正在观察并订阅正确的线程。
答案 0 :(得分:0)
只需在.observeOn(AndroidSchedulers.mainThread())
之后添加.observeScreenState()
(首先用import rx.android.schedulers.AndroidSchedulers;
导入)
所有UI逻辑必须在主线程上执行。因此,如果subscribe
中有与UI相关的任何内容,则必须在其前面添加.observeOn(AndroidSchedulers.mainThread())
。
可能还有另外一种情况,特别是当您在自定义可观察对象中调用UI逻辑或使用执行此操作的RxBinding
框架时,则需要添加.subscribeOn(AndroidSchedulers.mainThread())
。您的“拉动刷新”功能可能就是这种情况。