【问题标题】:Android how to trigger a process when two async processes finish executingAndroid如何在两个异步进程完成执行时触发一个进程
【发布时间】:2020-01-23 08:33:44
【问题描述】:

我是 Android 后台任务的新手。我正在使用 Firestore 执行以下任务:

  1. 阅读文档。 https://firebase.google.com/docs/firestore/query-data/get-data

    DBInstance.collection("restaurants")
                .get()
                .addOnCompleteListener(task -> {
                    if (task.isSuccessful()) {
    
                        for (QueryDocumentSnapshot document : Objects.requireNonNull(task.getResult())) {
    
                         // some other code
    
                        }
                    } else {
                        Log.d(TAG, "Error getting documents: ", task.getException());
                    }
    
    
                });
    
  2. 收听另一个文档的实时更新。 https://firebase.google.com/docs/firestore/query-data/listen

    final DocumentReference docRef = DBInstance.collection("users").document(FirebaseAuth.getInstance().getCurrentUser().getUid());
        docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
            @Override
            public void onEvent(@Nullable DocumentSnapshot snapshot,
                                @Nullable FirebaseFirestoreException e) {
                if (e != null) {
                    Log.w(TAG, "Listen failed.", e);
                    return;
                }
    
                String source = snapshot != null && snapshot.getMetadata().hasPendingWrites()
                        ? "Local" : "Server";
    
                if (snapshot != null && snapshot.exists()) {
                    Log.d(TAG, source + " data: " + snapshot.getData());
    
                    // some other code is run
    
                } else {
                    Log.i(TAG,"no snapshot found");
                }
            }
        });
    

由于这些是异步进程,因此它们是同时执行的(大致)。

我想在 1. 完成和 2. 返回非空快照时触发一个独立的方法。因此,当上面的some other code cmets 已经完成。

所以,我本质上想要一些后台进程处于空闲状态/侦听上述两个条件并执行任务/调用更新某些 UI 功能的方法。

我已经简要了解了BroadcastReciever。这相关吗?或者我可以创建一个在后台线程中运行的自定义侦听器吗?任何建议都会有所帮助,因为我不确定要搜索什么才能找到我想要的。

似乎有效的解决方案(Nehal 部分建议)

这与上面的代码相同,但空白处填写

DBInstance.collection("restaurants")
            .get()
            .addOnCompleteListener(task -> {
                if (task.isSuccessful()) {

                    for (QueryDocumentSnapshot document : Objects.requireNonNull(task.getResult())) {

                     restaurantsLoaded = true;
                     updateUI();

                    }
                } else {
                    Log.d(TAG, "Error getting documents: ", task.getException());
                    restaurantsLoaded = false;
                }


            });


    final DocumentReference docRef = DBInstance.collection("users").document(FirebaseAuth.getInstance().getCurrentUser().getUid());
docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
    @Override
    public void onEvent(@Nullable DocumentSnapshot snapshot,
                        @Nullable FirebaseFirestoreException e) {
        if (e != null) {
            Log.w(TAG, "Listen failed.", e);
            return;
        }

        String source = snapshot != null && snapshot.getMetadata().hasPendingWrites()
                ? "Local" : "Server";

        if (snapshot != null && snapshot.exists()) {
            Log.d(TAG, source + " data: " + snapshot.getData());

            usersSnapshotTriggered = true;
            udpateUI();

        } else {
            Log.i(TAG,"no snapshot found");
        }
    }
});


public void updateUI(){
    if(usersSnapshotTriggered && restaurantsLoaded){
        // perform the updates
    }
}

【问题讨论】:

  • 你能贴一些代码吗?
  • @NehalGodhasara 添加了我的代码

标签: java android firebase asynchronous google-cloud-firestore


【解决方案1】:

您可以尝试以下解决方案: 声明一个全局 int 变量,在两个 firebase 监听器中递增该变量并从两个监听器调用 someMethod()。

private int count=0;

DBInstance.collection("restaurants")
            .get()
            .addOnCompleteListener(task -> {
                if (task.isSuccessful()) {

                    for (QueryDocumentSnapshot document : Objects.requireNonNull(task.getResult())) {

                     // some other code

                    }
                    count++;
                    someMethod();
                } else {
                    Log.d(TAG, "Error getting documents: ", task.getException());
                }


            });


final DocumentReference docRef = DBInstance.collection("users").document(FirebaseAuth.getInstance().getCurrentUser().getUid());
    docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
        @Override
        public void onEvent(@Nullable DocumentSnapshot snapshot,
                            @Nullable FirebaseFirestoreException e) {
            if (e != null) {
                Log.w(TAG, "Listen failed.", e);
                return;
            }

            String source = snapshot != null && snapshot.getMetadata().hasPendingWrites()
                    ? "Local" : "Server";

            if (snapshot != null && snapshot.exists()) {
                Log.d(TAG, source + " data: " + snapshot.getData());

                // some other code is run
                    count++;
                    someMethod();
                    //Note : this method will call as many times as there is change in this data , so you have to handle according to your requirement
            } else {
                Log.i(TAG,"no snapshot found");
            }
        }
    });


private void someMethod(){
    if(count>=2){
       //execute your code
   }
}

希望这会有所帮助!

【讨论】:

  • 这将是我的解决方案(并且可能仍然是)。不确定这是否是正确的做事方式,并且正在寻找人们建议的其他解决方案。但绝对可能是正确的答案。
  • 是的,请尝试告诉我它是否适合您。
  • 很遗憾,这不是解决此类问题的方法。该全局 int 变量将始终具有默认值。基本上,您正在尝试从异步 API 同步使用值。这不是一个好主意。您应该按预期异步处理 API。签出this
  • @AlexMamo 为什么 int 变量不会改变?一旦任务完成,增量就会生效吗?如果将 int 变量替换为布尔标志,会解决问题吗?
  • @user7331538 使用布尔变量你无法实现这一点,至于这种情况我们必须考虑变量的 3 种不同状态(初始、方法 1 状态、方法 2 状态)
【解决方案2】:

我想在1.和2.都完成后触发一个独立的方法

在您的第一个示例中,通过添加一个完整的侦听器,您将始终能够知道操作何时完成。如果task.isSuccessful() 返回true,您肯定知道操作已完成。除此之外,您还可以调用getResult() 来获取属于您的restaurants 集合的元素。此外,以下代码行:

DBInstance.collection("restaurants")
        .get()

返回一个Task&lt;QuerySnapshot&gt; 对象。如果您有两个不同的查询,您可以将两个 Task 对象传递给 Tasks 的 whenAllSuccess() 方法,正如我在以下帖子中的回答中所述:

通过这种方式,您将能够知道两个操作何时完成。但是,当使用第二种方案时,您无法知道从数据库中获取数据何时完成,因为Cloud Firestore 是一个实时数据库,可能永远无法完成获取数据。这就是为什么被称为实时数据库的原因,因为在任何时候都可以更改数据库,可以添加或删除项目。

要部分了解您是否拥有特定集合中的所有数据的唯一方法是对其执行单值类型查询。即便如此,在调用该侦听器后数据可能会发生变化,因此您真正拥有的只是某个特定时刻的snapshot

作为结论,您唯一的解决方案是使用 whenAllSuccess() 并传递两个甚至更多 Task 对象。

我已经简要了解了 BroadcastReciever。这相关吗?

不,不是。根据文档,BroadcastReceiver 类是:

接收和处理 Context.sendBroadcast(Intent) 发送的广播意图的代码基类。

所以,事实并非如此。

或者我可以创建一个在后台线程中运行的自定义侦听器吗?

Cloud Firestore 客户端已在后台线程中运行所有网络操作。这意味着所有操作都不会阻塞您的主线程。将它放在后台线程中不会带来任何额外的好处。

【讨论】:

  • so all you really have is a snapshot at a particular moment in time. 没关系。我想要的是:“如果 1. 已完成且 2. 有快照存在,请运行该方法”。我理解 2. 是实时的,每次事件发生时都会有一个快照。这正是我想要的。每次有实时快照并且 1. 完成时,我想触发一个方法(这是一个 UI 更新)。所以我认为 Nehal 的答案仍然是一个更好、更简单的解决方案。
  • 由于这些操作的异步性质,Nehal 的回答甚至不起作用。 如果 1. 已完成且 2. 存在快照,则运行该方法 然后只需将“2”移动到“1”内,就在 onComplete() 方法内。否则,您必须实施我在上面的答案中向您展示的解决方案。
猜你喜欢
  • 2016-09-09
  • 1970-01-01
  • 2016-07-19
  • 2023-04-04
  • 2017-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-30
相关资源
最近更新 更多