Android:如何在两个并行网络线程完成后触发事件展示数据
嘿,这个需求其实挺常见的,我给你几个靠谱的实现方案,你可以根据自己的项目情况来选:
方案一:用CountDownLatch(最直接的原生Java实现)
CountDownLatch 就是专门用来解决这种“等待多个任务完成后再执行后续逻辑”的场景。我们初始化一个计数为2的 latch,每个线程完成网络请求后调用 countDown() 把计数减1,然后专门开一个线程等待计数归0,之后再回到UI线程更新数据。
代码示例:
// 在Activity中初始化CountDownLatch,计数设置为2(对应两个网络线程) CountDownLatch latch = new CountDownLatch(2); Thread firstNetworkCallThread = new Thread(new Runnable() { @Override public void run() { try { // 执行第一个网络请求,获取数据... // 假设这里已经拿到了firstData } catch (Exception e) { e.printStackTrace(); // 即使请求出错,也要调用countDown,避免latch一直等待 } finally { latch.countDown(); // 完成后计数减1 } } }); Thread secondNetworkCallThread = new Thread(new Runnable() { @Override public void run() { try { // 执行第二个网络请求,获取数据... // 假设这里已经拿到了secondData } catch (Exception e) { e.printStackTrace(); } finally { latch.countDown(); // 完成后计数减1 } } }); firstNetworkCallThread.start(); secondNetworkCallThread.start(); // 必须新开一个线程等待,绝对不能在UI线程调用await!否则会卡死界面 new Thread(() -> { try { latch.await(); // 阻塞等待,直到计数变为0(两个线程都完成) // 两个请求都搞定了,回到UI线程展示数据 runOnUiThread(() -> { // 这里写展示所有数据的逻辑,比如把两个请求的数据合并后展示 showCombinedData(); }); } catch (InterruptedException e) { e.printStackTrace(); } }).start();
方案二:用ExecutorService的invokeAll方法
如果觉得手动管理线程麻烦,用线程池的 invokeAll() 方法更省心——它会自动等待所有提交的任务完成,还能帮你管理线程生命周期。
代码示例:
// 创建一个固定大小为2的线程池,刚好处理两个网络请求 ExecutorService executor = Executors.newFixedThreadPool(2); // 把两个网络请求包装成Callable任务 List<Callable<Void>> tasks = new ArrayList<>(); tasks.add(() -> { // 第一个网络请求逻辑 return null; }); tasks.add(() -> { // 第二个网络请求逻辑 return null; }); // 新开线程等待所有任务完成 new Thread(() -> { try { executor.invokeAll(tasks); // 阻塞等待所有任务执行完毕 executor.shutdown(); // 任务完成后关闭线程池 // 回到UI线程更新数据 runOnUiThread(() -> { showCombinedData(); }); } catch (InterruptedException e) { e.printStackTrace(); executor.shutdownNow(); // 中断时强制关闭线程池 } }).start();
方案三:用Kotlin协程(如果项目已经迁移到Kotlin)
如果你的项目是用Kotlin开发的,那协程绝对是最优解——代码更简洁,还自带线程切换,不用手动处理 runOnUiThread。
代码示例:
// 借助lifecycleScope,自动和Activity生命周期绑定,避免内存泄漏 lifecycleScope.launch { // 在IO线程执行两个并行的网络请求 val firstDataDeferred = async(Dispatchers.IO) { // 第一个网络请求,返回数据 } val secondDataDeferred = async(Dispatchers.IO) { // 第二个网络请求,返回数据 } // 等待两个请求都完成,拿到结果 val firstData = firstDataDeferred.await() val secondData = secondDataDeferred.await() // 直接在主线程更新UI(因为lifecycleScope默认在主线程启动) showCombinedData(firstData, secondData) }
小提醒
不管用哪种方案,绝对不要在UI线程中调用阻塞等待的方法(比如 latch.await() 或者 executor.invokeAll()),否则会导致界面卡顿甚至ANR!
内容的提问来源于stack exchange,提问作者Arvin Rokni




