RxJava:即使在主线程上订阅后也得到 "Only the original thread that created a view hierarchy can touch its views"

RxJava: getting "Only the original thread that created a view hierarchy can touch its views" even after subscribing on main thread

我在自动完成文本视图上使用扩展函数来使用去抖动策略并在主线程上订阅。

 binding.autoCompleteTextView2.addRxTextWatcher()
         .observeOn(AndroidSchedulers.mainThread())
         .subscribeOn(AndroidSchedulers.mainThread())
         .debounce(400, TimeUnit.MILLISECONDS)
         .subscribe {
            if (!TextUtils.isEmpty(it)) {
                        viewModel.searchTeacher(viewModel.meditationDetails?.schoolId,it)
                        binding?.etEmailOfTeacher?.visible()   //Crash at this point
                        binding?.tvEmailOfYourTeacher?.visible()
                }
       }

扩展函数:-

fun EditText.addRxTextWatcher(): Observable<String?> {
val flowable = Observable.create<String?> {
    addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {
        }

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            it.onNext(s?.toString())
        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        }
    })
}

我在尝试更新 "Only the original thread that created a view hierarchy can touch its views" 的视图可见性时遇到崩溃。但我已经订阅并观察了 mainThread 。我在这里做错了什么?

完整日志:

2019-09-27 11:51:06.878 9490-9575/com.example.example E/ACRA: ACRA caught a CalledFromWrongThreadException for com.example.example
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7959)
        at android.view.ViewRootImpl.focusableViewAvailable(ViewRootImpl.java:3866)
        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.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:14121)
        at android.view.View.setVisibility(View.java:10007)
        at in.eightfolds.utils.ExtentionFunctionsKt.visible(ExtentionFunctions.kt:345)
        at com.example.example.fragment.OnBordingCreateMeditationAdditionalDetailsFragment$setAutoCompleteTextView2.accept(OnBordingCreateMeditationAdditionalDetailsFragment.kt:283)
        at com.example.example.fragment.OnBordingCreateMeditationAdditionalDetailsFragment$setAutoCompleteTextView2.accept(OnBordingCreateMeditationAdditionalDetailsFragment.kt:38)
        at io.reactivex.internal.observers.LambdaObserver.onNext(LambdaObserver.java:59)
        at io.reactivex.observers.SerializedObserver.onNext(SerializedObserver.java:111)
        at io.reactivex.internal.operators.observable.ObservableDebounceTimed$DebounceTimedObserver.emit(ObservableDebounceTimed.java:142)
        at io.reactivex.internal.operators.observable.ObservableDebounceTimed$DebounceEmitter.run(ObservableDebounceTimed.java:167)
        at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:59)
        at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:51)
        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)

如果我在 activity?.runOnUiThread { } 中写入可见性,那么它工作正常。那么为什么即使我在 mainthread 上订阅了也会出现异常?

debounce 运算符将订阅计算调度程序。像这样重新排序您的函数:

binding.autoCompleteTextView2.addRxTextWatcher() <-- text watch is on the main thread
         .debounce(400, TimeUnit.MILLISECONDS) <-- debounced throw the result on Computation Scheduler
         .observeOn(AndroidSchedulers.mainThread()) <-- ObserveOn throw the result on MainThread
         .subscribe {
            if (!TextUtils.isEmpty(it)) {
                        viewModel.searchTeacher(viewModel.meditationDetails?.schoolId,it)
                        binding?.etEmailOfTeacher?.visible()   //Crash at this point
                        binding?.tvEmailOfYourTeacher?.visible()
                }
       }

参考:RxJava Debounce doc

debounce(long, TimeUnit) is by default on the computation Scheduler

好处:您可以在每次操作后使用运算符 doOnNext 并记录哪个线程与 Thread.currentThread().getName()

一起使用

例如:

...
.debounce(...)
.doOnNext{ Log.d(TAG, "Debounce on: " +  Thread.currentThread().getName()) } <-- This will show RxComputationScheduler-N
.observeOn(AndroidSchedulers.mainThread())
.doOnNext{ Log.d(TAG, "Debounce on: " +  Thread.currentThread().getName()) } <-- This will show main
...

@xiaomi 关于默认在计算调度程序上运行的 debounce 是正确的,但是为了解决您的问题,您必须在 debounce:

binding.autoCompleteTextView2.addRxTextWatcher()
         .debounce(400, TimeUnit.MILLISECONDS) // debounce will operate on computation scheduler
         .observeOn(AndroidSchedulers.mainThread()) // change all the operators below to operate on main thread
         .subscribe {
                    // code here will be executed on main thread
                }
       }

来自docs

ObserveOn, affects the thread that the Observable will use below where that operator appears.