当前位置: 首页 > news >正文

Android学习总结之handler源码级

一、核心类关系与线程绑定(ThreadLocal 的核心作用)

1. Looper 与 ThreadLocal 的绑定

每个线程的 Looper 实例通过 ThreadLocal<Looper> sThreadLocal 存储,确保线程隔离:

public final class Looper {
    // 线程本地存储,每个线程独有一个 Looper
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    // 主线程 Looper(ActivityThread 中创建)
    static Looper sMainLooper; 
    final MessageQueue mQueue; // 关联的消息队列
    final Thread mThread; // 绑定的线程

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread(); // 记录当前线程
    }

    // 线程首次调用,创建 Looper 并存储到 ThreadLocal
    public static void prepare() {
        prepare(false); // quitAllowed 默认 false(主线程不允许退出)
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) { // 禁止重复创建
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed)); // 存入当前线程的 Looper
    }

    // 获取当前线程的 Looper
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
}
  • 主线程:Android 框架在 ActivityThread.main() 中自动调用 Looper.prepareMainLooper() 和 Looper.loop(),无需手动处理。
  • 子线程:必须手动调用 Looper.prepare()(创建 Looper 和 MessageQueue)和 Looper.loop()(启动消息循环),否则 Handler 无可用 Looper 会报错。
2. Handler 的构造与 Looper 关联

Handler 实例必须与一个 Looper 绑定,默认使用当前线程的 Looper(通过 Looper.myLooper() 获取):

public class Handler {
    final Looper mLooper; // 关联的 Looper
    final MessageQueue mQueue; // Looper 的消息队列
    final Callback mCallback; // 消息回调

    public Handler() {
        this(null, false);
    }

    public Handler(@Nullable Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: "
                        + klass.getCanonicalName());
            }
        }
        mLooper = Looper.myLooper(); // 获取当前线程的 Looper(必须已 prepare)
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue; // 关联消息队列
        mCallback = callback;
        mAsynchronous = async;
    }
}
  • 若子线程未调用 Looper.prepare(),创建 Handler 时会抛出 RuntimeException,这就是子线程必须先准备 Looper 的原因。

二、消息发送:从 Handler 到 MessageQueue 的入队

1. Message 的创建与重用(消息池机制)

Message 优先从消息池获取,避免频繁 GC:

public final class Message implements Parcelable {
    // 消息池头节点(静态,所有线程共享)
    private static Message sPool;
    // 消息池大小(最大 50 个)
    private static int sPoolSize = 0;
    // 下一个可用消息(形成单链表)
    @UnsupportedAppUsage
    Message next;

    // 从消息池获取消息
    public static Message obtain() {
        synchronized (sPoolSync) { // 线程安全
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next; // 取出头节点
                m.next = null; // 断开引用
                m.flags = 0; // 重置标志位
                sPoolSize--;
                return m;
            }
        }
        return new Message(); // 池空时新建
    }

    // 回收消息到池中(处理完后调用)
    void recycleUnchecked() {
        if (isInUse()) { // 确保未被使用
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycle();
    }

    private void recycle() {
        if (mRecycled) { // 已回收过则不再处理
            return;
        }
        mRecycled = true;
        if (sPoolSize < MAX_POOL_SIZE) { // 最大 50 个
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

  • 优势:减少对象创建开销,提升性能,尤其适合高频发送消息的场景。
2. MessageQueue 的入队逻辑(enqueueMessage)

消息按 msg.when(执行时间)排序插入,形成一个 非严格 FIFO 的有序单链表

boolean enqueueMessage(Message msg, long when) {
    msg.target = this; // target 指向发送消息的 Handler
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    synchronized (this) { // 加锁保证线程安全
        if (mQuitting) { // 队列已退出,回收消息
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages; // 当前头节点
        boolean needWake;
        if (p == null || when == 0 || when < p.when) { // 新消息时间更早,插入头部
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; // 当前队列是否阻塞,决定是否唤醒
        } else { // 找到合适位置插入(按 when 升序)
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) { // 遍历链表找到插入点
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p;
            prev.next = msg;
        }

        // 若队列阻塞且需要唤醒(如 Looper.loop() 在等待),通过 native 方法唤醒
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

  • 关键点
    • synchronized (this) 确保多线程安全,不同线程的 Handler 发送消息到同一 MessageQueue 时不会冲突。
    • 消息按 when 排序,而非严格的 FIFO,支持延迟消息(如 postDelayed)。
    • 异步消息(msg.setAsynchronous(true))可打断同步消息的等待,优先处理。

三、消息循环:Looper.loop () 的无限循环

1. loop () 方法核心逻辑
public static void loop() {
    final Looper me = myLooper(); // 获取当前线程的 Looper
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue; // 关联的消息队列

    // 确保主线程 Looper 不会被 GC 回收(Binder 机制相关)
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) { // 无限循环,直到队列退出
        Message msg = queue.next(); // 取出消息(可能阻塞)
        if (msg == null) { // 队列退出(msg.next == null)
            return; // 退出循环
        }

        // 处理消息:通过 msg.target(Handler)分发
        msg.target.dispatchMessage(msg);

        // 回收消息到池中(非必须,系统自动处理)
        msg.recycleUnchecked();
    }
}
2. MessageQueue.next () 的阻塞与唤醒

next() 是消息循环的核心,通过 native 层实现阻塞等待:

Message next() {
    final long ptr = mPtr; // 指向 native 层的 MessageQueue 实例
    int pendingIdleHandlerCount = -1; // 首次调用时初始化为 -1
    int nextPollTimeoutMillis = 0;

    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        // native 层阻塞等待消息,直到有消息或被唤醒
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // 检查是否有消息待处理
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;

            if (msg != null && msg.when > now) { // 消息未到执行时间,计算延迟
                nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
            } else { // 有可执行的消息,取出头节点
                msg = mMessages;
                mMessages = msg.next;
                msg.next = null;
                if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                nextPollTimeoutMillis = -1; // 下次立即 poll
            }

            if (msg != null) { // 有消息,返回给 Looper
                return msg;
            }

            // 无消息,检查是否退出
            mQuitting = true;
            return null;
        }
    }
}
  • 阻塞原理:通过 nativePollOnce 进入内核等待,当消息入队时(enqueueMessage 中调用 nativeWake)被唤醒。
  • 退出条件:调用 Looper.quit() 或 Looper.quitSafely() 时,mQuitting 设为 true,next() 返回 null,loop() 终止。

四、消息处理:Handler.dispatchMessage 的分发逻辑

消息最终由发送它的 Handler 处理,分发流程如下:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) { // 优先处理 Message 的 Runnable(post(Runnable))
        handleCallback(msg);
    } else if (mCallback != null) { // 其次处理 Handler 的 Callback
        if (mCallback.handleMessage(msg)) {
            return;
        }
    }
    handleMessage(msg); // 最后调用用户重写的 handleMessage()
}

private static void handleCallback(Message message) {
    message.callback.run(); // 执行 post(Runnable) 传入的 Runnable
}
  • 三种处理方式
    1. Message 自带的 Runnable:通过 post(Runnable) 发送的消息,直接执行 Runnable.run()
    2. Handler 的 Callback:通过构造函数传入的 Callback,优先级高于 handleMessage
    3. 用户重写的 handleMessage:最常用的消息处理逻辑。

五、子线程使用 Handler 的完整流程(源码级示例)

// 子线程类
class WorkerThread extends Thread {
    private Handler mHandler;
    private Looper mLooper;

    // 获取 Handler(供外部发送消息)
    public Handler getHandler() {
        return mHandler;
    }

    @Override
    public void run() {
        // 1. 准备 Looper(创建 Looper 和 MessageQueue)
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper(); // 保存当前线程的 Looper
            mHandler = new Handler() { // 创建 Handler 关联当前 Looper
                @Override
                public void handleMessage(Message msg) {
                    // 处理消息(子线程中执行)
                    processMessage(msg);
                    if (msg.what == MSG_QUIT) { // 退出消息
                        mLooper.quit(); // 停止消息循环
                    }
                }
            };
            notify(); // 通知主线程 Handler 已准备好
        }
        // 2. 启动消息循环
        Looper.loop();
    }
}

// 主线程使用子线程 Handler
public class MainActivity extends AppCompatActivity {
    private WorkerThread mWorkerThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mWorkerThread = new WorkerThread();
        mWorkerThread.start();

        synchronized (mWorkerThread) {
            try {
                mWorkerThread.wait(); // 等待子线程准备好 Handler
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 向子线程发送消息
        Message msg = Message.obtain();
        msg.what = WorkerThread.MSG_WORK;
        mWorkerThread.getHandler().sendMessage(msg);

        // 发送退出消息
        mWorkerThread.getHandler().sendMessage(Message.obtain(null, WorkerThread.MSG_QUIT));
    }
}
  • 关键步骤
    1. 子线程中调用 Looper.prepare() 创建 Looper 和 MessageQueue。
    2. 创建 Handler 时自动关联当前 Looper(因在 prepare() 之后调用)。
    3. 调用 Looper.loop() 启动消息循环,处理外部发送的消息。
    4. 通过 Looper.quit() 终止循环,避免子线程阻塞。

六、常见问题源码级解析

1. 为什么主线程可以直接创建 Handler?
  • 主线程(ActivityThread 所在线程)在启动时,框架自动调用了:
    public static void main(String[] args) {
        // ...
        Looper.prepareMainLooper(); // 准备主线程 Looper
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        Looper.loop(); // 启动主线程消息循环
    }
    

    因此主线程的 Looper 已存在,无需手动调用 prepare()
2. MessageQueue 真的是 “队列” 吗?
  • 从数据结构看,它是一个 单链表,而非传统的 FIFO 队列。消息按 msg.when 排序插入,保证按时间顺序执行,支持延迟消息。
3. postDelayed 不准时的根本原因?
  • postDelayed 计算延迟的基准时间是 SystemClock.uptimeMillis()(系统启动后非休眠时间),若设备休眠,休眠时间不计入延迟,导致实际执行时间晚于预期。
  • 此外,消息需等待前面的消息处理完成,若主线程被阻塞(如耗时操作),延迟消息会被阻塞。
4. 多线程发送消息到同一 MessageQueue 为何线程安全?
  • MessageQueue.enqueueMessage 使用 synchronized (this) 加锁,确保同一时间只有一个线程操作队列,避免并发问题。

七、Handler 机制的设计精髓

  1. 线程隔离:通过 ThreadLocal 保证每个线程独有 Looper 和 MessageQueue,避免资源竞争。
  2. 消息池:重用 Message 对象,减少内存分配和 GC 压力,提升性能。
  3. 有序调度:按时间排序的消息链表,支持延迟和异步消息,灵活控制执行顺序。
  4. 阻塞与唤醒:通过 native 层实现高效的等待与唤醒,避免 CPU 空转。

总结

     Handler 机制的核心是通过 Looper(消息循环)、MessageQueue(有序消息链表)、Handler(消息收发器) 的协作,实现线程间的安全通信。源码中大量使用 ThreadLocal、synchronized、单链表等技术,确保线程隔离、数据安全和性能优化。深入理解这些细节,能帮助开发者更好地处理线程通信、避免内存泄漏(如非静态内部类 Handler 导致的 Activity 泄漏),并在复杂场景中灵活运用 Handler 机制。

http://www.dtcms.com/a/109403.html

相关文章:

  • Reactive编程入门:Project Reactor 深度指南
  • 网络:华为数通HCIA学习:IP路由基础
  • vuex自存例子
  • linux_sysctl_fs_file_nr监控项
  • LeetCode刷题 -- 48. 旋转图像
  • 2025身份证号码前六位地区代码对照表
  • Android A/B 分区 OTA 如何查看升级的 img 分区
  • zk基础—4.zk实现分布式功能二
  • Ansible:playbook的高级用法
  • Unity中 JobSystem使用整理
  • LeetCode 接雨水问题详解 - 动态规划解法
  • CentOS 7安装hyperscan
  • LLM驱动的智能体:基于GPT的对话智能体开发指南
  • 如何学习一门编程语言
  • flux绘画模型介绍
  • Java学习总结-字符集
  • 项目之Boost搜索引擎
  • 六种光耦综合对比——《器件手册--光耦》
  • JavaWeb学习--MyBatis-Plus整合SpringBoot的ServiceImpl方法(查找部分)
  • Java在体育比分直播系统搭建中的应用
  • py文件打包为exe可执行文件,涉及mysql连接失败
  • leetcode76.最小覆盖子串
  • podman和与docker的比较 及podman使用
  • Linux红帽:RHCSA认证知识讲解(九)标准输入输出、重定向、过滤器与管道
  • PyTorch的dataloader制作自定义数据集
  • Golang改进后的任务调度系统分析
  • MySQL的进阶语法12(MySQL管理)
  • [250403] HuggingFace 新增检查模型与电脑兼容性的功能 | Firefox 发布137.0 支持标签组
  • 数据库系统-数据库模式
  • UART双向通信实现(序列机)