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
相关产品推荐
相关产品推荐

