如何由于方法返回DataSnapshot值?

发布于 2025-02-13 23:22:48 字数 586 浏览 0 评论 0原文

我对爪哇没有太多经验。我不确定这个问题是否很愚蠢,但是我需要从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) {}
    });
}

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?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

静赏你的温柔 2025-02-20 23:22:50

这是异步网络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!");
}

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

附加听众!

附加侦听器!

indataChange()方法!

这可能不是您所期望的,而是确切地解释了为什么您的数据在返回时为null

大多数开发人员的最初响应是尝试“修复”此异步行为,我个人建议反对这一点。网络是异步的,您越早接受您越早学习如何使用现代网络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返回数据。为了实现这一目标,首先,您需要创建一个接口这样:

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日,

有关更多信息,您可以查看以下文章:

和以下视频:

This is a classic issue with asynchronous web APIs. You cannot return something now that hasn't been loaded yet. In 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.

But not only Firebase Realtime Database loads data asynchronously, but almost all modern web APIs also 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. In other words, by the time onDataChange() method is called your data is not loaded yet.

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 will be:

Before attaching the listener!

After attaching the listener!

Inside onDataChange() method!

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

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.

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:

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 interface like this:

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 MyCallback interface as an argument wherever you need it like this:

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.

Edit: Feb 26th, 2021

For more info, you can check the following article:

And the following video:

吾家有女初长成 2025-02-20 23:22:50

添加了databasereference#get()和查询#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);
    }
});

有关更多信息,有关更多信息,更多信息,您可以检查以下文章:

和以下视频:

Starting from the "19.6.0" version, the Firebase Realtime Database SDK contains a new method called get(), that can be called either on a DatabaseReference or a Query object:

Added DatabaseReference#get() and Query#get(), which return data from the server even when older data is available in the local cache.

As we already know, Firebase API is asynchronous. So we need to create a callback for that. First, let's create an interface:

public interface FirebaseCallback {
    void onResponse(String name);
}

And a method that takes as an argument an object of 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());
            }
        }
    });
}

Now, to read the data, you need to simply call the above method passing as an argument an object of type FirebaseCallback:

readFirebaseName(new FirebaseCallback() {
    @Override
    public void onResponse(String name) {
        Log.d("TAG", name);
    }
});

For more info, you can check the following article:

And the following video:

东走西顾 2025-02-20 23:22:50

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

  1. 在类的顶部创建一个变量
  2. 可检索您的价值(您最正确地完成的)
  3. 将公共变量设置在类中的公共变量等于

一旦获取成功,就可以检索到的值,您可以做到。许多变量的东西。 4a和4b是一些简单的示例:

4A。 编辑:
作为使用的示例,您可以触发使用yournamevariable的其他所有需要运行的其他内容(并且您可以确定它yournamevariable 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);
  }
}

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


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

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:

  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

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

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 yourNameVariable not null)

4b. Edit:
As an example of use, you can use the variable in a function that is triggered by a button's 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 yourNameVariable wherever you would like throughout your class.


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

纵情客 2025-02-20 23:22:50

这是一个疯狂的主意,在OnDataChange内部,将其放入文本视图中,可见度消失了
textview.setvisiblity(gone)或其他
然后执行类似的操作

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

,然后使用textView.getText()。toString()

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

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

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

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

just a crazy simple Idea.

国产ˉ祖宗 2025-02-20 23:22:50

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

然后,从您的活动/片段观察livedata和内部on Changed执行您所需的操作。

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

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

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/Fragment observe the LiveData and inside onChanged do your desired operation.

getUserName().observe(this, new Observer<String>() {
    @Override
    public void onChanged(String userName) {
        //here, do whatever you want on `userName`
    }
});
童话里做英雄 2025-02-20 23:22:50

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

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

在异步方法

aPublicVariable = (String) dataSnapshot.getValue();
     

中设置此变量,如果异步调用不长,则在第二种方法中使用变量

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

它几乎一直工作。

This is NOT a 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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文