You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio Firebase开发中如何等待循环内所有异步任务完成再返回结果

问题原因分析

1. 初始版本错误原因

你写的触发回调判断条件为if (nbDone.get()==membersUid.size()-1),假设列表有3个元素,当第二个任务完成时计数就等于2(即size-1),此时就会触发回调,第三个任务还未执行完成,所以输出里会残留未转换的UID。
另外你使用AtomicReference<Integer>做计数没必要,直接用AtomicInteger更适配整型计数场景。

2. CountDownLatch版本错误原因

  • 第一层问题:getPlayerPseudoByUid本身就是异步方法,你在doWork里调用它之后没等回调返回就直接执行了doneSignal.countDown(),计数减一的时机和异步任务实际完成完全脱节,自然会提前结束等待。
  • 第二层问题:把doneSignal.countDown()放到回调里会崩溃,是因为你在主线程执行了doneSignal.await(),Android主线程不允许阻塞,一旦阻塞会触发ANR,而且Firebase的回调默认会投递到主线程执行,主线程卡着等待await释放,回调永远得不到执行,直接触发死锁崩溃。
正确实现方案

方案1:修复Atomic计数版本(最轻量修改)

public void convertListOfUidInPseudo(List<String> membersUid, convertListOfUidInPseudoListener callback){
    int total = membersUid.size();
    // 空列表直接返回
    if (total == 0) {
        callback.onCallbackConvertListOfUidInPseudo(new ArrayList<>());
        return;
    }
    AtomicInteger nbDone = new AtomicInteger(0);
    // 新建列表存储结果,不要修改传入的原始列表,避免副作用
    List<String> pseudoResult = new ArrayList<>(membersUid);
    for (int i=0; i<total; i++){
        int finalI = i;
        getPlayerPseudoByUid(membersUid.get(i), playerPseudo -> {
            pseudoResult.set(finalI, playerPseudo);
            int doneCount = nbDone.incrementAndGet();
            System.out.println(playerPseudo);
            // 完成数等于总任务数再触发回调
            if (doneCount == total){
                callback.onCallbackConvertListOfUidInPseudo(pseudoResult);
                System.out.println(pseudoResult);
            }
        });
    }
}

方案2:使用Firebase原生Task工具(更规范)

Firebase提供了Tasks.whenAllSuccess方法,可以批量等待多个异步任务完成,代码更易维护:

public void convertListOfUidInPseudo(List<String> membersUid, convertListOfUidInPseudoListener callback){
    int total = membersUid.size();
    if (total == 0) {
        callback.onCallbackConvertListOfUidInPseudo(new ArrayList<>());
        return;
    }
    List<Task<QuerySnapshot>> tasks = new ArrayList<>();
    for (String uid : membersUid) {
        tasks.add(getAllUsersDatas().whereEqualTo(UID_FIELD, uid).get());
    }
    Tasks.whenAllSuccess(tasks).addOnSuccessListener(results -> {
        List<String> pseudoResult = new ArrayList<>();
        for (Object result : results) {
            QuerySnapshot snapshot = (QuerySnapshot) result;
            pseudoResult.add((String) snapshot.getDocuments().get(0).get(PSEUDO_FIELD));
        }
        callback.onCallbackConvertListOfUidInPseudo(pseudoResult);
    });
}
注意事项
  • 建议给getPlayerPseudoByUid添加失败回调处理,避免某个请求失败后计数永远到不了总任务数,导致回调永远不触发。
  • 不要在Android主线程执行任何阻塞操作(比如CountDownLatch的await),会直接导致应用无响应。

内容的提问来源于stack exchange,提问作者AnchorInk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 15:54:01