等待 Firebase 异步回调在循环内完成

时间:2020-12-22 08:01:16

标签: java android firebase asynchronous firebase-realtime-database

我正在尝试获取分散在 Firebase 实时数据库中不同路径中的一堆数据。我运行了 n 次 for 循环,在每一次迭代中,数据库的路径都是根据某种逻辑确定的。

然后我创建一个 Firebase 查询(将每次迭代中每个路径的 Firebase 子响应限制为 1)并为其附加一个 addListenerForSingleValueEvent。在 onDataChange 内部,我使用在 for 循环中声明为 final 的变量(索引)将接收到的数据元素附加到固定大小的列表(X)。这样,索引变量会跟踪对数据库的所有 n 次调用,就好像它们并行运行一样,我可以将此列表 X 映射到其他几个列表。到目前为止一切顺利。

现在,一旦这 n 个异步回调完成返回响应,我想调用自定义接口回调将此数据发送到程序的某些部分。

但显然,for 循环不会等待这 n 个网络操作结束,并且我的 List X 在我尝试调用自定义回调接口的实例中保持为空。

在完成 n 次数据库调用后,如何调用我的自定义接口回调?

Structure of DB

代码是这样的:

.
.
.

for (int i = 0; i < n; i++) {
            final int index = i;

.
.
.
            //Y and X = some variable that changes every loop
            //countryCode = some variable that changes every loop

            Query query = mReference.child(Y + "/" + countryCode + "/" + X).limitToFirst(1);
            query.addListenerForSingleValueEvent(new ValueEventListener() {

                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {
                    for (DataSnapshot D : snapshot.getChildren()) {
                        <DataType> n = D.getValue(DataType.class);
                        keys.add(D.getKey());
                        X.set(index, n);
                    }
                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {
                }
            });
        } //end of for loop

       //Control reaches here before callbacks are over
       //Want to call custom callback here once all data is received

1 个答案:

答案 0 :(得分:3)

实现您想要的一种方法是使用 Future。您可以等待直到所有响应都返回,将它们包装在一个大 CompletableFuture<Boolean> 中。在您上面写的内容之上的一个粗略示例:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

...

private CompletableFuture<Boolean> queryDatabaseAsync() {

        CompletableFuture<Boolean> future = new CompletableFuture<>();

        // if at least one fails then flag will switch
        AtomicBoolean isSuccess = new AtomicBoolean(true);
        AtomicInteger stack = new AtomicInteger(n);

        for (int i = 0; i < n; i++) {
            final int index = i;
            Query query = ...;
            query.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {
                    ...
                    // finish if last
                    if (stack.decrementAndGet() < 1) {
                        future.complete(isSuccess.get());
                    }
                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {
                    ...
                    isSuccess.set(false);
                    // finish if last
                    if (stack.decrementAndGet() < 1) {
                        future.complete(isSuccess.get());
                    }
                }
            });
            
        }

        return future;
}

private void hitDatabase() {

    if (myFuture != null) {
        myFuture.cancel(true);
        myFuture = null;
    }

    myFuture = queryDatabaseAsync();
    myFuture.thenAccept(isSuccess -> {
        // yay!
    });
}