Rx Java-仅创建视图层次结构的原始线程

时间:2018-12-04 11:14:11

标签: android mvvm kotlin rx-java2 android-viewmodel

我对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还是很陌生,所以请放轻松,但是如果有人有任何建议。请不要将其标记为已回答,因为这些答案对我不起作用,我正在观察并订阅正确的线程。

1 个答案:

答案 0 :(得分:0)

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

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

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