关注

Android Handler 机制原理详解

一、 概述

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

该机制的核心目标是:

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

二、 核心组件

1. Handler(处理器)

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

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

⚠️ 每个 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. 整体流程图

在这里插入图片描述

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 请求重绘时插入屏障,确保 ChoreographerVSYNC 消息优先执行
// 插入同步屏障
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();

建议在 ThreadonDestroyquit() 方法中调用


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 实现轮询不推荐,应使用 AlarmManagerWorkManager 替代

九、总结

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

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

1.核心优势

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

2.应用场景

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

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

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/csdn_silent/article/details/153244375

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--