BlockingGet block UI thread RxJava 2

2020-04-05 08:12发布

问题:

I am dealing with the problem.

I am trying to call RxJava in the sync manner, however doing that results in blocking the Main thread.

Here is my code

   @Override
    public Single<SettingsBundle> getSettings() {
        SettingsBundle settingsModel = mSettingsManager.getSettings();
        return Single.just(settingsModel).map(mSettingsMapper);
    }

And here is my sync call

   @Override
    public SettingsBundle getSettingsSync() {
        return getSettings().blockingGet();
    }

When calling the getSettingsSync the Main thread is blocked, however sometimes it works fine, what is more problematic.

I have tried something like that

@Override
public SettingsBundle getSettingsSync() {
    return getSettings()
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .blockingGet();
}

But it stills remains blocked.

What I am doing wrong, I would be grateful for any help.

Thanks.

回答1:

TL;TR

never use observeOn(AndroidSchedulers.mainThread()) with blockingGet()


Long version

The output for:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val result =
                Single.just("Hello")
                .subscribeOn(Schedulers.io())
               // .observeOn(AndroidSchedulers.mainThread())
                .map {
                    println("1. blockingGet `$it` thread: ${Thread.currentThread()}")
                    return@map it
                }
                .blockingGet()
        println("2. blockingGet `$result` thread: ${Thread.currentThread()}")
    }
}

is

 1. blockingGet `Hello` thread: Thread[RxCachedThreadScheduler-1,5,main]
 2. blockingGet `Hello` thread: Thread[main,5,main]

As you can see result was generated on main thread (line 2), the map function was execute in the RxCachedThreadScheduler thread.

With the line .observeOn(AndroidSchedulers.mainThread()) decommented the blockingGet() never return and all is stucked.



回答2:

.observeOn(AndroidSchedulers.mainThread())
.blockingGet();

The problem exists in this specific combination of operators. AndroidSchedulers schedules code to run on the main thread, however the blockingGet() stops more code from executing on that thread. Simply put AndroidSchedulers and the blocking operators of RxJava do not work well together.

Since the android scheduler might be used in the construction of the observable this means any use of the blocking* operators on the main thread will be prone to deadlocks regardless of what you try to do.



回答3:

If you really need a function to run on the main thread and also need it to be synchronous, then you could do something like this:

  1. If this is the main thread (Looper.myLooper() == Looper.getMainLooper()), then run func()

  2. If not on the main thread, then you can use the combination of observeOn(AndroidSchedulers.mainThread()) with blockingGet()