当前位置:首页 > 行业动态 > 正文

安卓两个子线程间通信

安卓子线程间通信可通过Handler传Message或共享变量加锁

安卓子线程间通信的实现方式

基于Handler与Looper的通信

原理
每个子线程创建自己的LooperHandler,通过Message对象在不同线程的Handler之间传递数据,需确保两个线程持有对方的Handler引用。

实现步骤

  1. 线程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. 线程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
        }
    }
  3. 主线程启动并绑定Handler

    Thread1 thread1 = new Thread1();
    Thread2 thread2 = new Thread2();
    thread1.start();
    thread2.start();
    thread2.setTargetHandler(thread1.handler); // 线程2持有线程1的Handler

优点

  • 支持复杂消息(含whatargobjBundle)。
  • 可处理异步任务回调。

缺点

  • 需手动管理Handler引用,耦合度高。
  • 需为每个线程创建Looper,增加资源开销。

基于BlockingQueue的通信

原理
使用线程安全的BlockingQueue(如LinkedBlockingQueue)作为共享队列,一个线程生产数据,另一个线程消费数据。

实现步骤

  1. 定义共享队列

    BlockingQueue<String> queue = new LinkedBlockingQueue<>();
  2. 线程1生产数据

    new Thread(() -> {
        try {
            queue.put("Message from Thread1"); // 阻塞式放入
        } catch (InterruptedException e) {}
    }).start();
  3. 线程2消费数据

    new Thread(() -> {
        try {
            String data = queue.take(); // 阻塞式取出
            Log.d("Thread2", "收到数据: " + data);
        } catch (InterruptedException e) {}
    }).start();

优点

  • 简单高效,无需额外依赖。
  • 天然线程安全,避免手动同步。

缺点

  • 仅支持单一方向通信(需双向需两个队列)。
  • 无法携带复杂数据类型(需序列化)。

基于EventBus的通信

原理
使用事件总线(如GreenRobot EventBus),子线程通过post发布事件,其他线程通过subscribe监听事件。

实现步骤

  1. 添加依赖

    implementation 'org.greenrobot:eventbus:3.3.1'
  2. 定义事件类

    public class MessageEvent {
        public final String message;
        public MessageEvent(String message) {
            this.message = message;
        }
    }
  3. 线程1发布事件

    EventBus.getDefault().post(new MessageEvent("Hello from Thread1"));
  4. 线程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进行线程间通信时,如何避免内存泄漏?

解答

  1. 移除消息回调:在线程结束前调用handler.removeCallbacksAndMessages(null)清理未处理消息。
  2. 弱引用Handler:使用WeakReference包裹Handler,避免隐式持有Activity/Context引用。
  3. 及时解绑:在线程销毁时置空对其他线程Handler的引用,防止悬挂消息
0