tsiro
tsiro

Reputation: 2393

RxAndroid repeated ui manipulation task error

I have the following code to enable immersive mode. It is a repeated task which is executed every 1 second:

Completable
                        .fromAction(() -> {
                                if (uiSystemVisibility == 0) enableImmersiveMode();
                        })
                        .subscribeOn(Schedulers.io())
                        .repeatWhen(objectFlowable -> objectFlowable.delay(1, TimeUnit.SECONDS, AndroidSchedulers.mainThread()))
                        .subscribe();

enableImmersiveMode():

public void enableImmersiveMode() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        return;
    }

    getWindow().getDecorView().setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_IMMERSIVE);
}

When enableImmersiveMode() is called I am getting the following error:

Only the original thread that created a view hierarchy can touch its views

I have added to delay() method the mainThread scheduler to operate on, but the error persists. Could anyone tell me what I am doing wrong?

Upvotes: 0

Views: 36

Answers (1)

EAlgekrans
EAlgekrans

Reputation: 131

This line:

.subscribeOn(Schedulers.io())

means that the code in your callable will run on the IO thread. So first time that method is called it will be on the IO thread and not the main thread. Remove that line if this is not your intention. The default behavior is that whichever thread that subscribes to the observable will be the thread that is executing the code in your callable.

Furthermore, the scheduler you define in delay operator of repeatWhen is the scheduler that will be used for delaying and by default it is Schedulers.computation(). But you will never reach this part of the code since this is for when the observable re-subscribes after the initial subscription. But in the initial subscription your app crashes due to you accessing the UI from an IO thread.

If you simply want this code to execute every 1 second, you could try this:

val disposable = Observable.interval(1, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe {
                    if (uiSystemVisibility == 0) enableImmersiveMode();
                }

Be aware that this observable will repeat until you tell it to stop. Do that by calling:

disposable.dispose()

Upvotes: 3

Related Questions