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

标签 java android firebase firebase-realtime-database

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

最佳答案

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

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

Before attaching the listener!

After attaching the listener!

Inside onDataChange() method!

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

大多数开发人员的最初 react 是尝试“修复”这个异步行为,我个人建议不要这样做。网络是异步的,您越早接受这一点,您就能越早了解如何使用现代网络 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 接口(interface)的实例作为参数传递到您需要的任何地方,如下所示:

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

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

编辑: 2021 年 2 月 26 日

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

还有以下视频:

关于java - 如何作为方法的结果返回 DataSnapshot 值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47847694/

相关文章:

android - Dagger 2 注入(inject)多个接口(interface)和实现类

java - 这是递归的正确使用吗?

java - 如何将 JPA 关系添加到持久化实体的属性子集列表?

java - Comparator/able 中的 equals() 与 compareTo()(理论)

android - 带有 "app:showAsAction="ifRoom|collapseActionView""的 SearchView 是否有错误?

ios - 如何攻击 Firebase 数据库规则?

java - 一种在设备上保存 Arraylist 的方法?

android - 找到 : combining multiple "-exec" statements not working with toybox/Android?

android - FirebaseRecyclerAdapter 不自动更新数据

javascript - Firebase 事务不会更新 Firebase 数据库中的实际值