You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动