Handle

一、Handler、Looper 和 Message 的关系解析

在 Android 的消息机制中,HandlerLooperMessage 三者协同工作,实现线程间通信和任务调度。它们的关系可以用以下核心点概括:


1. 核心组件分工
组件作用
Message消息的载体,包含 what(标识)、arg(参数)、obj(数据对象)等。
Looper消息循环的核心,不断从 MessageQueue 取出消息并分发给 Handler
Handler消息的发送者和处理者,负责将 Message 投递到队列并处理回调。

2. 协作流程
HandlerMessageQueueLooper发送 Message (sendMessage/post)轮询取出 Message (loop())回调 dispatchMessage()处理消息 (handleMessage)HandlerMessageQueueLooper
  1. 消息发送
    • Handler.sendMessage(msg)Handler.post(runnable) 将消息/任务封装为 Message,插入到 MessageQueue
  2. 消息轮询
    • Looper.loop() 无限循环,从 MessageQueue 取出消息。
  3. 消息处理
    • Looper 调用 Handler.dispatchMessage(),最终触发 handleMessage()Runnable.run()

3. 关键机制详解
(1) Looper 的准备工作
  • 每个线程需绑定一个 Looper
    // 主线程(ActivityThread)自动创建 Looper
    Looper.prepareMainLooper(); 
    
    // 子线程需手动调用
    Looper.prepare();
    Looper.loop(); // 开启消息循环
    
  • Looper 与 MessageQueue 一对一绑定
    Looper 内部通过 ThreadLocal 保证线程隔离,每个线程有独立的 MessageQueue
(2) Handler 的关联
  • Handler 绑定到 Looper
    // 默认绑定当前线程的 Looper
    Handler handler = new Handler(); 
    
    // 显式指定 Looper(如主线程的 Looper)
    Handler handler = new Handler(Looper.getMainLooper());
    
  • 消息分发逻辑
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            msg.callback.run(); // 处理 Runnable
        } else if (mCallback != null) {
            mCallback.handleMessage(msg); // 处理 Callback
        } else {
            handleMessage(msg); // 子类重写该方法
        }
    }
    
(3) Message 的复用
  • 避免频繁创建对象:通过 Message.obtain() 从全局池中获取复用实例。
    Message msg = Message.obtain();
    msg.what = 1;
    msg.obj = "Data";
    handler.sendMessage(msg);
    

4. 常见问题与场景
Q1: 为什么子线程默认没有 Looper?
  • 设计考量:减少资源消耗(非所有子线程需要消息循环)。
  • 解决方案
    new Thread(() -> {
        Looper.prepare();
        Handler handler = new Handler();
        Looper.loop();
    }).start();
    
Q2: Handler 导致的内存泄漏如何避免?
  • 原因Handler 持有 Activity 的隐式引用(如匿名内部类)。
  • 解决:使用静态内部类 + WeakReference
    static class SafeHandler extends Handler {
        private WeakReference<Activity> mActivity;
        SafeHandler(Activity activity) {
            mActivity = new WeakReference<>(activity);
        }
        @Override public void handleMessage(Message msg) {
            Activity activity = mActivity.get();
            if (activity != null) { /* ... */ }
        }
    }
    
Q3: 如何退出 Looper 循环?
  • 主线程:不可退出(ActivityThread 生命周期依赖)。
  • 子线程
    Looper.myLooper().quitSafely(); // 安全退出
    

5. 性能优化建议
  1. 复用 Message:优先使用 Message.obtain() 而非 new Message()
  2. 减少延迟消息:避免大量 postDelayed 导致 MessageQueue 堆积。
  3. 线程选择:耗时任务使用子线程 HandlerThread,避免阻塞主线程 Looper。

总结

  • Message:数据的容器。
  • Looper:消息循环的引擎,驱动 Handler 工作。
  • Handler:连接发送端和处理端的桥梁。

三者共同构成 Android 的 异步消息机制,是理解事件驱动模型的核心基础。

MessageQueue 的数据结构及设计原理

1. 核心数据结构:单向链表(时间有序的优先级队列)

Android 的 MessageQueue 内部使用 按时间排序的单向链表 存储 Message,其结构如下:

// 简化后的 Message 链表结构
class Message {
    int what;        // 消息标识
    long when;       // 触发时间(毫秒)
    Object obj;      // 携带数据
    Message next;    // 指向下一个节点
}

// MessageQueue 关键字段
class MessageQueue {
    private Message mMessages;  // 链表头节点
    private boolean mQuitting;  // 队列退出标志
}
2. 链表排序规则
  • when 升序排列:消息根据执行时间(when)从小到大排列,头部是最早触发的消息。
  • 插入操作:新消息根据 when 插入到链表合适位置(时间复杂度 O(n))。
    // 插入逻辑伪代码
    void enqueueMessage(Message msg, long when) {
        msg.when = when;
        Message p = mMessages;
        if (p == null || when < p.when) {
            msg.next = p;
            mMessages = msg; // 插入头部
        } else {
            while (p.next != null && p.next.when <= when) {
                p = p.next;
            }
            msg.next = p.next;
            p.next = msg; // 插入中间或尾部
        }
    }
    
3. 选择单向链表的原因
(1) 适应高频插入和删除
  • 场景需求Handler 频繁插入 Message(如 sendMessageDelayed)和取出头部消息(next())。
  • 链表优势
    • 插入/删除效率:只需修改指针(O(n) 查找 + O(1) 插入),优于数组的 O(n) 搬移。
    • 动态扩容:无需预分配空间,适合不确定数量的消息。
(2) 延迟消息的高效管理
  • 延迟任务postDelayed() 需要根据 when 排序。
  • 链表适用性
    新消息插入时遍历链表找到合适位置,保证时间顺序,而 最小堆(如优先队列)虽然取头快(O(1)),但插入平均仍需 O(log n)。
(3) 避免内存碎片
  • 数组问题:动态数组扩容需拷贝数据,易产生碎片。
  • 链表优势:节点分散分配,无连续内存需求。
4. 对比其他数据结构
数据结构插入复杂度取头复杂度适用场景
单向链表O(n)O(1)高频插入、时间有序
优先队列O(log n)O(1)取头频繁但插入较少
动态数组O(n)O(1)随机访问多,插入删除少
5. 性能优化细节
  • 消息复用:通过 Message.obtain() 从全局池获取对象,减少 GC 压力。
  • 同步屏障postSyncBarrier() 插入特殊消息(target=null),优先处理异步消息(如 UI 绘制)。
  • 空闲处理IdleHandler 在队列空闲时执行低优先级任务。
6. 典型流程示例
// Handler 发送延迟消息
handler.sendMessageDelayed(msg, 1000);

// MessageQueue 内部插入逻辑
enqueueMessage(msg, SystemClock.uptimeMillis() + 1000);

// Looper 取出消息
Message next() {
    for (;;) {
        Message msg = mMessages;
        if (msg == null || msg.when > now) {
            return null; // 无消息或未到时间
        }
        mMessages = msg.next; // 移动头指针
        return msg;
    }
}
7. 设计哲学
  • 平衡取舍:链表在插入和取出间取得平衡,适合 Android 高频消息场景。
  • 线程安全:通过 synchronizednativePollOnce 实现高效等待。
总结

MessageQueue 选择单向链表的核心原因是:

  1. 时间有序性:完美支持延迟消息的排序需求。
  2. 动态性:适应高频、不确定数量的消息插入。
  3. 低开销:避免数组扩容和内存浪费,契合移动端资源限制。

同步屏障(Sync Barrier)的使用详解

同步屏障是 Android 消息机制中的高级功能,用于临时阻塞普通同步消息,优先处理异步消息(如 UI 绘制任务)。以下是具体使用方法和原理分析:


1. 同步屏障的核心作用
  • 普通消息:通过 Handler.sendMessage() 发送,默认是同步的(target 为发送它的 Handler)。
  • 异步消息:标记为 setAsynchronous(true),通常用于高优先级任务(如 VSYNC 信号触发的界面渲染)。
  • 同步屏障:插入一个特殊消息(target=null),使后续同步消息被阻塞,仅处理异步消息。

2. 使用步骤
(1) 插入同步屏障
// 获取 MessageQueue
MessageQueue queue = Looper.getMainLooper().getQueue();

// 插入同步屏障(API 16+)
try {
    Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
    method.setAccessible(true);
    int token = (int) method.invoke(queue); // 返回token用于移除屏障
} catch (Exception e) {
    e.printStackTrace();
}
(2) 发送异步消息
Handler handler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        // 处理异步任务
    }
};

// 创建异步消息
Message asyncMsg = Message.obtain();
asyncMsg.setAsynchronous(true); // 关键设置!
asyncMsg.what = 1;
handler.sendMessage(asyncMsg);
(3) 移除同步屏障
try {
    Method method = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class);
    method.setAccessible(true);
    method.invoke(queue, token); // 传入插入时返回的token
} catch (Exception e) {
    e.printStackTrace();
}

3. 使用场景
  • UI 渲染优化
    Choreographer 在 VSYNC 信号到来时,插入同步屏障,确保动画和绘制优先执行。
  • 自定义高优先级任务
    例如在页面卡顿时,临时阻塞业务逻辑消息,优先处理关键操作。

4. 原理解析
  • 消息队列结构
    Before Barrier:
      [SyncMsg1] -> [SyncMsg2] -> [AsyncMsg] -> [SyncMsg3]
    
    After Barrier:
      [SyncMsg1] -> [SyncMsg2] -> [BARRIER] -> [AsyncMsg] -> [SyncMsg3]
                                  ^ 同步消息在此处被阻塞
    
  • Looper 处理逻辑
    Message next() {
        for (;;) {
            // 遇到同步屏障时,跳过同步消息
            if (msg != null && msg.target == null) {
                do {
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            return msg;
        }
    }
    

5. 注意事项
  1. 权限限制
    postSyncBarrier()@hide 方法,需反射调用(注意兼容性)。
  2. 屏障泄漏
    必须成对调用 postSyncBarrier()removeSyncBarrier(),否则会导致同步消息永久阻塞。
  3. 主线程专用
    通常只在主线程使用,子线程的 Handler 很少需要同步屏障。

6. 完整示例
// 1. 插入同步屏障
MessageQueue queue = Looper.getMainLooper().getQueue();
int token = postSyncBarrier(queue);

// 2. 发送异步消息
Handler handler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        Log.d("Async", "处理异步任务");
        removeSyncBarrier(queue, token); // 任务完成后移除屏障
    }
};
Message msg = Message.obtain(handler);
msg.setAsynchronous(true);
handler.sendMessageDelayed(msg, 1000);

// 反射工具方法
private int postSyncBarrier(MessageQueue queue) {
    try {
        Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
        method.setAccessible(true);
        return (int) method.invoke(queue);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

private void removeSyncBarrier(MessageQueue queue, int token) {
    try {
        Method method = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class);
        method.setAccessible(true);
        method.invoke(queue, token);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

7. 性能影响
  • 优点:确保关键任务(如渲染)不被低优先级消息阻塞。
  • 缺点:滥用会导致普通消息延迟,影响响应性。

总结

同步屏障是 Android 消息系统的底层能力,正确使用可优化高优先级任务调度,但需谨慎处理移除时机。实际开发中,非必要不直接使用,优先考虑 Handler 默认机制或 Choreographer 等封装工具。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值