4
        DatabaseReference Ref = FirebaseDatabase.getInstance().getReference(Constants.Client + "/" + path);
        Ref.keepSynced(true);
        Ref.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });

I understand that ValueEventListener runs in a new thread, should I actually remove this at any point of time for proper thread management? (example for not too many threads running in parallel). If yes, how to do it?

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
Sreekanth Karumanaghat
  • 3,383
  • 6
  • 44
  • 72
  • Yeah . You should remove it in your components lifecycle . Othewise it will provide you callback without knowing the state of the Component(refer as Activity) . Remove the listener in `onStop()` or `onDestroy()`. – ADM Feb 19 '18 at 07:34
  • I have tried removing it, I have tried all the answers all over stack overflow but I am unable to remove it.Anyone can help? – Archit Puri May 08 '20 at 23:15

3 Answers3

10

When talking about listeners, yes, you need to remove them accordingly to the life-cycle of your activity and for this you need to use the following line of code:

databaseReference.removeEventListener(valueEventListener);

Remember if you don't do this, you'll end up wasting your battery and bandwidth. So:

  1. If you have added the listener in onStart you have to remove it in onStop.
  2. If you have added the listener in onResume you have to remove it in onPause.
  3. If you have added the listener in onCreate you have to remove it in onDestroy.

But remember onDestroy is not always called, so the last option in not always a good choice.

There is another approach in which there is no need to remove the listener and that is when using addListenerForSingleValueEvent:

Add a listener for a single change in the data at this location.

Jack Guo
  • 3,959
  • 8
  • 39
  • 60
Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • Why is onDestroy not always called? – Sreekanth Karumanaghat Feb 19 '18 at 10:59
  • 1
    Please take a look [here](https://stackoverflow.com/questions/19608948/is-ondestroy-not-always-called) and [here](https://stackoverflow.com/questions/4449955/activity-ondestroy-never-called), for a better understanding. – Alex Mamo Feb 19 '18 at 11:05
  • 1
    When using addListenerForSingleValueEvent() you should still remove it from the DatabaseReference. If it never fires (device is offline), the listener is never removed automatically and can cause a memory leak. – javmarina Aug 07 '20 at 16:00
  • @javmarina When using `addListenerForSingleValueEvent()`, there is no listener that should be removed because the listener fires only once. – Alex Mamo Aug 07 '20 at 20:52
  • 1
    I checked the source code for `addListenerForSingleValueEvent()` and it basically adds a new listener that is removed when `onDataChange()` is called. The listener is not removed if neither callback method is fired, for example when device is offline. Although that case is infrequent, we should take care of that in or code. See my answer for my proposed solution. – javmarina Aug 09 '20 at 01:01
4

To remove the ValueEventListener, you can then do this:

Remove the anonymity of the listener.

Change the code from this:-

      Ref.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });

into this:

   ValueEventListener listener= new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });
Ref.addValueEventListener(listener);

Now you will be able to remove the listener:

   @Override
public void onDestroy() {
if (Ref != null && listener != null) {
   Ref.removeEventListener(listener);
    }
 }

You need to remove it, so the listener does not stay running in the other activity lifecycles like onDestroy()

Sreekanth Karumanaghat
  • 3,383
  • 6
  • 44
  • 72
Peter Haddad
  • 78,874
  • 25
  • 140
  • 134
1

I had the same issue and was causing a lot of memory leaks. So I created a new class that handles added listeners and removes them when the corresponding method (onPause(), onStop() or onDestroy()) is called. Uses the androidx.lifecycle library and is applicable to both activities and fragments (in fact, any class that implements LifecycleOwner).

You can check the code here. You will probably be good to go without manually adding the androidx.lifecycle dependency, but you can also add this to your module-level build.gradle:

implementation 'androidx.lifecycle:lifecycle-runtime:VERSION'

In your current code, instead of:

databaseReference.addValueEventListener(valueEventListener);
// or
databaseReference.addListenerForSingleValueEvent(valueEventListener);

you need to use:

addValueEventListener(databaseReference, valueEventListener); 
// or
addListenerForSingleValueEvent(databaseReference, valueEventListener);

This is valid when called from activities or fragments that use FirebaseListenerHandler, as shown in the gist. If you need to add a Firebase listener in another situation (like services), you still have to manually remove them.

javmarina
  • 493
  • 6
  • 17