首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Android Handler 机制原理详解

Android Handler 机制原理详解

原创
作者头像
木易士心
发布2025-11-12 07:43:41
发布2025-11-12 07:43:41
2900
举报

@TOC

一、 概述

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

该机制的核心目标是:

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

二、 核心组件

1. Handler(处理器)

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

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

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

代码语言:java
复制
// 示例:创建 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 保证线程局部性
代码语言:java
复制
// 主线程中系统自动调用

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

代码语言:java
复制
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

代码语言:java
复制
// 示例:多种发送方式

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() 是消息分发的核心逻辑:

代码语言:java
复制
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 核心实现

代码语言:java
复制
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() —— 入队

代码语言:java
复制
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() —— 出队(核心阻塞方法)

代码语言:java
复制
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 消息优先执行
代码语言:java
复制
// 插入同步屏障

int token = mHandler.getLooper().getQueue().postSyncBarrier();



// 移除屏障

mHandler.getLooper().getQueue().removeSyncBarrier(token);

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

2. 空闲处理器(IdleHandler)

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

代码语言:java
复制
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {

    @Override

    public boolean queueIdle() {

        Log.d("IdleHandler", "消息队列空闲,执行预加载");

        // 返回 true:保持注册;false:执行一次后移除

        return false;

    }

});

典型用途:

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

3. 异步消息(Asynchronous Message)

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

代码语言:java
复制
Message msg = Message.obtain(handler, runnable);

msg.setAsynchronous(true);

handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + 1000);

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

六、内存优化机制

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

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

代码语言:java
复制
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 引用,导致无法回收。

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

代码语言:java
复制
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(第三方库)

代码语言:java
复制
// 如 Square 的 LeakCanary 推荐方式

WeakHandler weakHandler = new WeakHandler(this, new Handler.Callback() {

    @Override

    public boolean handleMessage(@NonNull Message msg) {

        // 安全处理

        return true;

    }

});

2. 正确退出 Looper

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

代码语言:java
复制
// 安全退出:处理完已到时的消息再退出

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 消息机制。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、 概述
  • 二、 核心组件
    • 1. Handler(处理器)
    • 2. MessageQueue(消息队列)
    • 3. Looper(循环器)
    • 4. Message(消息)
  • 三、工作原理
    • 1. 整体流程图
    • 2. 详细执行过程
      • 1. 初始化 Looper(子线程示例)
      • 2. 消息发送机制
      • 3. 消息处理流程
  • 四、源码分析(精简版)
    • 1. Looper 核心实现
    • 2. MessageQueue 关键方法
      • enqueueMessage() —— 入队
      • next() —— 出队(核心阻塞方法)
  • 五、高级特性
    • 1. 同步屏障(Sync Barrier)
    • 2. 空闲处理器(IdleHandler)
    • 3. 异步消息(Asynchronous Message)
  • 六、内存优化机制
    • 1. Message 对象池(对象复用)
  • 七、 使用注意事项
    • 1. 避免内存泄漏
      • 解决方案:静态内部类 + 弱引用
      • 或使用 WeakHandler(第三方库)
    • 2. 正确退出 Looper
    • 3. 主线程 vs 子线程
  • 八、常见问题与使用建议
  • 九、总结
    • 1.核心优势
    • 2.应用场景
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档