Firebase加载并同步数据异步地。所以你的loadModelWithDataFromFirebase()
不等待加载完成,它只是starts从数据库加载数据。到时候你的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 error) { throw error.toException(); }
});
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 error) { throw error.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 error) { throw error.toException(); }
});
}
在上面的代码中,我们不只是等待单个值事件,而是处理所有事件。这意味着每当书签发生更改时,onDataChange
被执行,我们(重新)加载示例数据(或任何其他适合您的应用程序需求的操作)。
为了使代码更可重用,您可能需要定义自己的回调接口,而不是调用中的精确代码onDataChange
。看一下这个答案 https://stackoverflow.com/a/50435519就是一个很好的例子。