上一篇
安卓两个子线程间通信
- 行业动态
- 2025-04-24
- 7
安卓子线程间通信可通过Handler传Message或共享变量加锁
安卓子线程间通信的实现方式
基于Handler与Looper的通信
原理:
每个子线程创建自己的Looper
和Handler
,通过Message
对象在不同线程的Handler
之间传递数据,需确保两个线程持有对方的Handler
引用。
实现步骤:
线程1创建Looper和Handler:
class Thread1 extends Thread { private Handler handler; public void setHandler(Handler h) { this.handler = h; } @Override public void run() { Looper.prepare(); // 创建消息队列 handler = new Handler(Looper.myLooper()) { @Override public void handleMessage(Message msg) { // 处理来自线程2的消息 String data = msg.getData().getString("key"); Log.d("Thread1", "收到消息: " + data); } }; Looper.loop(); // 启动循环 } }
线程2发送消息到线程1:
class Thread2 extends Thread { private Handler targetHandler; public void setTargetHandler(Handler h) { this.targetHandler = h; } @Override public void run() { // 模拟延迟后发送消息 try { Thread.sleep(1000); } catch (InterruptedException e) {} Message msg = Message.obtain(); Bundle bundle = new Bundle(); bundle.putString("key", "Hello from Thread2"); msg.setData(bundle); targetHandler.sendMessage(msg); // 发送消息到线程1 } }
主线程启动并绑定Handler:
Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); thread2.start(); thread2.setTargetHandler(thread1.handler); // 线程2持有线程1的Handler
优点:
- 支持复杂消息(含
what
、arg
、obj
、Bundle
)。 - 可处理异步任务回调。
缺点:
- 需手动管理
Handler
引用,耦合度高。 - 需为每个线程创建
Looper
,增加资源开销。
基于BlockingQueue的通信
原理:
使用线程安全的BlockingQueue
(如LinkedBlockingQueue
)作为共享队列,一个线程生产数据,另一个线程消费数据。
实现步骤:
定义共享队列:
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
线程1生产数据:
new Thread(() -> { try { queue.put("Message from Thread1"); // 阻塞式放入 } catch (InterruptedException e) {} }).start();
线程2消费数据:
new Thread(() -> { try { String data = queue.take(); // 阻塞式取出 Log.d("Thread2", "收到数据: " + data); } catch (InterruptedException e) {} }).start();
优点:
- 简单高效,无需额外依赖。
- 天然线程安全,避免手动同步。
缺点:
- 仅支持单一方向通信(需双向需两个队列)。
- 无法携带复杂数据类型(需序列化)。
基于EventBus的通信
原理:
使用事件总线(如GreenRobot EventBus
),子线程通过post
发布事件,其他线程通过subscribe
监听事件。
实现步骤:
添加依赖:
implementation 'org.greenrobot:eventbus:3.3.1'
定义事件类:
public class MessageEvent { public final String message; public MessageEvent(String message) { this.message = message; } }
线程1发布事件:
EventBus.getDefault().post(new MessageEvent("Hello from Thread1"));
线程2订阅事件:
public class Thread2 extends Thread { @Subscribe public void onMessage(MessageEvent event) { Log.d("Thread2", "收到事件: " + event.message); } @Override public void run() { EventBus.getDefault().register(this); // 注册订阅者 // 执行任务... } }
优点:
- 解耦发布与订阅逻辑。
- 支持多种事件类型和优先级。
缺点:
- 需引入第三方库。
- 事件处理顺序依赖注册顺序。
方法对比表
方法 | 复杂度 | 性能 | 数据类型支持 | 依赖第三方库 | 适用场景 |
---|---|---|---|---|---|
Handler/Looper | 高 | 中等 | 任意 | 无 | 需消息队列管理 |
BlockingQueue | 低 | 高 | 需序列化 | 无 | 简单数据传递 |
EventBus | 中 | 中 | 任意 | 是 | 复杂事件解耦 |
wait/notify | 高 | 低 | 任意 | 无 | 需精确同步控制 |
相关问题与解答
问题1:如何在多个子线程之间实现广播通信?
解答:
可使用EventBus
或自定义BroadcastReceiver
,所有子线程注册EventBus
订阅者,任一线程通过EventBus.getDefault().post(event)
发布事件,其他线程自动接收,若需用系统广播,需在Manifest
注册BroadcastReceiver
,但需注意静态注册可能导致内存泄漏。
问题2:使用Handler进行线程间通信时,如何避免内存泄漏?
解答:
- 移除消息回调:在线程结束前调用
handler.removeCallbacksAndMessages(null)
清理未处理消息。 - 弱引用Handler:使用
WeakReference
包裹Handler
,避免隐式持有Activity/Context引用。 - 及时解绑:在线程销毁时置空对其他线程
Handler
的引用,防止悬挂消息