El método getContactsFromFirebase () devuelve una lista vacía

public List getContactsFromFirebase(){ FirebaseDatabase.getInstance().getReference().child("Users") .addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { for (DataSnapshot snapshot : dataSnapshot.getChildren()) { Users user = snapshot.getValue(Users.class); assert user != null; String contact_found = user.getPhone_number(); mContactsFromFirebase.add(contact_found); Log.i("Test", mContactsFromFirebase.toString()); } } @Override public void onCancelled(DatabaseError databaseError) { } }); return mContactsFromFirebase; } 

Parece que no puedo encontrar el error. En el código anterior, cuando llamo al registro, obtengo los valores de mContactsFromFirebase , pero el método getContactsFromFirebase() devuelve una lista vacía. ¿Usted me podría ayudar por favor?

Los datos se cargan desde Firebase de forma asincrónica. Como puede llevar algo de tiempo obtener los datos del servidor, el código principal de Android continúa y Firebase llama a su onDataChange cuando los datos están disponibles.

Esto significa que cuando return mContactsFromFirebase , aún estará vacío. La manera más fácil de ver esto es mediante la colocación de algunas declaraciones de registro:

 System.out.println("Before attaching listener"); FirebaseDatabase.getInstance().getReference().child("Users") .addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { System.out.println("In onDataChange"); } @Override public void onCancelled(DatabaseError databaseError) { throw databaseError.toException(); // don't ignore errors } }); System.out.println("After attaching listener"); 

Cuando ejecuta este código, se imprimirá:

Antes de unir oyente

Después de unir oyente

En onDataChange

Probablemente no sea el orden en el que esperaba la salida. Como puede ver la línea después de que se llame a la callback antes de onDataChange . Eso explica por qué la lista que devuelve está vacía o (más correctamente) está vacía cuando la devuelve y solo se completa más tarde.

Hay algunas formas de lidiar con esta carga asíncrona.

Lo más simple de explicar es poner todo el código que devuelve la lista en el método onDataChange . Eso significa que este código solo se ejecuta después de que los datos se hayan cargado. En su forma más simple:

 public void onDataChange(DataSnapshot dataSnapshot) { for (DataSnapshot snapshot : dataSnapshot.getChildren()) { Users user = snapshot.getValue(Users.class); assert user != null; String contact_found = user.getPhone_number(); mContactsFromFirebase.add(contact_found); System.out.println("Loaded "+mContactsFromFirebase.size()+" contacts"); } } 

Pero hay más enfoques que incluyen el uso de una callback personalizada (similar al propio ValueEventListener Firebase):

 public interface UserListCallback { void onCallback(List value); } 

Ahora puede pasar una implementación de esta interfaz a su método getContactsFromFirebase :

 public void getContactsFromFirebase(final UserListCallback callback) { databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { for (DataSnapshot snapshot : dataSnapshot.getChildren()) { Users user = snapshot.getValue(Users.class); assert user != null; String contact_found = user.getPhone_number(); mContactsFromFirebase.add(contact_found); System.out.println("Loaded "+mContactsFromFirebase.size()+" contacts"); } myCallback.onCallback(mContactsFromFirebase); } @Override public void onCancelled(DatabaseError databaseError) { throw databaseError.toException(); } }); } 

Y luego llámalo así:

 getContactsFromFirebase(new UserListCallback() { @Override public void onCallback(List users) { System.out.println("Loaded "+users.size()+" contacts") } }); 

No es tan simple como cuando los datos se cargan sincrónicamente, pero esto tiene la ventaja de que se ejecuta sin bloquear el hilo principal.

Este tema se ha discutido mucho antes, por lo que le recomiendo que lea algunas de estas preguntas también:

  • esta publicación del blog de Doug
  • Establecer el valor de propiedad de Singleton en Firebase Listener (donde expliqué cómo en algunos casos puede obtener carga de datos sincrónicos, pero generalmente no puede)
  • Devolver un objeto Android (la primera vez que utilicé las instrucciones de registro para explicar lo que está pasando)
  • ¿Es posible cargar datos de manera sincronizada desde Firebase?
  • https://stackoverflow.com/a/38188683 (donde Doug muestra una forma genial pero compleja de usar la API de Task con la base de datos de Firebase)
  • ¿Cómo devolver el valor de dataSnapshot como resultado de un método? (de donde tomé prestada parte de la syntax de callback)