Android Hanlder 机制详解

一、 概述

Handler 机制是 Android 系统中实现线程间通信(Inter-thread Communication)的核心机制,尤其广泛用于将子线程中的任务结果传递回主线程(UI线程)进行更新操作。其底层基于 消息队列(MessageQueue) 和 循环器(Looper) 构建,形成一个典型的生产者-消费者模型。

该机制的核心目标是:

  • 实现跨线程的消息传递
  • 避免多线程并发修改 UI
  • 提供延迟执行和定时任务的能力
  • 支持异步任务调度与回调处理

二、 核心组件

1. Handler(处理器)

Handler 是开发者最常接触的类,负责:

  • 发送消息:sendMessage()、post() 等方法
  • 处理消息:通过重写 handleMessage(Message msg) 或设置 Callback
  • 与特定线程的 Looper 和 MessageQueue 绑定

⚠️ 每个 Handler 必须关联一个 Looper,否则会抛出异常。

// 示例:创建 Handler 并绑定当前线程的 Looper
Handler handler = new Handler(Looper.myLooper()) {
    @Override
    public void handleMessage(@NonNull Message msg) {
        // 在指定线程中处理消息
    }
};

2. MessageQueue(消息队列)

MessageQueue 是一个按时间排序的消息队列,内部使用单向链表结构存储待处理的消息。

主要职责:

  • 存储由 Handler 发送的 Message
  • 按 Message.when(执行时间戳)排序插入
  • 提供 next() 方法供 Looper 取出下一条可执行消息
  • 支持阻塞等待(无消息时休眠,有消息时唤醒)

💡 MessageQueue 并非传统 FIFO 队列,而是根据执行时间排序的优先队列。

3. Looper(循环器)

Looper 是消息循环的驱动者,每个线程最多只能有一个 Looper。

关键功能:

  • 调用 Looper.prepare() 初始化当前线程的 Looper
  • 调用 Looper.loop() 启动无限循环,不断从 MessageQueue 取出消息并分发
  • 使用 ThreadLocal 保证线程局部性
// 主线程中系统自动调用
Looper.prepareMainLooper(); // 初始化主线程 Looper
...
Looper.loop(); // 开始循环

4. Message(消息)

Message 是消息的载体,包含以下核心字段:

字段 说明
what 用户自定义消息类型
arg1, arg2 整型参数,用于传递简单数据
obj 任意对象(注意内存泄漏风险)
target 目标 Handler,由 Handler.sendMessage() 自动设置
callback 如果是 post(Runnable),则 Runnable 封装为 callback
when 消息应被执行的时间戳(毫秒)
next 指向链表中的下一个 Message

推荐使用 Message.obtain() 获取实例,避免频繁创建对象。


三、工作原理

1. 整体流程图

[图片上传失败...(image-ed932a-1761026296346)]

2. 详细执行过程

1. 初始化 Looper(子线程示例)

在非主线程中若要使用 Handler,必须手动创建 Looper。

class WorkerThread extends Thread {
    public Handler handler;

    @Override
    public void run() {
        Looper.prepare(); // 创建当前线程的 Looper 和 MessageQueue

        handler = new Handler(Looper.myLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what) {
                    case 1:
                        Log.d("Handler", "收到消息: " + msg.obj);
                        break;
                }
            }
        };

        Looper.loop(); // 开始无限循环读取消息
    }

    public void quit() {
        if (handler != null) {
            handler.getLooper().quit(); // 安全退出循环
        }
    }
}

📌 注意:

  • Looper.prepare() 只能调用一次,否则抛出 RuntimeException
  • Looper.loop() 是阻塞方法,直到调用 quit() 才退出

2. 消息发送机制

Handler 提供多种发送消息的方式:

方法 说明
sendEmptyMessage(int what) 发送空消息
sendMessage(Message msg) 发送自定义消息
post(Runnable r) 发送 Runnable 任务
sendEmptyMessageDelayed(what, delay) 延迟发送
postDelayed(Runnable r, long delayMillis) 延迟执行 Runnable

底层均调用 enqueueMessage() 将消息插入 MessageQueue。

// 示例:多种发送方式
handler.sendEmptyMessage(1);

Message msg = Message.obtain();
msg.what = 2;
msg.obj = "Hello";
handler.sendMessage(msg);

handler.post(() -> {
    Log.d("Handler", "Runnable 执行");
});

handler.postDelayed(() -> {
    Log.d("Handler", "1秒后执行");
}, 1000);

3. 消息处理流程

Handler.dispatchMessage() 是消息分发的核心逻辑:

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        // 优先处理 post(Runnable) 类型的消息
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            // 其次交给 Handler 的 Callback 处理
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 最终调用 handleMessage
        handleMessage(msg);
    }
}

private void handleCallback(Message message) {
    message.callback.run(); // 直接执行 Runnable
}

执行顺序:Runnable > Callback > handleMessage

四、源码分析(精简版)

1. Looper 核心实现

public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();

    final MessageQueue mQueue;
    final Thread mThread;

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

    // 准备 Looper
    public static void prepare() {
        prepare(true);
    }

    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
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

    // 消息循环主方法
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }

        final MessageQueue queue = me.mQueue;

        for (;;) {
            Message msg = queue.next(); // 可能阻塞
            if (msg == null) return; // 退出循环

            // 分发消息
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                // 空实现,预留钩子
            }

            msg.recycleUnchecked(); // 回收消息
        }
    }
}

🔍 关键点:

  • ThreadLocal 保证每个线程独享一个 Looper
  • loop() 是死循环,通过 queue.next() 阻塞等待消息
  • msg.target 即发送该消息的 Handler

2. MessageQueue 关键方法

enqueueMessage() —— 入队

boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        final long now = SystemClock.uptimeMillis();
        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;
        }
        // 唤醒消息队列(如果之前处于等待状态)
        notify();
    }
    return true;
}

next() —— 出队(核心阻塞方法)

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) return null; // 已退出

    long pendingIdleHandlerCount = -1;
    long nextPollTimeoutMillis = 0;

    for (;;) {
        // 阻塞等待,timeout 为 0 表示无限等待
        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 {
                    // 可执行,取出消息
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 无消息,无限等待
                nextPollTimeoutMillis = -1;
            }

            // 处理 IdleHandler(空闲时执行)
            if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount > 0) {
                // 执行空闲任务
            }
        }
    }
}

五、高级特性

1. 同步屏障(Sync Barrier)

用于优先处理异步消息(如 UI 绘制),屏蔽同步消息。

应用场景:

  • ViewRootImpl 请求重绘时插入屏障,确保 Choreographer 的 VSYNC 消息优先执行
// 插入同步屏障
int token = mHandler.getLooper().getQueue().postSyncBarrier();

// 移除屏障
mHandler.getLooper().getQueue().removeSyncBarrier(token);

⚠️ 滥用可能导致同步消息饥饿,不建议普通应用使用。

2. 空闲处理器(IdleHandler)

当 MessageQueue 没有消息需要处理时,会回调 IdleHandler,可用于执行低优先级任务。

Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
    @Override
    public boolean queueIdle() {
        Log.d("IdleHandler", "消息队列空闲,执行预加载");
        // 返回 true:保持注册;false:执行一次后移除
        return false;
    }
});

典型用途:

  • 延迟初始化组件
  • 预加载数据
  • 执行非关键任务以提升流畅度

3. 异步消息(Asynchronous Message)

避免被同步屏障阻塞,适用于高优先级任务。

Message msg = Message.obtain(handler, runnable);
msg.setAsynchronous(true);
handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + 1000);

Handler.createAsync() 可创建专门处理异步消息的 Handler

六、内存优化机制

1. Message 对象池(对象复用)

为减少频繁创建/销毁对象带来的 GC 压力,Message 内部维护了一个对象池。

public final class Message implements Parcelable {
    private static final int MAX_POOL_SIZE = 50;
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final Object sPoolSync = new Object();

    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();
    }

    public void recycle() {
        if (isInUse()) return;
        recycleUnchecked();
    }

    void recycleUnchecked() {
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        target = null;
        callback = null;
        data = null;
        when = 0;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
}

建议始终使用 Message.obtain() 而非 new Message()

七、 使用注意事项

1. 避免内存泄漏

常见场景:非静态内部类 Handler 持有外部 Activity 引用,导致无法回收。

解决方案:静态内部类 + 弱引用

private static class SafeHandler extends Handler {
    private final WeakReference<MainActivity> mActivity;

    public SafeHandler(MainActivity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(@NonNull Message msg) {
        MainActivity activity = mActivity.get();
        if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
            return;
        }

        switch (msg.what) {
            case 1:
                activity.updateUI((String) msg.obj);
                break;
        }
    }
}

或使用 WeakHandler(第三方库)

// 如 Square 的 LeakCanary 推荐方式
WeakHandler weakHandler = new WeakHandler(this, new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message msg) {
        // 安全处理
        return true;
    }
});

2. 正确退出 Looper

在子线程中使用 Looper 后,应在适当时机退出,避免资源浪费。

// 安全退出:处理完已到时的消息再退出
handler.getLooper().quitSafely();

// 立即退出:丢弃所有未处理消息
handler.getLooper().quit();

建议在 Thread 的 onDestroy 或 quit() 方法中调用


3. 主线程 vs 子线程

线程类型 Looper 是否自动创建 使用方式
主线程(UI线程) 是(系统自动调用 Looper.prepareMainLooper()) 直接创建 Handler
子线程 否 必须手动调用 Looper.prepare() 和 Looper.loop()

八、常见问题与使用建议

问题 解决方案
Can't create handler inside thread that has not called Looper.prepare() 在子线程中先调用 Looper.prepare()
内存泄漏 使用静态 Handler + WeakReference
消息延迟不准 系统休眠或高负载可能导致延迟,不保证精确时间
大量消息堆积 控制消息频率,避免 OOM
使用 postDelayed 实现轮询 不推荐,应使用 AlarmManager 或 WorkManager 替代

九、总结

Android 的 Handler 机制是一个精巧的线程通信架构,其设计体现了以下几个关键思想:

组件 角色 设计亮点
Handler 消息生产者与消费者 提供易用的 API
MessageQueue 消息存储与调度 单链表 + 时间排序 + 阻塞唤醒机制
Looper 消息循环驱动 ThreadLocal + 无限循环
Message 消息载体 对象池复用 + 多种数据承载方式

1.核心优势

  • 线程安全:保证 UI 更新在主线程完成
  • 异步解耦:任务可在任意线程发起,在指定线程执行
  • 延迟执行:支持定时任务和延迟操作
  • 内存高效:Message 对象池减少 GC 压力
  • 扩展性强:支持同步屏障、空闲处理器等高级特性

2.应用场景

  • 子线程更新 UI
  • 延迟操作(如启动页倒计时)
  • 定时任务(需结合 removeCallbacks 防止泄漏)
  • 线程间状态同步

💡 尽管现代 Android 开发越来越多地使用 Coroutine、LiveData、RxJava 等替代方案,但 Handler 仍是底层基石,理解其原理有助于深入掌握 Android 消息机制。

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容