如何通过方法返回DataSnapshot值?

Ily*_*ber 33 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) {}
    });
}
Run Code Online (Sandbox Code Playgroud)

Ale*_*amo 70

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

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

让我们举一个例子,在代码中放置一些日志语句,以便更清楚地了解正在发生的事情.

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!");
}
Run Code Online (Sandbox Code Playgroud)

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

在连接听众之前!

附上听众后!

在onDataChange()方法里面!

这可能不是您所期望的,但它确切地解释了null返回时数据的原因.

大多数开发人员的初始响应是尝试"修复"这个asynchronous behavior,我个人建议不要这样做.网络是异步的,越早接受,您就越早了解如何通过现代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) {}
});
Run Code Online (Sandbox Code Playgroud)

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

public interface MyCallback {
    void onCallback(String value);
}
Run Code Online (Sandbox Code Playgroud)

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

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) {}
    });
}
Run Code Online (Sandbox Code Playgroud)

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

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

这是您可以在onDataChange()方法外使用该值的唯一方法.有关详细信息,您还可以查看此视频.

  • @DragonFire 你只是将 `yourValue` 作为参数传递,你不知道数据。 (2认同)
  • @willy我不能在回调/readData()之外执行此操作,因为该操作是异步的。你总是需要等待数据。 (2认同)

Ale*_*amo 6

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

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

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

public interface FirebaseCallback {
    void onResponse(String name);
}
Run Code Online (Sandbox Code Playgroud)

以及一个以 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());
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

现在,要读取数据,您只需调用上述方法,将 FirebaseCallback 类型的对象作为参数传递:

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

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

以及以下视频: