提问人:Ilya S 提问时间:12/17/2017 最后编辑:Alex MamoIlya S 更新时间:3/1/2021 访问量:45822
如何返回DataSnapshot值作为方法的结果?
How to return DataSnapshot value as a result of a method?
问:
我对 Java 没有太多经验。我不确定这个问题是否愚蠢,但我需要从 Firebase 实时数据库中获取用户名并返回此名称作为此方法的结果。所以,我想出了如何获取这个值,但我不明白如何通过这种方法返回它。最好的方法是什么?
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 方法中“返回”它(本身),但可以说您实际上只想在获取完成后使用检索到的值。如果是这样,这是您需要做的:
- 在类的顶部创建一个变量
- 检索您的值(您已经正确地完成了)
- 将类中的公共变量设置为等于检索到的值
一旦提取成功,就可以对变量执行许多操作。4a 和 4b 是一些简单的示例:
4a. 编辑:作为使用示例,您可以触发在使用 (并且可以确保它不是 null) 的类中运行所需的任何其他内容yourNameVariable
yourNameVariable
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
时检查它是否不为 null,因为 onDataChange
是异步的,并且在您尝试在其他地方使用它时可能尚未完成。
评论
yourNameVariable
null
onDataChange()
yourNameVariable
null
null
Log.d(TAG, "hi there, " + yourNameVariable);
onDataChange()
onDataChange()
yourNameVariable will be be always null when using "wherever you would like throughout your class".
这是异步 Web API 的典型问题。您现在无法返回尚未加载的内容。换句话说,您不能简单地创建一个全局变量并在方法外部使用它,因为它将始终是 。发生这种情况是因为方法被称为异步方法。根据您的连接速度和状态,可能需要几百毫秒到几秒钟的时间才能获得该数据。onDataChange()
null
onDataChange()
但是,不仅 Firebase Realtime Database 异步加载数据,而且几乎所有现代 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 提高工作效率。asynchronous behavior
我发现为这种异步范式重新构建问题最容易。我没有说“首先获取数据,然后记录数据”,而是将问题描述为“开始获取数据。加载数据后,将其记录下来”。这意味着任何需要数据的代码都必须位于方法内部或从方法内部调用,如下所示: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()
编辑:26 2月 2021
有关详细信息,可以查看以下文章:
以及以下视频:
评论
Future
newSingleThreadExecutor()
yourValue
这是一个疯狂的想法,在 onDataChange 中,把它放在一个 TextView 中,可见性消失了什么的,
然后做类似的事情textview.setVisiblity(Gone)
textview.setText(dataSnapshot.getValue(String.class))
然后稍后用textview.getText().toString()
只是一个疯狂的简单想法。
评论
textview
这不是一个解决方案,只是一种在代码组织方法之外访问数据的方法。
// Get Your Value
private void getValue() {
fbDbRefRoot.child("fbValue").addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
String yourValue = (String) dataSnapshot.getValue();
useValue(yourValue);
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
// Use Your Value
private void useValue(String yourValue) {
Log.d(TAG, "countryNameCode: " + yourValue);
}
实现结果的另一种方式(但不一定是解决方案)
声明公共变量
public static String aPublicVariable;
在 Async 方法中设置此变量
aPublicVariable = (String) dataSnapshot.getValue();
随时随地使用变量
Log.d(TAG, "Not Elegant: " + aPublicVariable);
在第二种方法中,如果异步调用时间不长,它几乎会一直工作。
评论
用作返回类型并观察其值的变化以执行所需的操作。LiveData
private MutableLiveData<String> userNameMutableLiveData = new MutableLiveData<>();
public MutableLiveData<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?
String userName = dataSnapshot.getValue(String.class);
userNameMutableLiveData.setValue(userName);
}
@Override
public void onCancelled(DatabaseError databaseError) {}
});
return userNameMutableLiveData;
}
然后从你的观察和内部做你想要的操作。Activity/Fragment
LiveData
onChanged
getUserName().observe(this, new Observer<String>() {
@Override
public void onChanged(String userName) {
//here, do whatever you want on `userName`
}
});
从“19.6.0”版本开始,Firebase Realtime Database SDK 包含一个名为 get() 的新方法,可以在 DatabaseReference 或 Query 对象上调用该方法:
添加了 DatabaseReference#get() 和 Query#get(),即使本地缓存中有较旧的数据,它们也会从服务器返回数据。
众所周知,Firebase API 是异步的。因此,我们需要为此创建一个回调。首先,让我们创建一个接口:
public interface FirebaseCallback {
void onResponse(String name);
}
以及一个将 tye FirebaseCallback 的对象作为参数的方法:
public void readFirebaseName(FirebaseCallback callback) {
DatabaseReference uidRef = databaseReference.child(String.format("users/%s/name", uid))
uidRef.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
@Override
public void onComplete(@NonNull Task<DataSnapshot> task) {
if (task.isSuccessful()) {
String name = task.getResult().getValue(String.class);
callback.onResponse(name);
} else {
Log.d(TAG, task.getException().getMessage());
}
}
});
}
现在,要读取数据,您只需调用上述方法,将 FirebaseCallback 类型的对象作为参数传递:
readFirebaseName(new FirebaseCallback() {
@Override
public void onResponse(String name) {
Log.d("TAG", name);
}
});
有关详细信息,可以查看以下文章:
以及以下视频:
评论
onDataChange()