RxJava: получение «Только оригинальный поток, создавший иерархию представлений, может касаться его представлений» даже после подписки на основной поток - PullRequest
0 голосов
/ 27 сентября 2019

Я использую функцию расширения в автозаполнении текстового просмотра для использования стратегии дебадинга и подписки на mainthread.

 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) {
        }
    })
}

Я получаю сбой при попыткеобновить видимость представления: «Только исходный поток, создавший иерархию представления, может касаться его представления».Но я уже подписан и наблюдаю за 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$2.accept(OnBordingCreateMeditationAdditionalDetailsFragment.kt:283)
        at com.example.example.fragment.OnBordingCreateMeditationAdditionalDetailsFragment$setAutoCompleteTextView2$2.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?

Ответы [ 2 ]

0 голосов
/ 27 сентября 2019

@ xiaomi's корректно в отношении debounce, работающего в планировщике вычислений по умолчанию, но чтобы исправить вашу проблему, вы должны переключиться обратно в основной поток, используя оператор observeOn после 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
                }
       }

Из документов :

ObserveOn влияет на поток, который Observable будет использовать ниже, где появляется этот оператор.

0 голосов
/ 27 сентября 2019

Оператор 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()
                }
       }

Ref: RxJava Debounce doc

debounce (long, TimeUnit) по умолчанию в планировщике вычислений

Бонус: вы можете использовать оператор doOnNext после каждой операции и записывать, какой поток используется с Thread.currentThread().getName()

ex:

...
.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
...
...