No event listener removal for Firebase DatabaseReference?

10,286

Solution 1

As the answer superman links to show: you indeed will need to remove the listener in the opposite lifecycle event.

To remove a listener, you need to keep the handle that is returned when you call addChildListener():

Query query = mDatabase.child("projects").orderByChild("viewCount").limitToLast(15);
ChildEventListener listener = query.addChildEventListener(new ChildEventListener() { ...

And then pass it to removeEventListener():

query.removeChildEventListener(listener);

You can see that I explicitly use a Query here, since that is what you actually attach your listener to. But if you have a plain DatabaseReference the same methods are available, since DatabaseReference inherits from Query.

Solution 2

Firebase objects are lightweight references to locations in your Firebase Database. There is no need (nor ability) to manage their life-cycle. So, you can just let the Java garbage collector take care of them.

However, once you start attaching listeners (e.g. addValueEventListener()) you should detach them in the corresponding life-cycle event with removeEventListener(). Also see Firebase adding listeners in adapters in Android and How stop Listening to firebase location in android.

Solution 3

As Frank says, you need to remove the listener from the exact DatabaseReference, not just the root DatabaseReference as I was doing.

To simplify this, I created a small class below:

public class FirebaseListenerRef {

    private ValueEventListener listener;
    private DatabaseReference ref;

    public FirebaseListenerRef(DatabaseReference ref, ValueEventListener listener) {
        this.listener = listener;
        this.ref = ref;

        // Start listening on this ref.
        ref.addValueEventListener(listener);
    }

    public void detach() {
        ref.removeEventListener(listener);
    }
}

To use this, just create and detach it in paired lifecycle events, e.g.

private FirebaseListenerRef myFirebaseListenerRef;

public void onAttach(Content context) {
    super.onAttach(context);
    myFirebaseListenerRef = new FirebaseListenerRef(
        FirebaseDatabase.getInstance().getReference().child("path-to-listen-on", 
        new ValueEventListener() { 
            ... implement the listener logic
         }
      );
}

public void onDetach() {
    super.onDetach();
    if ( myFirebaseListenerRef != null ) {
    // Detach our listener from its reference
        myFirebaseListenerRef.detach();
        myFirebaseListenerRef == null;
    }
}

Solution 4

If you use Query to add addValueEventListener. Then you should remove the listener using Query instance not DatabaseReference instance.

In question with DatabaseReference the developer was using orderByChild which is of type Query So to remove the listener we need to get reference to Query instance and then use removeEventListener.

Here is my example of removing the listener:

ValueEventListener carChangesListener;
Query ownerId_Query;

Then

DatabaseReference bikeRef = FirebaseDatabase.getInstance().getReference().child("Cars");
        ownerId_Query = bikeRef.orderByChild("ownerId").equalTo(FirebaseAuth.getInstance().getCurrentUser().getUid());
        carChangesListener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                if (dataSnapshot.getValue() != null) {
                    /*NOTE: handle cars response here*/
                }else{
                    Toast.makeText(getActivity(), "No car found for this user.",
                            Toast.LENGTH_LONG).show();
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.w(HOME_TAG, "Failed to read value.", databaseError.toException());
                Toast.makeText(getActivity(), "Error!" + databaseError.toException().getMessage(),
                        Toast.LENGTH_LONG).show();
            }
        };
        ownerId_Query.addValueEventListener(bikeChangesListener);

Now to remove the carChangesListener I will do following:

ownerId_Query.removeEventListener(carChangesListener);
Share:
10,286
Lion789
Author by

Lion789

Updated on June 05, 2022

Comments

  • Lion789
    Lion789 about 2 years

    Hey do I need to remove this listener at some point, or does it get removed on its own? I have this called in a fragment within my activity, and users can go to another view without this one being destroyed. Therefore not sure if I am somehow suppose to remove this in an onDestroy, onPause call? I do not see a way to remove it since it is a DatabaseReference

    Here is the code:

    private DatabaseReference mDatabase;
      mDatabase.child("projects").orderByChild("viewCount").limitToLast(15).addChildEventListener(new ChildEventListener() {
                @Override
                public void onChildAdded(DataSnapshot dataSnapshot, String s) {
    
  • Lion789
    Lion789 over 7 years
    so adding a ChildEventListener ... does that require then for the listener to be removed if so how do I call it because there is no method like that on the DatabaseReference?
  • Tamas
    Tamas about 6 years
    Thanks, I had the exact same problem. Removing the listener from DatabaseReference does nothing. This mistake doesn't event show up in the logs, and most tutorials fail to mention it.
  • DragonFire
    DragonFire about 4 years
    I think it is about time for android to think about a class of variables and methods which are not null by birth (so they could be declared as private nonNull -> and so on) if they have not got them already.. the null pointer reference is mostly a nuisance in my experience...
  • DragonFire
    DragonFire about 4 years
    you may like to reattach the listener like query.addChildEventListener(listener) on your on onResume, onRestart or onStart depending on requirement.... because if you remove the listeners and minimize the app and bring it up again it will have stopped listening