Java 如何作为方法的结果返回 DataSnapshot 值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47847694/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 02:41:54  来源:igfitidea点击:

How to return DataSnapshot value as a result of a method?

javaandroidfirebasefirebase-realtime-database

提问by Ilya Cucumber

I don't have much experience with Java. I'm not sure if this question is stupid, but I need to get a user name from Firebase realtime database and return this name as a result of this method. So, I figured out how to get this value, but I don't understand how to return it as result of this method. What's the best way to do this?

我对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) {}
    });
}

采纳答案by Alex Mamo

This is a classic issue with asynchronous web APIs. You cannot return something now that hasn't been loaded yet. With other words, you cannot simply create a global variable and use it outside onDataChange()method because it will always be null. This is happening because onDataChange()method is called asynchronous. Depending on your connection speed and the state, it may take from a few hundred milliseconds to a few seconds before that data is available.

这是异步 Web API 的经典问题。您现在无法返回尚未加载的内容。换句话说,您不能简单地创建一个全局变量并在onDataChange()方法之外使用它,因为它始终是null. 这是因为onDataChange()方法被称为异步。根据您的连接速度和状态,在该数据可用之前可能需要几百毫秒到几秒。

But not only Firebase Realtime Database loads data asynchronously, almost all of modern other web APIs do, since it may take some time. So instead of waiting for the data (which can lead to unresponsive application dialogs for your users), your main application code continues while the data is loaded on a secondary thread. Then when the data is available, your onDataChange() method is called and can use the data. With other words, by the time onDataChange()method is called your data is not loaded yet.

但不仅 Firebase 实时数据库异步加载数据,几乎所有现代其他 Web API 都这样做,因为这可能需要一些时间。因此,不是等待数据(这可能导致用户的应用程序对话框无响应),而是在数据加载到辅助线程时继续主应用程序代码。然后,当数据可用时,您的 onDataChange() 方法将被调用并可以使用该数据。换句话说,在onDataChange()调用time方法时,您的数据尚未加载。

Let's take an example, by placing a few log statements in the code, to see more clearly what's going on.

我们举个例子,通过在代码中放置几条日志语句,来更清楚地看到发生了什么。

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!");
}

If we run this code will, the output wil be:

如果我们运行此代码,输出将是:

Before attaching the listener!

After attaching the listener!

Inside onDataChange() method!

在附加监听器之前!

附加监听器后!

在 onDataChange() 方法中!

This is probably not what you expected, but it explains precisely why your data is nullwhen returning it.

这可能不是您所期望的,但它准确地解释了您的数据null在返回时为何如此。

The initial response for most developers is to try and "fix" this asynchronous behavior, which I personally recommend against this. The web is asynchronous, and the sooner you accept that, the sooner you can learn how to become productive with modern web APIs.

大多数开发人员的最初反应是尝试“修复” this asynchronous behavior,我个人建议不要这样做。Web 是异步的,您越早接受这一点,您就可以越早学习如何使用现代 Web API 提高工作效率。

I've found it easiest to reframe problems for this asynchronous paradigm. Instead of saying "First get the data, then log it", I frame the problem as "Start to get data. When the data is loaded, log it". This means that any code that requires the data must be inside onDataChange()method or called from inside there, like this:

我发现为这种异步范式重构问题是最容易的。我没有说“首先获取数据,然后记录它”,而是将问题描述为“开始获取数据。加载数据时,记录它”。这意味着任何需要数据的代码都必须在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) {}
});

If you want to use that outside, there is another approach. You need to create your own callback to wait for Firebase to return you the data. To achieve this, first you need to create an interfacelike this:

如果你想在外面使用它,还有另一种方法。您需要创建自己的回调以等待 Firebase 返回数据。要实现这一点,首先你需要创建一个interface这样的:

public interface MyCallback {
    void onCallback(String value);
}

Then you need to create a method that is actually getting the data from the database. This method should look like this:

然后你需要创建一个实际从数据库中获取数据的方法。此方法应如下所示:

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) {}
    });
}

In the end just simply call readData()method and pass an instance of the MyCallbackinterface as an argument wherever you need it like this:

最后,只需简单地调用readData()方法并将MyCallback接口的实例作为参数传递到您需要的任何地方,如下所示:

readData(new MyCallback() {
    @Override
    public void onCallback(String value) {
        Log.d("TAG", value);
    }
});

This is the only way in which you can use that value outside onDataChange()method. For more information, you can take also a look at this video.

这是您可以在onDataChange()方法之外使用该值的唯一方法。有关更多信息,您还可以观看此视频

回答by Rbar

I believe I understand what you are asking. Although you say you want to "return" it (per se) from the fetch method, it may suffice to say you actually just want to be able to use the value retrieved after your fetch has completed. If so, this is what you need to do:

我相信我明白你在问什么。尽管您说您想从 fetch 方法“返回”它(本身),但可以说您实际上只是希望能够使用在您的 fetch 完成后检索到的值。如果是这样,这就是你需要做的:

  1. Create a variable at the top of your class
  2. Retrieve your value (which you have done mostly correctly)
  3. Set the public variable in your class equal to value retrieved
  1. 在类的顶部创建一个变量
  2. 检索你的价值(你大部分都做对了)
  3. 将类中的公共变量设置为等于检索到的值

Once your fetch succeeds, you could do many things with the variable. 4a and 4b are some simple examples:

获取成功后,您可以使用该变量做很多事情。图 4a 和 4b 是一些简单的例子:

4a. Edit:As an example of use, you can trigger whatever else you need to run in your class that uses yourNameVariable(and you can be sure it yourNameVariablenot null)

4a. 编辑:作为使用示例,您可以触发在使用的类中运行所需的任何其他内容yourNameVariable(并且您可以确定它yourNameVariable不为空)

4b. Edit:As an example of use, you can use the variable in a function that is triggered by a button's onClickListener.

4b. 编辑:作为使用示例,您可以在由按钮的 onClickListener 触发的函数中使用该变量。



Try this.

尝试这个。

// 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);
  }
}

Then, you can use yourNameVariablewherever you would like throughout your class.

然后,您可以yourNameVariable在整个课程中使用任何您想要的地方。



Note: just be sure you check that yourNameVariableis not null when using it since onDataChangeis asynchronous and may not have completed at the time you attempt to use it elsewhere.

注意:请确保yourNameVariable在使用它时检查它不为空,因为它onDataChange是异步的,并且在您尝试在其他地方使用它时可能尚未完成。

回答by klaid bendio Moran

Here's a crazy Idea, inside onDataChange, put it inside a TextView with visibility gone textview.setVisiblity(Gone)or something, then do something like

这是一个疯狂的想法,在 onDataChange 里面,把它放在一个可见性消失的 TextView 中 textview.setVisiblity(Gone),然后做一些类似的事情

textview.setText(dataSnapshot.getValue(String.class))

then later get it with textview.getText().toString()

然后后来得到它 textview.getText().toString()

just a crazy simple Idea.

只是一个疯狂的简单想法。

回答by DragonFire

This is solution, just a way to access the data outside the method for code organization.

这是解决方案,只是一种在代码组织方法之外访问数据的方法。

// 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);

}

Another way of achieving result (but not necessarily a solution)

实现结果的另一种方式(但不一定是解决方案)

Declare a public variable

声明一个公共变量

public static String aPublicVariable;

Set This Variable Inside The Async Method

在 Async 方法中设置此变量

aPublicVariable = (String) dataSnapshot.getValue();

Use The Variable Anywhere

随处使用变量

Log.d(TAG, "Not Elegant: " + aPublicVariable);

In the second method if the async call is not long it will nearly work all the time.

在第二种方法中,如果异步调用时间不长,它几乎可以一直工作。

回答by Md. Asaduzzaman

Use LiveDataas return type and observe the changes of it's value to execute desired operation.

使用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;
}

Then from your Activity/Fragmentobserve the LiveDataand inside onChangeddo your desired operation.

然后从你的Activity/Fragment观察LiveData和里面onChanged做你想要的操作。

getUserName().observe(this, new Observer<String>() {
    @Override
    public void onChanged(String userName) {
        //here, do whatever you want on `userName`
    }
});