首页 > 解决方案 > 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 很陌生,所以请放轻松,但如果有人有任何提示。请不要将此标记为已回答,因为这些答案对我不起作用,我正在观察和订阅正确的线程。

标签: androidmvvmkotlinrx-java2android-viewmodel

解决方案


只需在.observeOn(AndroidSchedulers.mainThread())之后添加.observeScreenState()(先用 导入import rx.android.schedulers.AndroidSchedulers;

所有 UI 逻辑都必须在主线程上执行。因此,如果您有任何与 UI 相关的内容,则必须在其之前subscribe添加。.observeOn(AndroidSchedulers.mainThread())

可能还有另一种情况,特别是当您在自定义可观察对象中调用 UI 逻辑或使用RxBinding执行此操作的框架时,您需要添加.subscribeOn(AndroidSchedulers.mainThread()). 您的“拉动刷新”功能可能就是这种情况。


推荐阅读