这是异步 Web API 的经典问题。您现在无法退回尚未加载的内容。换句话说,您不能简单地创建一个全局变量并在onDataChange() 方法之外使用它,因为它始终是null。发生这种情况是因为 onDataChange() 方法被称为异步。根据您的连接速度和状态,数据可用可能需要几百毫秒到几秒的时间。
但不仅 Firebase 实时数据库异步加载数据,几乎所有现代 Web API 也这样做,因为这可能需要一些时间。因此,与其等待数据(这可能会导致用户的应用程序对话框无响应),不如在将数据加载到辅助线程时继续主应用程序代码。然后当数据可用时,您的 onDataChange() 方法被调用,并且可以使用数据。换句话说,在调用onDataChange() 方法时,您的数据还没有加载。
我们举个例子,通过在代码中放置一些日志语句,更清楚地看到发生了什么。
private String getUserName(String uid) {
Log.d("TAG", "Before attaching the listener!");
databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// How to return this value?
dataSnapshot.getValue(String.class);
Log.d("TAG", "Inside onDataChange() method!");
}
@Override
public void onCancelled(DatabaseError databaseError) {}
});
Log.d("TAG", "After attaching the listener!");
}
如果我们运行这段代码,输出将是:
在附加监听器之前!
附加监听器后!
onDataChange() 方法内部!
这可能不是您所期望的,但它准确地解释了为什么您的数据在返回时是null。
大多数开发人员的最初反应是尝试“修复”这个asynchronous behavior,我个人建议不要这样做。网络是异步的,您越早接受这一点,您就能越早了解如何使用现代网络 API 提高工作效率。
我发现为这种异步范式重构问题最容易。我没有说“首先获取数据,然后记录它”,而是将问题描述为“开始获取数据。加载数据后,记录它”。这意味着任何需要数据的代码都必须在 onDataChange() 方法内或从那里调用,如下所示:
databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// How to return this value?
if(dataSnapshot != null) {
System.out.println(dataSnapshot.getValue(String.class));
}
}
@Override
public void onCancelled(DatabaseError databaseError) {}
});
如果您想在外部使用它,还有另一种方法。您需要创建自己的回调以等待 Firebase 向您返回数据。为此,首先,您需要像这样创建一个interface:
public interface MyCallback {
void onCallback(String value);
}
然后您需要创建一个实际从数据库中获取数据的方法。此方法应如下所示:
public void readData(MyCallback myCallback) {
databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
myCallback.onCallback(value);
}
@Override
public void onCancelled(DatabaseError databaseError) {}
});
}
最后只需简单地调用readData() 方法并将MyCallback 接口的实例作为参数传递到您需要的任何地方,如下所示:
readData(new MyCallback() {
@Override
public void onCallback(String value) {
Log.d("TAG", value);
}
});
这是您可以在onDataChange() 方法之外使用该值的唯一方法。如需更多信息,您还可以查看此video。
编辑: 2021 年 2 月 26 日
更多信息,您可以查看以下文章:
还有以下视频: