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

新手上路,请多包涵

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

原文由 Ilya S 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 432
2 个回答

这是异步 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

大多数开发人员的最初反应是尝试“修复”这个 asynchronous behavior ,我个人建议不要这样做。 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) {}
});

如果你想在外面使用它,还有另一种方法。您需要创建自己的回调以等待 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() 方法之外使用该值的唯一方法。有关更多信息,您还可以观看此 视频

编辑: 2021 年 2 月 26 日

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

以及以下视频:

原文由 Alex Mamo 发布,翻译遵循 CC BY-SA 4.0 许可协议

“19.6.0” 版本开始,Firebase 实时数据库 SDK 包含一个名为 get() 的新方法,可以在 DatabaseReference 或 Query 对象上调用:

添加了 DatabaseReference#get() 和 Query#get(),即使本地缓存中有旧数据,它们也会从服务器返回数据。

正如我们所知,Firebase API 是异步的。所以我们需要为此创建一个回调。首先,让我们创建一个接口:

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

以及一个将 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);
    }
});

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

以及以下视频:

原文由 Alex Mamo 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题