如何返回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) {}
    });
}

答案 1

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

但是,不仅 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!");
}

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

在附加监听器之前!

附加监听器后!

Inside onDataChange() method!

这可能不是您所期望的,但它准确地解释了为什么您的数据在返回时会如此。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()

编辑:2月 26th, 2021

有关详细信息,可以查看以下文章:

以及以下视频:


答案 2

“19.6.0”版本开始,Firebase 实时数据库 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);
    }
});

有关详细信息,可以查看以下文章:

以及以下视频:


推荐