Rxjava: Single....blockingGet() hangs in RxJava 2.1.3 when using Android/Firebase

Created on 30 Aug 2017  路  9Comments  路  Source: ReactiveX/RxJava

When using the following pattern to synchronously get data from Firebase Realtime Database:

String s = Single.create(new SingleOnSubscribe<String>() {
    @Override
    public void subscribe(SingleEmitter<String> e) throws Exception {
        FirebaseDatabase.getInstance().getReference("path").orderByChild("child")
                     .equalTo("xyz").addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                e.onSuccess("Got it");
            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
                e.onError(databaseError.toException());
            }
        });
    }
}).blockingGet();

It will hang and create an ANR error. If I use the same Firebase "innards" outside of the Single, it fires just fine. The Single without the Firebase code inside also will fire, so it seems there is some incompatibility between the two.

[Apologies on the formatting, I can't for the life of me get it to look nice]

Any ideas?

2.x Question StackOverflow

Most helpful comment

If blockingGet doesn't return it means the ValueEventListener was not called. We had some issues when dealing with Android features that use weak references for their listeners: If you don't have a strong reference to them, they may disappear and events are never signalled. Try this:

ValueEventListener vel = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                e.onSuccess("Got it");
            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
                e.onError(databaseError.toException());
            }
};

WhatEverThisTypeIs fb = FirebaseDatabase.getInstance()
                     .getReference("path")
                     .orderByChild("child")
                     .equalTo("xyz");
fb.addListenerForSingleValueEvent(vel);

e.setCancellation(() -> fb.removeListenerForSingleValueEvent(vel));

Also blocking on the main thread is discouraged so not sure on which you blocked originally and in your Rx. Assuming from the ANR that you block the main thread and the Firebase listener signals on the main thread but can't since you are blocking it, hence a deadlock.

All 9 comments

Any ideas?

Use three backticks before and after the code example and add line breaks to long lines.

It will hang and create an ANR error.

What's the error?

Firebase Realtime Database

If you have a question/issue about a library/technology built on top of RxJava (such as Retrofit, RxNetty, etc.), please consider asking a question on StackOverflow first (then maybe on their issue list).

Thank you for the formatting tip!

The traces shows this, it seems relevant, but not sure what it says:

....
at java.lang.Object.wait!(Native method)
  - waiting on <0x0883bdc5> (a java.lang.Object)
  at java.lang.Thread.parkFor$(Thread.java:2127)
  - locked <0x0883bdc5> (a java.lang.Object)
  at sun.misc.Unsafe.park(Unsafe.java:325)
  at java.util.concurrent.locks.LockSupport.park(LockSupport.java:161)
  at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:840)
  at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:994)
  at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1303)
  at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:203)
  at io.reactivex.internal.observers.BlockingMultiObserver.blockingGet(BlockingMultiObserver.java:83)
  at io.reactivex.Single.blockingGet(Single.java:2154)
....

I figured since the code inside the Single.Create... worked fine on it's own, it was more related to RxJava. However, I will go ahead and send an email to their support also, although I don't hold out a lot of hope that their support know much about integrating with RxJava :-/

If blockingGet doesn't return it means the ValueEventListener was not called. We had some issues when dealing with Android features that use weak references for their listeners: If you don't have a strong reference to them, they may disappear and events are never signalled. Try this:

ValueEventListener vel = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                e.onSuccess("Got it");
            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
                e.onError(databaseError.toException());
            }
};

WhatEverThisTypeIs fb = FirebaseDatabase.getInstance()
                     .getReference("path")
                     .orderByChild("child")
                     .equalTo("xyz");
fb.addListenerForSingleValueEvent(vel);

e.setCancellation(() -> fb.removeListenerForSingleValueEvent(vel));

Also blocking on the main thread is discouraged so not sure on which you blocked originally and in your Rx. Assuming from the ANR that you block the main thread and the Firebase listener signals on the main thread but can't since you are blocking it, hence a deadlock.

I tried this

String s = Single.create(new SingleOnSubscribe<String>() {
    @Override
    public void subscribe(SingleEmitter<String> e) throws Exception {
        ValueEventListener vel = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                e.onSuccess("Got it");
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                e.onError(databaseError.toException());
            }
        };


        Query q = FirebaseDatabase.getInstance().getReference("path").orderByChild("child")
                .equalTo("xyz");
        q.addListenerForSingleValueEvent(vel);

        e.setCancellable(() -> q.removeEventListener(vel));
    }
}).blockingGet();

and got the same result. I also tried "...subscribeOn(Schedulers.newThread()).blockingGet();" and no change.

Please try replacing blockingGet with subscribe? If it works, then please consider my comment again:

you block the main thread and the Firebase listener signals on the main thread but can't since you are blocking it, hence a deadlock.

It does work when I move it to subscribe(), unfortunately it breaks the desired functionality of having synchronous access to the Firebase result. Do you have any other suggestions of how to do this?

I defer you to your StackOverflow questions where those who know the technologies involved can help you out. You may want to change your views about that synchronous access though.

https://stackoverflow.com/questions/45965683/single-create-blockingget-hangs-in-rxjava-2-1-3-when-using-android-firebas

bummer, thank you for all the help

Yeap, deadlock 馃憤

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dzharikhin picture dzharikhin  路  4Comments

Jaap-van-Hengstum picture Jaap-van-Hengstum  路  3Comments

ZakTaccardi picture ZakTaccardi  路  3Comments

aballano picture aballano  路  3Comments

paulblessing picture paulblessing  路  3Comments