Difference Between Addvalueeventlistener() and Addlistenerforsinglevalueevent() of Firebase

Difference between addValueEventListener() and addListenerForSingleValueEvent() of firebase

addValueEventListener() keep listening to query or database reference it is attached to.

But addListenerForSingleValueEvent() executes onDataChange method immediately and after executing that method once, it stops listening to the reference location it is attached to.

What is the difference between get() and addListenerForSingleValueEvent?

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().

Firebase Child and Value listeners

These are my specific questions:

If querying is involved, is ChildEventListener the one that I choose instead of the alternative?

From the two examples above, it seems like ValueEventListener is used when I can specify the node that has the Key:Value pair immediately one level below the specified node. On the other hand, ChildEventListener is used when there is no Value involved, just Childs (which is what the "Child" in ChildEventListener is meant for?). Am I right to say this previous statement?

Both valueeventlistener and childeventlistener are kinda the same but they are different from addListenerForSingleValueEvent which reads data only once.

Now, no you can choose any of the two if query is involved, you dont have to use childeventListener always.

For the second question its kinda true what you said, usually in the valueeventlistener we use for loop:

  for(DataSnapshot data : dataSnapshot.getChildren()){

So the data will iterate inside the children of dataSnapshot. But if you use ChildEventListener you dont have to use that for loop above.

But obviously if it is nested more you will have to do that loop in childeventlistener

Android Firebase keep addListenerForSingleValueEvent updated

If you have enabled offline capabilities, it means that you'll be able to use your app even if it temporarily loses its network connection. So the ValueEventListener will keep listening on the local stored datas as long as you are offline. If you want the results to be updated, then you need to go online.

If you want to listen to data only once, then you need to use: addListenerForSingleValueEvent(). If you want to use addValueEventListener, just don't forget to remove the listener according to the life-cycle of your activity as I have already answered here.

Firebase Offline Capabilities and addListenerForSingleValueEvent

Update (2021): There is a new method call (get on Android and getData on iOS) that implement the behavior you'll like want: it first tries to get the latest value from the server, and only falls back to the cache when it can't reach the server. The recommendation to use persistent listeners still applies, but at least there's a cleaner option for getting data once even when you have local caching enabled.



How persistence works

The Firebase client keeps a copy of all data you're actively listening to in memory. Once the last listener disconnects, the data is flushed from memory.

If you enable disk persistence in a Firebase Android application with:

Firebase.getDefaultConfig().setPersistenceEnabled(true); 

The Firebase client will keep a local copy (on disk) of all data that the app has recently listened to.

What happens when you attach a listener

Say you have the following ValueEventListener:

ValueEventListener listener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println(snapshot.getValue());
}

@Override
public void onCancelled(FirebaseError firebaseError) {
// No-op
}
};

When you add a ValueEventListener to a location:

ref.addValueEventListener(listener); 
// OR
ref.addListenerForSingleValueEvent(listener);

If the value of the location is in the local disk cache, the Firebase client will invoke onDataChange() immediately for that value from the local cache. If will then also initiate a check with the server, to ask for any updates to the value. It may subsequently invoke onDataChange() again if there has been a change of the data on the server since it was last added to the cache.

What happens when you use addListenerForSingleValueEvent

When you add a single value event listener to the same location:

ref.addListenerForSingleValueEvent(listener);

The Firebase client will (like in the previous situation) immediately invoke onDataChange() for the value from the local disk cache. It will not invoke the onDataChange() any more times, even if the value on the server turns out to be different. Do note that updated data still will be requested and returned on subsequent requests.

This was covered previously in How does Firebase sync work, with shared data?

Solution and workaround

The best solution is to use addValueEventListener(), instead of a single-value event listener. A regular value listener will get both the immediate local event and the potential update from the server.

A second solution is to use the new get method (introduced in early 2021), which doesn't have this problematic behavior. Note that this method always tries to first fetch the value from the server, so it will take longer to completely. If your value never changes, it might still be better to use addListenerForSingleValueEvent (but you probably wouldn't have ended up on this page in that case).

As a workaround you can also call keepSynced(true) on the locations where you use a single-value event listener. This ensures that the data is updated whenever it changes, which drastically improves the chance that your single-value event listener will see the current value.

Firebase addListenerForSingleValueEvent keeps getting old values that no longer is in Firebase database?

The method you need to use to have all the values updated is addValueEventListener(). Because you need to listen for more than one value this is the only way to achieve it and to use addListenerForSingleValueEvent().

The most important thing is to remove the listener in your onDestroy method like this:

yourReference.removeEventListener(valueEventListener);

Android Firebase addListenerForSingleValueEvent is not working

The data is loaded from Firebase asynchronously. By the time your log test, the onDataChange hasn't run yet.

To see this in action, add some more logging inside onDataChange:

private int test;

test = 0;
userRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild(userID)) {
test = 1;
}
else {
test = 2;
}
Log.d(TAG, "Test2 = " + test);
}

@Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // Don't ignore errors
}
});

Log.d(TAG, "Test = " + test);

Now you'll first see:

Test = 0

And then once the data has loaded and onDataChange gets called:

Test = 1

or

Test = 2

For this reason you should always put (or call) the code that needs the data from the database from within onDataChange().

(Kotlin) I want to retrieve data from realtime database(firebase) without using addValueEventListener

Nice to meet Korean on stackOverflow.

I can't write down the answer as Korean, so I am writing down the answer in English.

If you want to detect data for once, you can use addListenerForSingleValueEvent().

addListenerForSingleValueEvent() can be used when you need one callback.

You can check the Korean Tistory blog, which has a good explain.

https://stack07142.tistory.com/282

Also the official docs might be helpful.

If you have any more question, please don't hesitate to write a comment.

Gam sa hap ni da



Related Topics



Leave a reply



Submit