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?
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.
bummer, thank you for all the help
Yeap, deadlock 馃憤
Most helpful comment
If
blockingGetdoesn't return it means theValueEventListenerwas 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: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.