Skip to content
Advertisement

What is the difference between get() and addListenerForSingleValueEvent?

I see a lot of tutorials, documentation and questions about using the Firebase Realtime Database on Android talk about the addListenerForSingleValueEvent method. This method reads the value from the database once, which is what I want to do.

But in the auto-complete in my IDE and in the documentation I also see a method called get(), which also seems to read a value from the database once. From the name and signature it seems simpler, and more modern (as it returns a Task). But since the other method is mentioned so much more, I want to make sure I’m using the right code.

So what is the difference between get() and addListenerForSingleValueEvent, why is the latter mentioned so much more in documentation, tutorials and questions, and which one should I use when I want to read a value from the database once?

Advertisement

Answer

First off: keep in mind that Firebase Realtime Database is best when used to keep data in sync between the client and the database server (or between multiple clients) by using a long-lived listener. On Android you do this with addValueEventListener, which you should try to use whenever possible.

But in some cases you’ll want to read a value from the database only once. So let’s see if I can answer the questions in turn, starting with the most important one:

Which method should I use when I want to read a value from the database once?

If you need to read a value from the database only once, use the new get() method.

In Java that looks like this:

ref.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
    @Override
    public void onComplete(@NonNull Task<DataSnapshot> task) {
        if (!task.isSuccessful()) {
            Log.e("firebase", "Error getting data", task.getException());
        }
        else {
            Log.d("firebase", String.valueOf(task.getResult().getValue()));
        }
    }
});

And in Kotlin it is:

ref.get().addOnSuccessListener {
    Log.i("firebase", "Got value ${it.value}")
}.addOnFailureListener{
    Log.e("firebase", "Error getting data", it)
}

Why do you recommend using get() when addListenerForSingleValueEvent is mentioned so much more?

We introduced addListenerForSingleValueEvent in our first Android SDK, and it has been there ever since. Over the years a lot of tutorials have been written, and a lot of questions have been asked and answered.

We’re updating the documentation of course. But there’s no way we can get all tutorials updated. So for the foreseeable future, there will be more mentions of addListenerForSingleValueEvent than of the new get() method.

what is the difference between get() and addListenerForSingleValueEvent?

As said: the addListenerForSingleValueEvent method has been part of the Firebase Android SDK for as long as it exists, and is used to read a value from the database once. It does this by:

  1. Attaching a listener with addValueEventListener
  2. Waiting for the value to show up from the persistence layer
  3. Calling onDataChange
  4. Removing the listener

This worked really well… until we introduced disk caching in version 2.0 of the SDK (way back at I/O 2015). Before that, all values in step 2 would always come from the server, either because the client already had a listener, or because this would attach the first listener to the server. But with disk caching, if you had previously read the value but currently had no listener to it, step 2 will read the value from the disk cache, and your onDataChange will be called with that value immediately. Even if the value on the server has been updated since. In fact, behind the scenes the listener will update the value in the disk cache, but only after calling your onDataChange with the (possibly stale) value from the cache.

While this behavior can be explained, it is not what almost anyone wanted. Unfortunately we found this edge case too late to classify it as a simple implementation bug and fix it. So we left it in, and recommended that folks either use disk persistence or use addListenerToSingleValueEvent, but not both. Or you could call keepSynced(true) on the same reference/query as a workaround. All messy, and not good.

Fast forward 5+ years, and we finally introduced a new method that doesn’t have this awkward behavior anymore. And since Android APIs have moved on quite a bit since 2015, we also use a (slightly) more modern method signature: Task<DataSnapshot> get().

Advertisement