Home » Android » android – How to detach firestore listener

android – How to detach firestore listener

Posted by: admin May 14, 2020 Leave a comment

Questions:

I’m new to FireStore. I created a ListenerRegistration to update my Recycler View. I know my implementation may not be perfect, but every time my activity got destroyed, my app throws an error on the lines that are inside this Listener. I don’t know why, but mt registration.remove() is not working before on destroy or after finish() activity. Can someone help?

public class MainActivity extends AppCompatActivity {

  private ListenerRegistration registration;
  private com.google.firebase.firestore.Query query;

  private void requestPacienteList(){

    FirebaseFirestore db = FirebaseFirestore.getInstance();
    progress.setVisibility(View.VISIBLE);
    query = db.collection("Hospital");
    registration = query.addSnapshotListener(new EventListener<QuerySnapshot>() {

        @Override
        public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
            for(DocumentSnapshot documentSnapshot : documentSnapshots){

                if(documentSnapshot.get("nome").equals("Santa Clara")){

                    hospital = documentSnapshot.toObject(Hospital.class);
                    hospital.setHospitalDocumentKey(documentSnapshot.getId());
                    FirebaseFirestore db = FirebaseFirestore.getInstance();

                    db.collection("Hospital")
                            .document(hospital.getHospitalDocumentKey())
                            .collection("Pacientes")
                            .addSnapshotListener(new EventListener<QuerySnapshot>() {

                                 @Override
                                public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {

                                    homeModelList.clear();
                                    for(DocumentSnapshot documentSnapshot : documentSnapshots){

                                        final Paciente paciente = documentSnapshot.toObject(Paciente.class);
                                        paciente.setPacienteKey(documentSnapshot.getId());
                                        FirebaseFirestore db = FirebaseFirestore.getInstance();
                                        db.collection("Pessoa")
                                                .document(paciente.getProfissionalResponsavel())
                                                .addSnapshotListener(new EventListener<DocumentSnapshot>() {

                                                    @Override
                                                    public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) {

                                                        Profissional profissional = documentSnapshot.toObject(Profissional.class);
                                                        int[] covers = new int[]{R.drawable.ic_person_black};
                                                        HomeModel p = new HomeModel(paciente.getNome()+" "+paciente.getSobrenome(),paciente.getBox(),paciente.getLeito(),
                                                        covers[0],profissional.getNome()+ " "+profissional.getSobrenome(),paciente.getPacienteKey());
                                                        homeModelList.add(p);
                                                        homeAdapter.notifyDataSetChanged();
                                                        prepareListaPacientes();
                                                    }
                                        });
                                    }
                                }
                            });
                    }
            }
        }
    });

    switch (id){

        case R.id.logout:

            if(FirebaseAuth.getInstance().getCurrentUser()!=null)
                FirebaseAuth.getInstance().signOut();

            Intent it = new Intent(HomeActivity.this, MainActivity.class);
            startActivity(it);

            if(registration!=null)
                registration.remove();

            finish();

            drawerLayout.closeDrawers();
            break;
    }
  }

}

My onDestroy method:

@Override
protected void onDestroy() {
    super.onDestroy();
    registration.remove();
}

When I remove this if:

if(FirebaseAuth.getInstance().getCurrentUser()!=null)
    FirebaseAuth.getInstance().signOut();

My problem is gone. But if I don’t, I get the following error:

java.lang.NullPointerException: Attempt to invoke virtual method
‘java.lang.Object
com.google.firebase.firestore.DocumentSnapshot.toObject(java.lang.Class)’
on a null object reference
at santauti.app.Activities.Home.HomeActivity$4$1$1.onEvent(HomeActivity.java:200)
at santauti.app.Activities.Home.HomeActivity$4$1$1.onEvent(HomeActivity.java:197)
at com.google.firebase.firestore.DocumentReference.zza(Unknown Source)
at com.google.firebase.firestore.zzd.onEvent(Unknown Source)
at com.google.android.gms.internal.zzejz.zza(Unknown Source)
at com.google.android.gms.internal.zzeka.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

How to&Answers:

When you use addSnapshotListener you attach a listener that gets called for any changes. Apparently you have to detach those listeners before the activity gets destroyed. An alternative is to add the activity to your call to addSnapshotListener:

 db.collection("Pessoa").document(paciente.getProfissionalResponsavel())
   .addSnapshotListener(MainActivity.this, new EventListener<DocumentSnapshot>() {

You’ll need to update MainActivity.this to match your code.

By passing in the activity, Firestore can clean up the listeners automatically when the activity is stopped.

Yet another alternative is to use get() to get those nested documented, which just reads the document once. Since it only reads once, there is no listener to clean up.

Answer:

use registration.remove(); for Stop listening to changes

Query query = db.collection("cities");
ListenerRegistration registration = query.addSnapshotListener(
        new EventListener<QuerySnapshot>() {
            // ...
        });

// ...

// Stop listening to changes
registration.remove();

see more about this : https://firebase.google.com/docs/firestore/query-data/listen#detach_a_listener

Answer:

If you’re using MVVM architecture specifically the ViewModel and LiveData architecture classes, then it’s not a good idea to pass an activity via addSnapshotListener() method.

What you need to do is store the ListenerRegistration from the addSnapshotListener method and manually remove the listener on onInactive() method of LiveData class.

Answer:

Add these two method in your code, this will solve your problem.

@Override
public void onEvent(DocumentSnapshot documentSnapshot, 
FirebaseFirestoreException e) {
    if (e != null) {
        Log.w(LOG_TAG, ":onEvent", e);
        return;
    }
}

and

@Override
protected void onStop() {
    super.onStop();

    if (registration!= null) {
        registration.remove();
        registration = null;
    }
}

Answer:

ListenerRegistration is the Key

If you want to start and stop Realtime Updates from a FirebaseDatabase then you should register and unregister your EventListners with ListenerRegistration.

Full Code:

MyActivity extends AppCompatActivity implements EventListener<QuerySnapshot>{

      private CollectionReference collectionRef; //<- You will get realtime updates on this
      private ListenerRegistration registration;

      public void registerListner(){
           registration = collectionRef.addSnapshotListener(this);      
      }

      public void unregisterListener(){
           registration.remove();           //<-- This is the key
      }


      @Override
      public void onEvent(@javax.annotation.Nullable QuerySnapshot queryDocumentSnapshots, @javax.annotation.Nullable FirebaseFirestoreException e) {

          for(DocumentChange dc : queryDocumentSnapshots.getDocumentChanges()){

              Log.d("Tag",  dc.getType().toString()+" "+dc.getDocument().getData());

          }
      }

}