【底层机制】Handler/Looper 实现线程切换的技术细节

核心架构概览

Handler/Looper 机制的本质是 "线程间消息传递 + 消息队列 + 事件循环" 的组合。

css 复制代码
[生产者线程] → Handler → MessageQueue → Looper → [消费者线程]

1. 核心组件深度解析

1.1 Looper - 消息泵

java 复制代码
public final class Looper {
    // 每个线程独享的ThreadLocal存储
    private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
    
    final MessageQueue mQueue;  // 关联的消息队列
    final Thread mThread;       // 所属线程
    
    // 准备当前线程的Looper
    public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
    
    // 核心循环 - 消息泵
    public static void loop() {
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;
        
        for (;;) {  // 无限循环
            Message msg = queue.next(); // 可能阻塞
            if (msg == null) return;    // 只有队列退出时返回null
            
            // 关键:在目标线程执行消息处理!
            msg.target.dispatchMessage(msg);
            msg.recycleUnchecked();
        }
    }
}

关键设计:ThreadLocal

  • 每个线程有自己独立的 Looper 实例
  • sThreadLocal 保证线程隔离,不同线程访问得到不同对象

1.2 MessageQueue - 消息仓库

java 复制代码
public final class MessageQueue {
    private long mPtr; // Native层的消息队列指针
    
    // 核心:消息入队(线程安全)
    boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {  // 🔐 关键锁!
            msg.markInUse();
            msg.when = when;
            
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                // 插入队列头部
                msg.next = p;
                mMessages = msg;
            } else {
                // 按执行时间排序插入
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) break;
                }
                msg.next = p;
                prev.next = msg;
            }
            
            // 唤醒可能阻塞的next()方法
            if (needWake) nativeWake(mPtr);
        }
        return true;
    }
    
    // 核心:消息出队
    Message next() {
        for (;;) {
            // 可能阻塞在nativePollOnce
            nativePollOnce(ptr, nextPollTimeoutMillis);
            
            synchronized (this) {
                // 查找可执行的消息
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                
                if (msg != null && msg.target == null) {
                    // 同步屏障处理
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                
                if (msg != null) {
                    if (now < msg.when) {
                        // 消息还未到执行时间,设置超时
                        nextPollTimeoutMillis = (int) Math.min(
                            msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 找到可执行消息,从队列移除
                        mMessages = msg.next;
                        msg.next = null;
                        return msg;
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }
            }
        }
    }
}

1.3 Handler - 消息代理

java 复制代码
public class Handler {
    final Looper mLooper;
    final MessageQueue mQueue;
    
    public Handler() {
        this(Looper.myLooper()); // 默认绑定当前线程Looper
    }
    
    public Handler(Looper looper) {
        mLooper = looper;
        mQueue = looper.mQueue;
    }
    
    // 发送消息的核心方法
    public boolean sendMessage(Message msg) {
        return sendMessageDelayed(msg, 0);
    }
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        return sendMessageAtTime(msg, 
            SystemClock.uptimeMillis() + delayMillis);
    }
    
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;  // 关键:设置消息的处理者
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    
    // 消息分发
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            // 处理Runnable
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) return;
            }
            // 最终调用到我们重写的handleMessage
            handleMessage(msg);
        }
    }
}

2. 线程切换的完整流程

场景:子线程 → 主线程更新UI

java 复制代码
// 1. 主线程初始化时
class MainThread {
    void onCreate() {
        // 系统已调用:Looper.prepareMainLooper();
        // 系统已调用:Looper.loop();
        
        mMainHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                // 这里在主线程执行!
                updateUI(msg.obj);
            }
        };
    }
}

// 2. 子线程发送消息
class WorkerThread {
    void doWork() {
        // 在子线程执行网络请求
        String data = fetchDataFromNetwork();
        
        // 关键:通过主线程的Handler发送消息
        Message msg = mMainHandler.obtainMessage(WHAT_UPDATE_UI, data);
        mMainHandler.sendMessage(msg);
    }
}

详细执行流程:

  1. 子线程调用 mMainHandler.sendMessage(msg)

    • 获取到主线程 Handler 的引用
    • 创建 Message 并设置数据
  2. Handler.enqueueMessage()

    • 设置 msg.target = this (this 是主线程的 Handler)
    • 调用 mQueue.enqueueMessage(msg, time)
  3. MessageQueue 线程安全入队

    • 获取 MessageQueue 的锁 (synchronized (this))
    • 按执行时间将消息插入到合适位置
    • 如果 Looper 正在阻塞,调用 nativeWake() 唤醒
  4. 主线程 Looper.loop() 处理

    • queue.next() 从队列取出消息
    • 调用 msg.target.dispatchMessage(msg)
    • 这里 msg.target 就是主线程的 Handler
  5. 在主线程执行

    • dispatchMessage() 在主线程调用
    • 最终执行到 handleMessage() 更新 UI

3. 关键技术实现

3.1 线程绑定机制

java 复制代码
// Handler 构造时绑定目标线程
public Handler(Looper looper) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mThread = looper.mThread;  // 保存目标线程引用
}

// 可以在任何线程调用,但会在目标线程执行
public final boolean post(Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
}

3.2 消息池优化

java 复制代码
// 避免频繁创建Message对象
public final Message obtainMessage() {
    return Message.obtain(this);
}

// Message池实现
public final class Message implements Parcelable {
    private static Message sPool;  // 回收的消息对象池
    private static int sPoolSize = 0;
    
    Message next;  // 链表结构
    
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    
    public void recycle() {
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
}

3.3 Native 层支持

MessageQueue 在 Native 层使用 epoll 机制实现高效阻塞/唤醒:

cpp 复制代码
// native/libmessagequeue/MessageQueue.cpp
void NativeMessageQueue::pollOnce(int timeoutMillis) {
    mLooper->pollOnce(timeoutMillis);
}

// frameworks/base/libs/utils/Looper.cpp
int Looper::pollOnce(int timeoutMillis) {
    int result = 0;
    for (;;) {
        // 使用epoll等待事件
        int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
        
        if (eventCount == 0) {
            result = ALOOPER_POLL_TIMEOUT;
            break;
        }
        
        // 处理唤醒事件
        for (int i = 0; i < eventCount; i++) {
            if (eventItems[i].data.fd == mWakeEventFd) {
                awoken();  // 读取唤醒事件
            }
        }
    }
    return result;
}

4. 并发安全设计

4.1 锁策略

java 复制代码
public final class MessageQueue {
    // 入队操作加锁
    boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {  // 使用对象内置锁
            // 临界区代码
        }
    }
    
    // 出队操作同样加锁
    Message next() {
        synchronized (this) {
            // 临界区代码  
        }
    }
}

4.2 线程封闭

通过 ThreadLocal 实现线程数据隔离:

java 复制代码
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();

// 每个线程访问得到不同的Looper实例
Looper.myLooper() == sThreadLocal.get()

5. 总结

Handler/Looper 实现线程切换的核心在于:

  1. 消息路由:Handler 作为消息的路由器,知道目标线程的消息队列
  2. 队列隔离:每个线程有独立的消息队列,实现线程数据隔离
  3. 事件循环:Looper 在目标线程不断从队列取消息执行
  4. 线程安全:通过 synchronized 保证多线程入队安全
  5. 高效阻塞:Native 层使用 epoll 实现高效等待/唤醒

这种设计实现了 "在哪里定义,在哪里执行" 的线程切换效果,是 Android 异步编程的基石。

相关推荐
自由的疯4 小时前
优雅的代码java
java·后端·面试
.NET修仙日记5 小时前
2025年ASP.NETMVC面试题库全解析
面试·职场和发展·c#·asp.net·mvc·面试题·asp.net mvc
轻口味5 小时前
Rokid Glasses 移动端控制应用开发初体验-助力业务创新
android·操作系统·app
绝无仅有5 小时前
面试真实经历某商银行大厂Java问题和答案总结(一)
后端·面试·github
绝无仅有5 小时前
面试真实经历某商银行大厂Java问题和答案总结(二)
后端·面试·github
召摇5 小时前
深入Next.js应用性能优化:懒加载技术全解析
前端·面试·next.js
召摇5 小时前
Next.js Server Actions进阶指南:安全传递额外参数的完整方案
前端·面试·next.js
召摇5 小时前
JavaScript字符串填充:padStart()方法
前端·javascript·面试