How to Set Addsnapshotlistener and Remove in Populateviewholder in Recyclerview Item

How to set addSnapshotListener and remove in populateViewHolder in RecyclerView Item?

To achieve this, you need to use a EventListener<DocumentSnapshot> like this:

EventListener<DocumentSnapshot> eventListener = new EventListener<DocumentSnapshot>() {
@Override
public void onEvent(DocumentSnapshot snapshot, FirebaseFirestoreException e) {
if (snapshot != null && snapshot.exists()) {
//Do what you need to do
}
}
};

Declare a global ListenerRegistration listenerRegistration; variable and add the SnapshotListener in the place where is needed like this:

if (listenerRegistration == null ) {
listenerRegistration = yourRef.addSnapshotListener(eventListener);
}

To remove the listener, just use the following lines of code, in your onStop() method:

@Override
protected void onStop() {
if (listenerRegistration != null) {
listenerRegistration.remove();
}
}

Also, don't forget to add it again once your onStart() method is called.

@Override
protected void onStart() {
super.onStart();
listenerRegistration = yourRef.addSnapshotListener(eventListener);
}

When you are calling addSnapshotListener for listening to realtime updates, it means that you attach a listener that gets called for every change that takes place in your database. So this is happening also when your app is closed, that's why it's mandatory to detach the listeners before the activity gets destroyed.

If in your app you don't need to get the data in realtime, then you can simply use a get() call directly on the reference, which just reads the document only once. Since it only reads once, there is no listener to be removed. This is the correspondent of addListenerForSingleValueEvent() used in the Firebase realtime database.

There is also a more elegant way for removing the listener which is to pass the activity as first the argument in the addSnapshotListener() method, so Firestore can clean up the listeners automatically when the activity is stopped.

ListenerRegistration lg = yourDocumentRef
.addSnapshotListener(YourActivity.this, eventListener);

How to stop snapshot listener which was inside the RecyclerView Adapter?


Is it okay if I don't include the Activity in and leave the snapshotlistener like that?

No, it is not! When you are calling addSnapshotListener for listening to realtime updates, it means that you attach a listener that gets called for every change that takes place in your database. So this is happening also when your app is closed, that's why it's mandatory to detach the listeners before the activity gets destroyed.

If not, Is there any way to solve this?

Yes, there is. Please see my answer from the following post:

  • How to set addSnapshotListener and remove in populateViewHolder in RecyclerView Item?

If in your app you don't need to get the data in realtime, then you can simply use a get() call directly on the reference, which just reads the document only once. Since it only reads once, there is no listener to be removed.

How to make the listener keep attached, even after an error occurs in Firestore?

As the docs say, if your query fails or if you have improper security rules, the listener will not receive any more events. Unfortunately, this behavior cannot be changed.

What you might try to implement, is to attach the listener again, if you get an error message. But this might not be the best solution because if you forget to add the proper rules, you'll end up adding the listener over and over again.

What you should do is attach and detach the listener according to your app's state. For instance, in Android you do something like this:

  • How to set addSnapshotListener and remove in populateViewHolder in RecyclerView Item?

How do I detach a listener defined in another scope?


private var listener: ListenerRegistration?

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)

let db = Firestore.firestore()
let docRef = db.collection("collection").document("document")

listener = docRef.addSnapshotListener { (querySnapshot, errur) in
// ....
}
}

override func viewWillDisappear(_ animated: Bool) {
// always call super.viewWillDisappear also.
super.viewWillDisappear(animated)
listener.remove() //ERROR: Use of unresolved identifier 'listener'
listener = nil
}

How would I update the list of items when 1 item is changed by the user in next screen and comes back in mobile app linked to Firestore?

If all your items are stored in a single document, to see the changes that are made in real-time, then you should listen for real-time updates. In this way, you don't have to fetch the document again and again.

When using real-time listeners, don't also forget to detach them, once are not needed anymore.

  • How to set addSnapshotListener and remove in populateViewHolder in RecyclerView Item?

How to restrict the number of adding and removing a listener in Firestore?


Is this a bad approach?

No, it's not! You should definitely remove the listener once it's not needed anymore. I assume you are adding the listener in your onStart() method and removing it in your onStop() method of your activity, right? If so, please note that this is the normal behaviour, since both methods are apart of the life-cycle of an activity and are called every time the orientation is changed. So everytime the reorientation takes place, the activity is destroyed and recreated again. Please see more infos:

  • https://developer.android.com/guide/components/activities/activity-lifecycle.

If you want a more elegant way to remove the listener, you should see the last part of my answer from this post. So you can pass the activity as the first argument in the addSnapshotListener() method and the listener will be removed automatically for you.

Edit:

According to your comment, you're right. Even if you are using that solution, the number of attaching and removing the listener would be the same. In this case, I have a solution that can reduce that number.

private boolean pending = false;
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
@Override
public void run() {
//Remove listener
pending = false;
}
};

@Override
protected void onStart() {
super.onStart();
if (pending) {
handler.removeCallbacks(runnable);
} else {
//Attach listener
}
pending = false;
}

@Override
protected void onStop() {
super.onStop();
handler.postDelayed(runnable, 3000);
pending = true;
}

Which basically means that the Handler will schedule the removal of your listener by using a Runnable callback that will actualy perform the removal after three seconds after the call to onStop(). I've set the delay to three seconds for the orientation change but in real world situation it's normally much faster even on old phones.

So if the orientation is faster than those three seconds, we simply remove the callbacks and allow the listener to keep listening. This obviously means that you reduce the number of removing the listener.

This will be also very useful because there will be not a second round trip to and from the Firestore backend to pull down the data even if the results did not change.

Firestore: When should I detach the snapshot listener which updates a TextView in the navigation DrawerLayout?

The better practice will be save login in sharedPref first time and get it all the time for setting in the textView when needed. So in this case you don't need to make requests all the time drawer open. The second advantage will be it will work much faster. Third advantage is you will reduce the number of requests instead of making requests all the time you will just make request to update if needed so instead of thousands several same reading requests you will make one update request and everything gonna happen locally. And even if user will not have internet connection textView will show needed information. And you will not pay money in case you get a lot of users for latency to google cloud. Also it answer the question of what to do with listener

How to continuously check for field/document change in Firestore - Android


The problem with this code is that I am not able to get listeners continuously with this code.

Why would you say that? This is what it does, it gets the data in real-time.

Can we use Android Services or something to continuously check for changes in the document or a field in the document.

If you want to get updates even if the user closes the app, indeed you need a service. For more info, please check my answer from the following post:

  • How to create firebase background service in Android?

I am also worried that this will kind of increase the number of reads/writes for our database.

Yes, it will increase for sure the number of reads/writes if you continue to keep the listener active. So for not paying extra reads/writes, you should remove the listener according to the life-cycle of your activity as explained in my answer from the following post:

  • How to set addSnapshotListener and remove in populateViewHolder in RecyclerView Item?


Related Topics



Leave a reply



Submit