Java 如何作为方法的结果返回DataSnapshot值?
我对Java没有太多经验。我不确定这个问题是否愚蠢,但我需要从Firebase实时数据库中获取一个用户名,并返回此名称作为此方法的结果。所以,我知道了如何获得这个值,但我不知道如何将它作为这个方法的结果返回。最好的方法是什么Java 如何作为方法的结果返回DataSnapshot值?,java,android,firebase,firebase-realtime-database,Java,Android,Firebase,Firebase Realtime Database,我对Java没有太多经验。我不确定这个问题是否愚蠢,但我需要从Firebase实时数据库中获取一个用户名,并返回此名称作为此方法的结果。所以,我知道了如何获得这个值,但我不知道如何将它作为这个方法的结果返回。最好的方法是什么 private String getUserName(String uid) { databaseReference.child(String.format("users/%s/name", uid)) .addListenerForSing
private String getUserName(String uid) {
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);
}
@Override
public void onCancelled(DatabaseError databaseError) {}
});
}
我相信我理解你的要求。尽管您说希望从fetch方法“返回”它(本身),但可以说您实际上只是希望能够在fetch完成后使用检索到的值。如果是这样,您需要做的就是:
yournamerable
的类中触发需要运行的任何其他命令(并且您可以确保它yournamerable
不为null)
4b编辑:
作为使用示例,您可以在由按钮的onClickListener触发的函数中使用该变量
试试这个
// 1. Create a variable at the top of your class
private String yourNameVariable;
// 2. Retrieve your value (which you have done mostly correctly)
private void getUserName(String uid) {
databaseReference.child(String.format("users/%s/name", uid))
.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// 3. Set the public variable in your class equal to value retrieved
yourNameVariable = dataSnapshot.getValue(String.class);
// 4a. EDIT: now that your fetch succeeded, you can trigger whatever else you need to run in your class that uses `yourNameVariable`, and you can be sure `yourNameVariable` is not null.
sayHiToMe();
}
@Override
public void onCancelled(DatabaseError databaseError) {}
});
}
// (part of step 4a)
public void sayHiToMe() {
Log.d(TAG, "hi there, " + yourNameVariable);
}
// 4b. use the variable in a function triggered by the onClickListener of a button.
public void helloButtonWasPressed() {
if (yourNameVariable != null) {
Log.d(TAG, "hi there, " + yourNameVariable);
}
}
然后,您可以在整个课程中的任何地方使用yourNameVariable
注意:请确保在使用时检查
yourNameVariable
是否为空,因为onDataChange
是异步的,在您尝试在其他地方使用它时可能尚未完成。我相信我理解您的要求。尽管您说希望从fetch方法“返回”它(本身),但可以说您实际上只是希望能够在fetch完成后使用检索到的值。如果是这样,您需要做的就是:
yournamerable
的类中触发需要运行的任何其他命令(并且您可以确保它yournamerable
不为null)
4b编辑:
作为使用示例,您可以在由按钮的onClickListener触发的函数中使用该变量
试试这个
// 1. Create a variable at the top of your class
private String yourNameVariable;
// 2. Retrieve your value (which you have done mostly correctly)
private void getUserName(String uid) {
databaseReference.child(String.format("users/%s/name", uid))
.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// 3. Set the public variable in your class equal to value retrieved
yourNameVariable = dataSnapshot.getValue(String.class);
// 4a. EDIT: now that your fetch succeeded, you can trigger whatever else you need to run in your class that uses `yourNameVariable`, and you can be sure `yourNameVariable` is not null.
sayHiToMe();
}
@Override
public void onCancelled(DatabaseError databaseError) {}
});
}
// (part of step 4a)
public void sayHiToMe() {
Log.d(TAG, "hi there, " + yourNameVariable);
}
// 4b. use the variable in a function triggered by the onClickListener of a button.
public void helloButtonWasPressed() {
if (yourNameVariable != null) {
Log.d(TAG, "hi there, " + yourNameVariable);
}
}
然后,您可以在整个课程中的任何地方使用yourNameVariable
注意:请确保在使用时检查
yourNameVariable
是否为空,因为onDataChange
是异步的,并且在尝试在其他地方使用它时可能尚未完成。这是异步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
的原因
大多数开发人员的最初反应是尝试“修复”这一异步行为,我个人建议不要这样做。web是异步的,您越早接受这一点,就越早学会如何使用现代web 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) {}
});
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(new MyCallback() {
@Override
public void onCallback(String value) {
Log.d("TAG", value);
}
});
如果你想在外面使用,还有另一种方法。您需要创建自己的回调以等待Firebase返回数据。要实现这一点,首先需要创建一个接口,如下所示:
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) {}
});
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(new MyCallback() {
@Override
public void onCallback(String value) {
Log.d("TAG", value);
}
});
然后,您需要创建一个从数据库中实际获取数据的方法。此方法应如下所示:
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) {}
});
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(new MyCallback() {
@Override
public void onCallback(String value) {
Log.d("TAG", value);
}
});
最后,只需调用readData()
方法,并在需要时将MyCallback
接口的实例作为参数传递,如下所示:
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) {}
});
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(new MyCallback() {
@Override
public void onCallback(String value) {
Log.d("TAG", value);
}
});
这是在onDataChange()
方法之外使用该值的唯一方法。有关更多信息,您还可以查看此
编辑:2021年2月26日
有关更多信息,您可以查看以下文章:
以及以下短片:
这是异步web API的经典问题。你不能重新开始