Данные загружаются из Firebase асинхронно.Поскольку получение данных с сервера может занять некоторое время, основной код Android продолжается, и Firebase вызывает ваш onDataChange
, когда данные становятся доступны.
Это означает, что к тому времени, когда вы return mContactsFromFirebase
, оно еще пусто.Самый простой способ убедиться в этом - разместить несколько операторов журнала:
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");
Когда вы запустите этот код, он напечатает:
Перед подключением слушателя
После подключения прослушивателя
InDataChange
Вероятно, это не тот порядок, в котором вы ожидали вывод. Как вы можете видеть строку после , обратный вызов получаетзвонил раньше onDataChange
.Это объясняет, почему возвращаемый вами список пуст, или (точнее) он пуст, когда вы его возвращаете, и заполняется только позже.
Существует несколько способов справиться с этой асинхронной загрузкой.
Простейшее объяснение состоит в том, чтобы поместить весь код, который возвращает список , в метод onDataChange
.Это означает, что этот код выполняется только после загрузки данных.В простейшем виде:
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");
}
}
Но есть и другие подходы, в том числе использование настраиваемого обратного вызова (аналогично ValueEventListener
в Firebase):
public interface UserListCallback {
void onCallback(List<Users> value);
}
Теперь вы можете передать реализациюэтого интерфейса к вашему getContactsFromFirebase
методу:
public void getContactsFromFirebase(final UserListCallback myCallback) {
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();
}
});
}
И затем назовите его так:
getContactsFromFirebase(new UserListCallback() {
@Override
public void onCallback(List<Users> users) {
System.out.println("Loaded "+users.size()+" contacts")
}
});
Это не так просто, как когда данные загружаются синхронно, но это имеетПреимущество в том, что он работает без блокировки вашего основного потока.
Эта тема уже обсуждалась много ранее, поэтому я рекомендую вам также проверить некоторые из этих вопросов: