Ответ 1
Firebase загружает и синхронизирует данные асинхронно. Так что ваш loadModelWithDataFromFirebase()
не ждет окончания загрузки, он просто начинает загружать данные из базы данных. К тому времени, когда ваша функция loadModelWithDataFromFirebase()
вернется, загрузка еще не завершена.
Вы можете легко проверить это для себя с помощью некоторых удачно расположенных записей журнала:
public void loadModelWithDataFromFirebase(){
Firebase db = new Firebase(//url);
Firebase bookmarksRef = fb.child(//access correct child);
Log.v("Async101", "Start loading bookmarks");
final ArrayList<Bookmark> loadedBookmarks = new ArrayList<Bookmark>();
bookmarksRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.v("Async101", "Done loading bookmarks");
//getting all properties from firebase...
Bookmark bookmark = new Bookmark(//properties here);
loadedBookmarks.add(bookmark);
}
@Override
public void onCancelled(FirebaseError firebaseError) { }
});
Log.v("Async101", "Returning loaded bookmarks");
setBookmarks(loadedBookmarks);
}
Вопреки тому, что вы, вероятно, ожидаете, порядок операторов журнала будет:
Start loading bookmarks
Returning loaded bookmarks
Done loading bookmarks
У вас есть два варианта решения асинхронной природы этой загрузки:
-
раздавить асинхронную ошибку (обычно сопровождаемую бормотанием фраз вроде: "это была ошибка, эти люди не знают, что делают")
-
обнять асинхронного зверя (обычно сопровождаемого проклятиями в течение нескольких часов, но через некоторое время мирными и лучше управляемыми приложениями)
Возьми синюю таблетку - сделай асинхронный вызов синхронным
Если вы хотите выбрать первый вариант, хорошо выполненный примитив синхронизации поможет вам:
public void loadModelWithDataFromFirebase() throws InterruptedException {
Firebase db = new Firebase(//url);
Firebase bookmarksRef = fb.child(//access correct child);
Semaphore semaphore = new Semaphore(0);
final ArrayList<Bookmark> loadedBookmarks = new ArrayList<Bookmark>();
bookmarksRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Bookmark bookmark = new Bookmark(//properties here);
loadedBookmarks.add(bookmark);
semaphore.release();
}
@Override
public void onCancelled(FirebaseError firebaseError) { throw firebaseError.toException(); }
});
semaphore.acquire();
setBookmarks(loadedBookmarks);
}
Обновление (20160303): когда я только что проверил это на Android, оно заблокировало мое приложение. Он работает на обычной JVM, но Android более требователен, когда дело доходит до многопоточности. Не стесняйтесь попробовать и заставить это работать... или
Возьмите красную таблетку - разберитесь с асинхронной природой синхронизации данных в Firebase
Если вместо этого вы решите использовать асинхронное программирование, вам следует пересмотреть логику своего приложения.
В настоящее время у вас есть "Сначала загрузите закладки. Затем загрузите данные образца. А затем загрузите еще больше".
При использовании модели асинхронной загрузки вы должны подумать: "Всякий раз, когда закладки загружены, я хочу загрузить данные образца. Когда загружаются данные образца, я хочу загрузить еще больше".
Преимущество такого подхода состоит в том, что он также работает, когда данные могут постоянно изменяться и, следовательно, синхронизироваться несколько раз: "Когда меняются закладки, я хочу также загружать данные образца. Когда меняются данные образца, я хочу загружать даже данные". Больше."
В коде это приводит к вложенным вызовам или цепочкам событий:
public void synchronizeBookmarks(){
Firebase db = new Firebase(//url);
Firebase bookmarksRef = fb.child(//access correct child);
final ArrayList<Bookmark> loadedBookmarks = new ArrayList<Bookmark>();
bookmarksRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Bookmark bookmark = new Bookmark(//properties here);
loadedBookmarks.add(bookmark);
setBookmarks(loadedBookmarks);
loadSampleData();
}
@Override
public void onCancelled(FirebaseError firebaseError) { throw firebaseError.toException(); }
});
}
В приведенном выше коде мы не просто ждем события с одним значением, мы имеем дело со всеми из них. Это означает, что всякий раз, когда закладки изменяются, выполняется onDataChange
и мы (повторно) загружаем пример данных (или любое другое действие, соответствующее потребностям вашего приложения).
Чтобы сделать код более пригодным для повторного использования, вы можете захотеть определить свой собственный интерфейс обратного вызова вместо вызова точного кода в onDataChange
. Посмотрите на этот ответ для хорошего примера этого.