Handler机制

Handler机制的核心价值与设计背景

在Android中,只能在主线程(UI线程)中更新UI,这是为了保证UI操作的线程安全性。

常见的情况是:

  • 网络请求、文件读写、数据库操作等耗时任务必须在子线程执行
  • 这些任务完成后需要将结果反馈到UI线程更新界面
java 复制代码
// 错误示例:直接在子线程更新UI
new Thread(() -> {
    // 模拟耗时操作
    Thread.sleep(2000);
    // ❌ 直接更新UI会导致崩溃或不可预知的行为
    textView.setText("任务完成");
}).start();

Android采用了消息队列(Message Queue)模型来解决这个问题,而不是简单的线程同步机制(如锁、信号量)。这是因为:

  1. 解耦生产者和消费者:发送消息的线程不需要知道接收线程的状态
  2. 支持延迟和定时消息:可以调度未来某个时间点执行的任务
  3. 有序处理:消息按照先进先出(FIFO)的顺序处理
  4. 线程安全:消息队列内部实现了线程同步

通俗解释:

kotlin 复制代码
**传统方式(同步机制)** :没有驿站,直接送货
// 就像:快递员直接上门,你必须在家等着
class TraditionalDelivery {
    void deliverPackage() {
        synchronized(lock) {  // 就像敲门
            if (homeHasPerson) {  // 你在家吗?
                receivePackage();  // 签收
            } else {
                wait();  // 快递员在门口等着
            }
        }
    }
}

**消息队列方式**:有驿站
// Android的消息队列就像驿站
class MessageQueueExample {
    // 发送消息就像把快递放驿站
    fun sendMessage(msg: Message) {
        messageQueue.enqueueMessage(msg)  // 放入驿站
        // 放完就走,不用等收件人
    }
    
    // 主线程(收件人)有空时去驿站取
    fun processMessages() {
        while (true) {
            val msg = messageQueue.nextMessage()  // 去驿站取快递
            handleMessage(msg)  // 处理/签收
        }
    }
}

Handler机制由三个紧密协作的类组成:

组件 角色 类比
Handler 消息的发送者与处理者 快递员(收件和派件)
MessageQueue 消息的存储队列 快递仓库(暂存包裹)
Looper 消息的循环分发器 仓库分拣员(不断取件)

重要原则一个线程只能有一个Looper和一个MessageQueue,但可以有多个Handler。

核心组件

MessageQueue:消息队列

MessageQueue是Handler机制的存储核心,它内部维护了一个消息链表。

java 复制代码
// MessageQueue中的关键结构
public final class MessageQueue {
    // 消息队列的头部,链表结构
    private Message mMessages;
    
    // 标记是否需要退出循环
    private boolean mQuitting;
    
    // 用于线程同步的锁
    private final Object mLock = new Object();
    
    // 空闲处理器集合
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<>();
}

消息入队:当Handler发送消息时,最终会调用MessageQueue的enqueueMessage方法:

java 复制代码
boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        msg.markInUse();
        msg.when = when;
        
        Message p = mMessages;
        boolean needWake;
        
        // 情况1:队列为空,或新消息的执行时间最早
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;  // 如果当前阻塞,需要唤醒
        } 
        // 情况2:插入到链表中间合适位置
        else {
            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;
        }
        
        // 如果需要唤醒,则调用nativeWake
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}
  • 消息按执行时间(when)排序:不是简单的先进先出,而是按执行时间排序
  • 线程安全 :通过synchronized保证多线程安全
  • 唤醒机制:如果队列为空时Looper正在阻塞,插入新消息需要唤醒它

消息出队:Looper通过消息队列提供的next()方法从队列中获取消息:

java 复制代码
Message next() {
    for (;;) {
        // 1. 处理空闲处理器(IdleHandler)
        if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
            pendingIdleHandlerCount = mIdleHandlers.size();
        }
        if (pendingIdleHandlerCount <= 0) {
            // 没有空闲处理器,进入阻塞
            mBlocked = true;
            continue;
        }
        
        // 2. 从队列头部取消息
        synchronized (this) {
            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 {
                    // 消息可以执行了
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    return msg;
                }
            } else {
                // 队列为空,无限等待
                nextPollTimeoutMillis = -1;
            }
        }
        
        // 3. 没有消息,进入阻塞状态(Native层实现)
        nativePollOnce(ptr, nextPollTimeoutMillis);
    }
}
  • 阻塞优化:没有消息时,Looper会在Native层阻塞,不消耗CPU
  • 时间调度:消息按执行时间排序,未到时间的消息会等待
  • 空闲处理:在等待消息时,会执行IdleHandler

Looper:消息循环的引擎

Looper是让线程拥有消息循环能力的关键组件

Looper初始化:

java 复制代码
// 主线程的Looper在ActivityThread.main()中初始化
public static void main(String[] args) {
    // 1. 准备主线程Looper
    Looper.prepareMainLooper();
    
    // 2. 创建ActivityThread实例
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    
    // 3. 开始消息循环
    Looper.loop();
    
    // 4. 正常情况下不会执行到这里
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

普通线程创建Looper:

java 复制代码
class WorkerThread extends Thread {
    public Handler handler;
    
    @Override
    public void run() {
        // 1. 准备Looper(创建Looper和MessageQueue)
        Looper.prepare();
        
        // 2. 创建Handler,会自动绑定当前线程的Looper
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                // 处理消息
            }
        };
        
        // 3. 开始消息循环
        Looper.loop();
    }
    
    // 4. 安全退出循环
    public void quit() { 
        if (handler != null) { 
            handler.getLooper().quit();
        } 
    }
}

每个线程只能有一个Looper,这是通过ThreadLocal实现的:

java 复制代码
public final class Looper {
    // ThreadLocal存储每个线程的Looper实例
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
    
    // 准备Looper
    private static void prepare() {
        // 检查是否已经准备过
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("一个线程只能创建一个Looper");
        }
        // 创建Looper并存入ThreadLocal
        sThreadLocal.set(new Looper());
    }
    
    // 获取当前线程的Looper
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
}
  • 每个Thread对象内部有一个ThreadLocalMap
  • ThreadLocal作为key,Looper作为value存储
  • 这样每个线程都能独立访问自己的Looper,互不干扰

Looper.loop()是Handler机制的心脏:

java 复制代码
public static void loop() {
    // 1. 获取当前线程的Looper
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    
    // 2. 获取消息队列
    final MessageQueue queue = me.mQueue;
    
    // 3. 开始无限循环
    for (;;) {
        // 4. 获取下一条消息(可能会阻塞)
        Message msg = queue.next();
        if (msg == null) {
            // 没有消息,退出循环
            return;
        }
        
        // 5. 分发消息给Handler处理
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            // 确保消息回收
            msg.recycleUnchecked();
        }
    }
}

为什么loop()不会导致ANR?

  • ANR是因为主线程的Looper.loop()在某次消息处理时耗时过长
  • 不是loop()本身导致的,而是handleMessage()中的代码太耗时
  • queue.next()在没有消息时会阻塞,不消耗CPU

loop()如何退出?

  • 调用Looper.quit()Looper.quitSafely()
  • quit()立即退出,丢弃所有未处理消息
  • quitSafely()处理完所有非延迟消息后退出

Handler:消息的生产者与消费者

Handler是开发者最直接接触的组件,它既是消息的发送者 ,也是处理者

Handler必须与一个Looper关联,有几种构造方式:

java 复制代码
// 方式1:默认关联当前线程的Looper
// 如果当前线程没有Looper会崩溃
Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        // 处理消息
    }
};

// 方式2:显式指定Looper
Handler handler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        // 在主线程处理消息
    }
};

// 方式3:指定Looper和回调
Handler.Callback callback = new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        // 处理消息,返回true表示已处理
        return true;
    }
};
Handler handler = new Handler(Looper.myLooper(), callback);

源码关键点

java 复制代码
public Handler(@Nullable Callback callback, boolean async) {
    // 1. 获取当前线程的Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    
    // 2. 绑定消息队列
    mQueue = mLooper.mQueue;
    
    // 3. 设置回调和异步标志
    mCallback = callback;
    mAsynchronous = async;
}

消息的发送:Handler提供了两类方法发送消息:

sendMessage系列:发送带有what、arg1、arg2、obj等数据的消息

java 复制代码
// 发送空消息
handler.sendEmptyMessage(WHAT_CODE);

// 发送延迟消息
handler.sendMessageDelayed(msg, 1000);  // 1秒后执行

// 发送指定时间消息
handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + 1000);

// 发送到队列头部(紧急消息)
handler.sendMessageAtFrontOfQueue(msg);

post系列:发送Runnable任务

java 复制代码
// 立即执行
handler.post(() -> {
    // 在主线程执行
    updateUI();
});

// 延迟执行
handler.postDelayed(() -> {
    // 1秒后执行
    doSomething();
}, 1000);

// 带token的post,可以批量取消
Object token = new Object();
handler.postAtTime(runnable, token, SystemClock.uptimeMillis() + 1000);

底层实现post方法最终也会转换成Message

java 复制代码
public final boolean post(Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;  // Runnable存储在callback字段
    return m;
}

当Looper从队列中取出消息后,会调用handler.dispatchMessage()

java 复制代码
public void dispatchMessage(Message msg) {
    // 1. 如果Message有callback(通过post发送的Runnable),优先执行
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 2. 如果Handler设置了全局Callback
        if (mCallback != null) {
            // Callback可以拦截消息处理
            if (mCallback.handleMessage(msg)) {
                return;  // 已处理,不再往下传递
            }
        }
        // 3. 最后调用子类实现的handleMessage
        handleMessage(msg);
    }
}

private static void handleCallback(Message message) {
    message.callback.run();
}

处理优先级:Message.callback(Runnable)→ 2. Handler.Callback → 3. Handler.handleMessage()

简单理解,当前handler如果接收到的消息中有Runnable,则执行,否则,如果handler设置了Callback,则执行Callback,否则执行重写的handleMessage方法。

Message的复用机制与内存优化

Message是Handler机制中传递的数据单元,它的复用机制体现了Android对性能的优化。 Message的结构:

java 复制代码
public final class Message implements Parcelable {
    // 标识符,用于区分消息类型
    public int what;
    
    // 两个整型参数
    public int arg1;
    public int arg2;
    
    // 任意对象
    public Object obj;
    
    // 目标Handler
    /*package*/ Handler target;
    
    // Runnable回调
    /*package*/ Runnable callback;
    
    // 下一条消息(链表结构)
    /*package*/ Message next;
    
    // 消息池(静态变量)
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;
}

为了避免频繁创建Message对象导致内存抖动,Android实现了Message对象池:

java 复制代码
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            // 从池中取一个Message
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0;  // 清除IN_USE标志
            sPoolSize--;
            return m;
        }
    }
    // 池为空,创建新对象
    return new Message();
}

void recycleUnchecked() {
    // 清空消息内容
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;
    
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            // 放入池中
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

使用方法:

java 复制代码
// 推荐:使用obtain()获取Message
Message msg = Message.obtain();
msg.what = MSG_UPDATE_UI;
msg.obj = data;
handler.sendMessage(msg);

// 不推荐:直接new Message()
Message msg = new Message();  // 可能造成内存抖动

高级特性与底层机制

同步屏障

同步屏障是一种特殊的消息,用于优先处理异步消息

  • 同步消息:普通通过Handler发送的消息
  • 异步消息 :通过setAsynchronous(true)标记的消息
  • 同步屏障:一个target为null的Message,遇到它时,会跳过所有同步消息,只处理异步消息

典型应用:View的绘制

java 复制代码
// 在ViewRootImpl中
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        // 1. 设置同步屏障
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        
        // 2. 发送异步的绘制消息
        mChoreographer.postCallback(
            Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}

源码中的屏障处理

java 复制代码
Message next() {
    for (;;) {
        synchronized (this) {
            Message prevMsg = null;
            Message msg = mMessages;
            
            // 遇到同步屏障
            if (msg != null && msg.target == null) {
                // 跳过所有同步消息,只找异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            // ... 处理找到的消息
        }
    }
}

创建异步消息的方式

java 复制代码
// 方式1:创建Handler时指定
Handler handler = new Handler(Looper.myLooper(), null, true);  // 第三个参数为async

// 方式2:标记已有Message
Message msg = Message.obtain();
msg.setAsynchronous(true);
handler.sendMessage(msg);

// 方式3:post异步任务
handler.post(() -> {
    // 异步任务
    doAsyncTask();
});

这里易混淆,handler.post()本身只是把Runnable包装成Message发送出去,这个Message是否是异步的,取决于:

  1. Handler本身是否是异步的(方式1)
  2. 手动设置Message为异步,即doAsyncTask()是异步的(方式3)

IdleHandler:空闲时执行的任务

IdleHandler允许在消息队列空闲时执行任务。

  • 延迟初始化:等主线程空闲时再初始化非紧急组件
  • 批量操作:收集多次变化,一次性处理
  • 性能监控:检测主线程是否卡顿

使用方法

java 复制代码
// 添加IdleHandler
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
    @Override
    public boolean queueIdle() {
        // 在队列空闲时执行
        doBackgroundWork();
        
        // 返回true表示继续保留,下次空闲还会执行
        // 返回false表示执行一次后移除
        return false;
    }
});

在MessageQueue的next()方法中:

java 复制代码
Message next() {
    for (;;) {
        // ... 检查消息队列
        
        // 空闲时处理IdleHandler
        if (pendingIdleHandlerCount < 0 
            && (mMessages == null || now < mMessages.when)) {
            pendingIdleHandlerCount = mIdleHandlers.size();
        }
        
        if (pendingIdleHandlerCount <= 0) {
            // 没有IdleHandler,继续阻塞
            mBlocked = true;
            continue;
        }
        
        // 执行IdleHandler
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null;  // 释放引用
            
            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf("MessageQueue", "IdleHandler threw exception", t);
            }
            
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
    }
}

Native层的实现

MessageQueue的阻塞和唤醒实际上是在Native层实现的:

java 复制代码
// Java层
public final class MessageQueue {
    // Native层的指针
    private long mPtr;  // 指向NativeMessageQueue
    
    private native static long nativeInit();
    private native static void nativeDestroy(long ptr);
    private native void nativePollOnce(long ptr, int timeoutMillis);
    private native static void nativeWake(long ptr);
}

// Native层(C++)
class NativeMessageQueue : public MessageQueue {
    // 使用Linux的epoll机制实现高效I/O多路复用
    Looper* mLooper;
    
    void pollOnce(int timeoutMillis) {
        mLooper->pollOnce(timeoutMillis);
    }
    
    void wake() {
        mLooper->wake();
    }
}

为什么要在Native层实现阻塞?

  1. 效率更高:避免Java层循环空转消耗CPU
  2. 精准唤醒:使用epoll等系统调用,精确控制线程唤醒
  3. 统一管理:与Input系统、Binder等共用事件循环

内存泄漏问题与解决方案

Handler内存泄漏是Android开发中最常见的问题之一。

泄露的根本原因

java 复制代码
public class MainActivity extends AppCompatActivity {
    // 非静态内部类隐式持有外部类引用
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // 隐式持有MainActivity.this的引用
            updateUI();
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 发送延迟消息
        mHandler.sendEmptyMessageDelayed(0, 60000);  // 60秒后执行
        
        // 如果Activity在60秒内被销毁
        // 但MessageQueue中还有未处理的消息引用着Handler
        // Handler又引用着Activity,导致Activity无法被回收
    }
}

解决方案

方案1:静态内部类 + 弱引用(最常用)

java 复制代码
public class MainActivity extends AppCompatActivity {
    private SafeHandler mSafeHandler;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 创建安全的Handler
        mSafeHandler = new SafeHandler(this);
    }
    
    // 静态内部类,不持有外部类的引用
    private static class SafeHandler extends Handler {
        // 使用弱引用持有Activity
        private final WeakReference<MainActivity> mActivityRef;
        
        public SafeHandler(MainActivity activity) {
            super(Looper.getMainLooper());
            mActivityRef = new WeakReference<>(activity);
        }
        
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = mActivityRef.get();
            if (activity != null && !activity.isFinishing()) {
                // Activity还存在,可以安全使用
                activity.handleMessage(msg);
            } else {
                // Activity已被回收,清理消息
                removeCallbacksAndMessages(null);
            }
        }
    }
    
    // Activity中的处理方法
    private void handleMessage(Message msg) {
        switch (msg.what) {
            case 1:
                updateUI();
                break;
        }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 清理Handler
        if (mSafeHandler != null) {
            mSafeHandler.removeCallbacksAndMessages(null);
        }
    }
}

方案2:使用Lifecycle + ViewModel(现代Android架构)

kotlin 复制代码
public class MainActivity extends AppCompatActivity {
// 使用ViewModel + LiveData
class MainViewModel : ViewModel() {
    private val handler = Handler(Looper.getMainLooper())
    
    fun doTaskWithDelay(delayMillis: Long, task: () -> Unit) {
        handler.postDelayed({
            task()
        }, delayMillis)
    }
    
    override fun onCleared() {
        super.onCleared()
        // ViewModel销毁时自动清理
        handler.removeCallbacksAndMessages(null)
    }
}

// Activity中使用
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 安全的延迟任务
        viewModel.doTaskWithDelay(30000) {
            updateUI()  // 如果Activity已销毁,这里不会执行
        }
    }
}

总结

  1. 三组件关系

    一个线程 ↔ 一个Looper ↔ 一个MessageQueue ↔ 多个Handler

  2. 消息循环流程

    Handler发送消息 → MessageQueue入队排序 → Looper循环取出 → 分发回Handler处理

  3. 内存管理

    • 使用Message.obtain()复用对象
    • 注意Handler内存泄漏问题
    • 及时移除不需要的消息
  4. 高级特性

    • 同步屏障用于优先处理异步消息(如UI绘制)
    • IdleHandler用于空闲时执行任务
    • Native层实现高效阻塞唤醒
相关推荐
福大大架构师每日一题1 天前
milvus v2.6.8 发布:搜索高亮上线,性能与稳定性全面跃升,生产环境强烈推荐升级
android·java·milvus
草莓熊Lotso1 天前
脉脉独家【AI创作者xAMA】| 开启智能创作新时代
android·java·开发语言·c++·人工智能·脉脉
李坤林1 天前
Android Binder详解【5】 ServiceManager
android·binder
Ya-Jun1 天前
Android 扫雷游戏项目设计报告
android·游戏
_李小白1 天前
【Android 性能分析】第五天:Perfetto UI分析CPU
android·ui
MindCareers1 天前
Beta Sprint Day 1-2: Alpha Issue Fixes Initiated + Mobile Project Setup
android·c语言·数据库·c++·qt·sprint·issue
龚礼鹏1 天前
Android应用程序 c/c++ 崩溃排查流程三——ndk-stack工具使用
android
zhengfei6111 天前
CVE-2025-13156 - Vitepos WooCommerce 的销售(POS) 系统漏洞
android
奥陌陌1 天前
自定义view, 图片右上角显示数字
android