0

I'm using firebase realtime database and as far I know, you have to use the

 DatabaseReference.addValueEventListener

to read a value from the firebase database.

Here addValueEventListener is asynchronous by default. I need to read it synchronously. For FireBase storage, for the same issue while uploading files, I could make the process synchronous by using

     UploadTask.TaskSnapshot await = Tasks.await(uploadTask);

Instead of using

     uploadTask.addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
                                                    @Override
                                                    public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {

                                                    }
                                                })          

Is there any similar synchronous alternative for addValueEventListener like shown above?

Roman Pokrovskij
  • 9,449
  • 21
  • 87
  • 142
doe
  • 971
  • 2
  • 11
  • 27
  • 1
    Trying to return a value synchronously from an API that's asynchronous, it's not a good idea. You should handle the APIs asynchronously as intended. Would you like to get an answer in this way? – Alex Mamo Oct 05 '18 at 09:19
  • I'm doing that because I'm using this function inside a block which is already asynchronous. – doe Oct 05 '18 at 09:23
  • Is there any synchronous version of this? – doe Oct 05 '18 at 09:23
  • There is but calling a synchronous method in your application’s main thread could freeze your app totally, which is a terrible user experience. On Android, you might also end up getting Application Not Responding dialogs. Is this what you want? – Alex Mamo Oct 05 '18 at 09:26
  • See, As I told , I'm not calling the synchronous method on the main thread. I have some block which is running in a worker thread always, there I have to use this code. – doe Oct 05 '18 at 13:24
  • @doe There is no synchronous way to get data out of an asynchronous API. Even the `Tasks.await(uploadTask);` you show is just syntactic sugar: the call is still asynchronous. This sounds like a [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem), while the actual problem is that you're unsure how to have to async calls. Please edit your question to show the [minimal **complete** code that reproduces the problem](http://stackoverflow.com/help/mcve). – Frank van Puffelen Oct 05 '18 at 14:09
  • https://medium.com/google-developers/why-are-the-firebase-apis-asynchronous-e037a6654a93 – Doug Stevenson Oct 05 '18 at 16:57
  • @FrankvanPuffelen The problem I'm facing is actually, my firebase calls are happening in between an RxJava observable chain, which will be executing in an `io` thread. So, using one more asynchronous call inside the chain causes issue, that's why I opted the `Tasks.await(uploadTask)` for uploading, and I was searching for a similar solution for RealTimeDatabase also – doe Oct 05 '18 at 18:51
  • @FrankvanPuffelen As you said, if there's no synchronous way, I would have to go for the `Observable.create` way , that bridges the reactive world with the callback-style. Is that the only solution in this case? – doe Oct 05 '18 at 18:55
  • 1
    A `addValueEventListener` can trigger multiple times, so doesn't complete. But `addSingleValueEventListener` *can* complete. See [Doug's blost post here](https://firebase.googleblog.com/2016/10/become-a-firebase-taskmaster-part-4.html) for how to turn a `addListenerForSingleValueEvent` into a `Task` that you can await. – Frank van Puffelen Oct 05 '18 at 18:56

2 Answers2

1

Trying to return a value synchronously from an API that's asynchronous, it's not a good idea. But if your block is already asynchronous with main thread - you can use CountDownLatch and await callback response.

Example:

CountDownLatch latch = new CountDownLatch(1);
uploadTask.addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>(){
       @Override
       public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
            //Handle response
            latch.countDown();
       }
});
latch.await();
Alexey Zatsepin
  • 1,179
  • 7
  • 14
  • This is great. I wonder how devs write code when dealing with server side logic and using firebase admin sdk. I had a situation to check for existing chats of a user before starting a new chat group and i found this answer useful. – Adi Oct 20 '18 at 22:31
0

Most of the firebase methods return an instance of Task<>. Hence we can easily await on them in a non-main thread to get the result in-place (instead of implementing onCompleteListener and running into 'callback-hell').

But I was unsuccessful to find a getValue kind of method which returns a Task. I found this blogpost mentioned in the comments of the question. After going through that, I wrote one getValue extension function for myself.

internal fun DatabaseReference.getValue(): Task<DataSnapshot> {
    val dbSource = TaskCompletionSource<DataSnapshot>()

    addListenerForSingleValueEvent(object : ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            dbSource.setResult(snapshot)
        }

        override fun onCancelled(error: DatabaseError) {
            dbSource.setException(error.toException())
        }
    })

    return dbSource.task
}

Also if any corrections, inform in comments please :)

Dharman
  • 30,962
  • 25
  • 85
  • 135
Sourav Kannantha B
  • 2,860
  • 1
  • 11
  • 35