Handler基本概念

0 阅读20分钟

Handler基本概念

一、Handler 是什么

1.1 定义

Handler 是 Android 消息机制中的核心类,用于在不同线程之间传递和处理消息。它通过结合 Message、MessageQueue 和 Looper,提供了一套完整的异步消息处理框架。

核心作用:

  • 线程间通信:将任务从工作线程切换到主线程执行
  • 延迟执行:支持延迟消息和定时任务
  • 消息调度:管理消息队列,按时间顺序处理消息

为什么需要 Handler:

  • Android UI 线程不安全:只能在主线程更新 UI
  • 耗时操作必须在子线程:避免 ANR(Application Not Responding)
  • 线程切换需求:子线程完成任务后需要通知主线程更新 UI

1.2 在 Android 架构中的定位

Handler 位于 Android Framework 层,是连接应用层和底层消息机制的桥梁。

应用层 Activity/Fragment
    ↓ 使用
Handler (Framework 层)
    ↓ 依赖
MessageQueue + Looper (Framework 层)
    ↓ 底层实现
Native MessageQueue + epoll (Native 层)

典型应用场景:

  1. 子线程更新 UI:网络请求完成后更新界面
  2. 定时任务:倒计时、轮询刷新
  3. 线程协作:多线程任务协调
  4. 系统服务:ActivityManagerService、WindowManagerService 内部大量使用

二、Handler 四大核心类

Handler 消息机制由四个核心类协作完成,理解它们的关系是掌握 Handler 的关键。

2.1 四大核心类关系图

┌─────────────────────────────────────────────────┐
│                   Thread                        │
│  ┌──────────────────────────────────────────┐  │
│  │              Looper                      │  │
│  │  - sThreadLocal: ThreadLocal<Looper>    │  │
│  │  - mQueue: MessageQueue                 │  │
│  │  - loop(): 循环取消息                    │  │
│  └──────────────────────────────────────────┘  │
│                     ↓ 包含                      │
│  ┌──────────────────────────────────────────┐  │
│  │          MessageQueue                    │  │
│  │  - mMessages: Message (链表头)          │  │
│  │  - next(): 取出下一条消息               │  │
│  │  - enqueueMessage(): 插入消息           │  │
│  └──────────────────────────────────────────┘  │
│         ↑ 入队                  ↓ 出队          │
│  ┌──────────┐          ┌──────────────────┐   │
│  │ Handler  │          │     Message      │   │
│  │ - mLooper│          │ - target:Handler │   │
│  │ - mQueue │          │ - when: long     │   │
│  │ - send() │          │ - next: Message  │   │
│  │ - post() │          │                  │   │
│  └──────────┘          └──────────────────┘   │
└─────────────────────────────────────────────────┘

2.2 Handler - 消息发送与处理者

源码路径: frameworks/base/core/java/android/os/Handler.java

核心职责:

  1. 发送消息到 MessageQueue
  2. 处理 Looper 分发的消息

关键字段:

// Handler.java (Android 12)
public class Handler {
    final Looper mLooper;           // 关联的 Looper
    final MessageQueue mQueue;      // 关联的消息队列
    final Callback mCallback;       // 消息处理回调
    final boolean mAsynchronous;    // 是否是异步 Handler

    public Handler(@Nullable Callback callback, boolean async) {
        mLooper = Looper.myLooper();  // 获取当前线程的 Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;       // 获取 Looper 的消息队列
        mCallback = callback;
        mAsynchronous = async;
    }
}

关键方法:

方法作用使用场景
sendMessage(Message)发送消息需要传递数据
sendEmptyMessage(int)发送空消息只需要消息标识
post(Runnable)发送 Runnable简单的任务执行
sendMessageDelayed()延迟发送消息定时任务
handleMessage(Message)处理消息子类重写处理逻辑
removeMessages(int)移除消息取消待执行的任务

2.3 Message - 消息载体

源码路径: frameworks/base/core/java/android/os/Message.java

核心职责: 封装需要传递的数据和目标 Handler。

关键字段:

// Message.java (Android 12)
public final class Message implements Parcelable {
    public int what;              // 消息标识
    public int arg1;              // 整型参数1
    public int arg2;              // 整型参数2
    public Object obj;            // 任意对象
    public long when;             // 消息执行时间
    public Bundle data;           // 复杂数据

    /*package*/ Handler target;   // 目标 Handler
    /*package*/ Runnable callback;// Runnable 回调
    /*package*/ Message next;     // 链表指针

    private static Message sPool; // 消息池头节点
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50; // 最大池容量
}

消息池机制:

Message 采用享元模式实现对象池复用,避免频繁创建销毁对象。内部维护一个最大容量为 50 的单链表结构对象池。

核心特性:

  • obtain():从对象池获取可复用的 Message,池为空则创建新对象
  • recycleUnchecked():清空字段数据并放回对象池
  • 最佳实践:使用 Message.obtain() 而非 new Message()

详细源码实现:详见 ../04-Message对象池/Message对象池原理.md

2.4 MessageQueue - 消息队列

源码路径: frameworks/base/core/java/android/os/MessageQueue.java

核心职责: 维护消息链表,按时间顺序管理消息。

关键字段:

// MessageQueue.java (Android 12)
public final class MessageQueue {
    private long mPtr; // Native 层 NativeMessageQueue 指针

    Message mMessages; // 消息链表头节点

    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<>();
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuitting;

    private boolean mBlocked;
    private int mNextBarrierToken;
}

核心数据结构:

MessageQueue 本质是一个按时间排序的单链表:

mMessages → [when=100][when=200][when=500] → null
            (最早执行)                  (最晚执行)

关键方法:

  • enqueueMessage(Message, long):将消息按时间戳插入链表
  • next():取出下一条可执行消息,无消息时阻塞
  • postSyncBarrier():设置同步屏障,优先处理异步消息

重要特性:

  • 优先队列:按 when 时间戳排序,最早执行的消息在链表头部
  • 阻塞唤醒:底层使用 epoll 机制,无消息时阻塞,有消息时唤醒
  • 同步屏障:支持同步屏障机制优先处理异步消息

消息入队详细流程:详见 ../03-MessageQueue队列管理/01-消息入队enqueueMessage.md 消息出队详细流程:详见 ../03-MessageQueue队列管理/02-消息出队next.md epoll 阻塞唤醒机制:详见 ../03-MessageQueue队列管理/04-epoll机制.md

2.5 Looper - 消息循环器

源码路径: frameworks/base/core/java/android/os/Looper.java

核心职责: 在当前线程开启消息循环,不断从 MessageQueue 取消息分发给 Handler。

关键字段:

// Looper.java (Android 12)
public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // 主线程 Looper

    final MessageQueue mQueue;
    final Thread mThread;

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

核心方法:

// Looper.java (Android 12)
// 1. 准备 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));
}

// 2. 开启消息循环
public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;

    for (;;) {
        Message msg = queue.next(); // 阻塞式取消息
        if (msg == null) return;    // 退出循环

        msg.target.dispatchMessage(msg); // 分发给 Handler
        msg.recycleUnchecked();          // 回收消息
    }
}

// 3. 获取当前线程 Looper
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

重要特性:

  • 线程本地存储:每个线程最多一个 Looper
  • 无限循环for(;;) 持续运行,不会退出(除非调用 quit()
  • 主线程 Looper:由 ActivityThread.main() 自动创建

2.6 四大核心类协作流程

┌─────────────┐
│ 1. Handler  │ sendMessage(msg)
│   创建消息   │────────┐
└─────────────┘        │
                       ↓
┌─────────────────────────────────┐
│ 2. MessageQueue                 │
│    按时间顺序插入链表            │
│    [msg1][msg2][msg3]     │
└─────────────────────────────────┘
                       │
                       ↓
┌─────────────────────────────────┐
│ 3. Looper.loop()                │
│    for(;;) {                    │
│      msg = queue.next();        │
│      msg.target.dispatchMessage │
│    }                            │
└─────────────────────────────────┘
                       │
                       ↓
┌─────────────────────────────────┐
│ 4. Handler                      │
│    handleMessage(msg)           │
│    处理消息                      │
└─────────────────────────────────┘

三、Handler 基本工作机制

3.1 完整工作流程概述

Handler 消息机制的核心是生产者-消费者模型

  1. 准备阶段:线程调用 Looper.prepare() 创建 Looper 和 MessageQueue
  2. 发送阶段:Handler 将 Message 插入 MessageQueue
  3. 循环阶段Looper.loop() 不断从 MessageQueue 取消息
  4. 处理阶段:Looper 将消息分发给对应的 Handler 处理

时序图:

Thread A (子线程)          MessageQueue          Thread B (主线程 + Looper)
    │                          │                        │
    │ handler.sendMessage()    │                        │
    │─────────────────────────→│                        │
    │                          │ enqueueMessage()       │
    │                          │ (插入链表)              │
    │                          │                        │
    │                          │←───────────────────────│
    │                          │   queue.next()         │
    │                          │                        │
    │                          │───────────────────────→│
    │                          │   返回 Message          │
    │                          │                        │
    │                          │                        │
    │                          │                        │ handler.dispatchMessage()
    │                          │                        │ handleMessage()
    │                          │                        │ (处理消息)

3.2 线程切换原理

核心问题:为什么 Handler 能实现线程切换?

// 示例代码
new Thread(() -> {
    // 子线程
    handler.sendMessage(msg); // ← 在子线程发送
}).start();

// Handler 在主线程创建
Handler handler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        // ← 在主线程执行
        textView.setText("更新 UI");
    }
};

原理解析:

// Handler.java (Android 12)
public Handler(@NonNull Looper looper) {
    mLooper = looper;         // ← 关键:绑定主线程 Looper
    mQueue = looper.mQueue;   // ← 使用主线程的 MessageQueue
}

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue; // ← 插入主线程的队列
    return enqueueMessage(queue, msg, uptimeMillis);
}

关键点:

  1. Handler 创建时绑定了主线程的 Looper
  2. 无论在哪个线程调用 sendMessage(),消息都会插入主线程的 MessageQueue
  3. 主线程的 Looper 循环取出消息后,在主线程调用 handleMessage()

3.3 消息处理的三级分发

Handler 处理消息时有三级优先级:

// Handler.java (Android 12)
public void dispatchMessage(@NonNull Message msg) {
    // 优先级1:Message 自带的 callback (Runnable)
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 优先级2:Handler 的 mCallback
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return; // 返回 true 则拦截
            }
        }
        // 优先级3:Handler 子类重写的 handleMessage()
        handleMessage(msg);
    }
}

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

分发优先级:

优先级来源触发条件典型使用
1msg.callbackhandler.post(Runnable)简单任务
2handler.mCallback创建 Handler 时传入拦截处理
3handleMessage()子类重写常规使用

示例:

// 优先级1:post 方式
handler.post(() -> {
    // msg.callback.run()
    textView.setText("优先级1");
});

// 优先级2:Callback 拦截
Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        // 返回 true 拦截,不再调用优先级3
        return true;
    }
});

// 优先级3:子类重写
Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        // 常规处理
    }
};

四、源码关键实现细节

4.1 ThreadLocal 实现 Looper 线程隔离

问题:如何保证每个线程只有一个 Looper?

// Looper.java (Android 12)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

public static void prepare() {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(true));
}

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

ThreadLocal 原理:

// ThreadLocal.java (Android 12)
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t); // 获取线程的 Map
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            return (T)e.value;
        }
    }
    return setInitialValue();
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals; // 每个 Thread 对象都有独立的 Map
}

核心机制:

  • 每个 Thread 内部有一个 ThreadLocalMap
  • ThreadLocal 作为 key,Looper 作为 value
  • 不同线程的 Map 互不影响,实现隔离

4.2 MessageQueue 的阻塞与唤醒

问题:队列为空时如何避免空转浪费 CPU?

核心机制:

MessageQueue 底层使用 Linux epoll 机制实现高效的阻塞与唤醒:

// MessageQueue.java - next() 方法核心流程
for (;;) {
    nativePollOnce(ptr, nextPollTimeoutMillis); // 阻塞等待

    synchronized (this) {
        Message msg = mMessages;
        if (msg != null && now >= msg.when) {
            return msg;  // 返回可执行消息
        } else if (msg != null) {
            nextPollTimeoutMillis = msg.when - now; // 计算等待时间
        } else {
            nextPollTimeoutMillis = -1; // 无消息,无限等待
        }
    }
}

阻塞时机:

  • 队列为空nextPollTimeoutMillis = -1,无限阻塞
  • 有延迟消息:阻塞到消息执行时间
  • 有即时消息:立即返回

唤醒条件:

  • 新消息插入队列头部
  • 消息队列从空变为非空

epoll 详细实现:详见 ../03-MessageQueue队列管理/04-epoll机制.md 消息出队完整流程:详见 ../03-MessageQueue队列管理/02-消息出队next.md

4.3 主线程 Looper 的创建时机

问题:主线程的 Looper 何时创建?

// ActivityThread.java (Android 12)
public static void main(String[] args) {
    // ...

    Looper.prepareMainLooper(); // ← 创建主线程 Looper

    // ...
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    // ...
    Looper.loop(); // ← 开启主线程消息循环

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

主线程 Looper 特点:

// Looper.java (Android 12)
public static void prepareMainLooper() {
    prepare(false); // quitAllowed = false,主线程 Looper 不允许退出
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

关键点:

  1. 应用启动时,ActivityThread.main() 自动创建主线程 Looper
  2. 主线程 Looper 不允许退出(quitAllowed = false
  3. 可通过 Looper.getMainLooper() 在任意线程获取主线程 Looper

五、实战应用场景

5.1 子线程更新 UI

public class NetworkActivity extends AppCompatActivity {
    private TextView tvResult;
    private Handler mainHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_network);
        tvResult = findViewById(R.id.tv_result);

        // 创建主线程 Handler
        mainHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == 1) {
                    String result = (String) msg.obj;
                    tvResult.setText(result); // 在主线程更新 UI
                }
            }
        };

        loadData();
    }

    private void loadData() {
        new Thread(() -> {
            // 模拟网络请求
            try {
                Thread.sleep(2000);
                String data = "服务器返回数据";

                // 发送消息到主线程
                Message msg = Message.obtain();
                msg.what = 1;
                msg.obj = data;
                mainHandler.sendMessage(msg);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

5.2 延迟任务与定时轮询

public class CountdownActivity extends AppCompatActivity {
    private TextView tvCountdown;
    private Handler handler;
    private int countdown = 60;

    private Runnable countdownTask = new Runnable() {
        @Override
        public void run() {
            if (countdown > 0) {
                tvCountdown.setText(countdown + "秒");
                countdown--;
                handler.postDelayed(this, 1000); // 延迟1秒再次执行
            } else {
                tvCountdown.setText("倒计时结束");
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_countdown);
        tvCountdown = findViewById(R.id.tv_countdown);

        handler = new Handler(Looper.getMainLooper());
        handler.post(countdownTask); // 开始倒计时
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacks(countdownTask); // 移除任务,避免内存泄漏
    }
}

5.3 HandlerThread 的使用

HandlerThread 是 Android 提供的封装了 Looper 的 Thread 类。

public class HandlerThreadExample {
    private HandlerThread handlerThread;
    private Handler workHandler;

    public void start() {
        // 创建 HandlerThread
        handlerThread = new HandlerThread("WorkThread");
        handlerThread.start();

        // 获取 HandlerThread 的 Looper
        workHandler = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                // 在子线程执行耗时任务
                processData(msg.obj);
            }
        };
    }

    public void doWork(Object data) {
        Message msg = Message.obtain();
        msg.obj = data;
        workHandler.sendMessage(msg);
    }

    private void processData(Object data) {
        // 耗时操作,在子线程执行
        try {
            Thread.sleep(1000);
            Log.d("HandlerThread", "处理数据: " + data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void stop() {
        handlerThread.quitSafely(); // 安全退出
    }
}

HandlerThread 源码:

// HandlerThread.java (Android 12)
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare(); // 创建 Looper
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll(); // 通知 getLooper() 方法
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop(); // 开启循环
        mTid = -1;
    }

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait(); // 等待 run() 方法创建 Looper
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
}

六、面试题精选

6.1 基础题(P5 级别)

问题1:Handler、Looper、MessageQueue 的关系是什么?

标准答案(30秒): Handler 消息机制由三个核心类组成:Handler 负责发送和处理消息,MessageQueue 负责存储消息队列,Looper 负责循环取消息。它们的关系是:一个线程只有一个 Looper,一个 Looper 包含一个 MessageQueue,可以有多个 Handler 共享同一个 Looper 的 MessageQueue。发送消息时 Handler 将消息插入 MessageQueue,Looper 循环从 MessageQueue 取消息后分发给对应的 Handler 处理。

追问1:一个线程可以有几个 Looper?几个 Handler?

答案: 一个线程只能有一个 Looper,但可以有多个 Handler。

原因:

  • Looper 通过 ThreadLocal 存储,Looper.prepare() 时会检查当前线程是否已存在 Looper,如果存在则抛出异常。
  • Handler 创建时需要指定 Looper,多个 Handler 可以绑定同一个 Looper,共享同一个 MessageQueue。

追问2:为什么主线程不需要调用 Looper.prepare()?

答案: 主线程的 Looper 在应用启动时已经自动创建。应用启动时会执行 ActivityThread.main() 方法,其中调用了 Looper.prepareMainLooper() 创建主线程 Looper,并调用 Looper.loop() 开启消息循环。所以开发者在主线程使用 Handler 时不需要手动调用 Looper.prepare()


问题2:Handler 如何实现线程切换?

标准答案(30秒): Handler 实现线程切换的关键在于 Handler 创建时绑定的 Looper。Handler 在哪个线程创建就会绑定那个线程的 Looper,后续无论在哪个线程调用 sendMessage,消息都会被插入到创建时绑定的 Looper 的 MessageQueue 中,最终由 Looper 所在的线程取出并执行 handleMessage。例如主线程创建 Handler 后,子线程调用 sendMessage,消息会插入主线程 MessageQueue,由主线程的 Looper 取出后在主线程执行,从而实现线程切换。

追问1:Handler 在子线程创建时需要注意什么?

答案: 子线程默认没有 Looper,需要手动调用 Looper.prepare() 创建 Looper,然后创建 Handler,最后调用 Looper.loop() 开启消息循环。使用完毕后还需要调用 Looper.quit()Looper.quitSafely() 退出循环,释放资源。

示例:

new Thread(() -> {
    Looper.prepare();           // 1. 创建 Looper
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // 处理消息
        }
    };
    Looper.loop();              // 2. 开启循环
}).start();

追问2:如何在子线程向主线程发送消息?

答案: 有两种方式:

  1. 使用主线程 Looper 创建 Handlernew Handler(Looper.getMainLooper())
  2. 在主线程创建 Handler 并传递给子线程使用
// 方式1:使用主线程 Looper
Handler mainHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        // 在主线程执行
    }
};

new Thread(() -> {
    mainHandler.sendMessage(msg); // 子线程发送
}).start();

问题3:Message 的 obtain 方法有什么作用?

标准答案(30秒): Message.obtain() 用于从消息池中获取可复用的 Message 对象,避免频繁创建销毁对象。Message 内部维护了一个最大容量为 50 的对象池,使用单链表结构存储。当调用 obtain 时会从池中取出一个 Message 复用,如果池为空则创建新对象。消息处理完毕后会自动调用 recycleUnchecked 方法清空数据并放回池中。使用 obtain 而非 new Message 能减少内存分配,提升性能。

追问1:Message 对象池的最大容量是多少?为什么要限制容量?

答案: 最大容量是 50 个。限制容量的原因:

  1. 避免内存占用过大:Message 对象可能携带大量数据(Bundle、Object 等),无限制缓存会占用过多内存
  2. 平衡性能与内存:50 个对象足够应对大部分高频消息场景
  3. 防止内存泄漏:超过容量的对象会被 GC 回收,避免长期持有

追问2:Message 回收时为什么要清空所有字段?

答案: 防止内存泄漏。Message 可能持有 Activity、View 等对象的引用,如果不清空直接放回对象池,即使外部不再使用该 Message,这些对象也无法被 GC 回收,导致内存泄漏。清空字段(what、obj、data、callback、target 等)可以断开引用链,使对象能够正常回收。


6.2 进阶题(P6+ 级别)

问题4:Handler 导致的内存泄漏原因及解决方案?

标准答案(30秒): Handler 内存泄漏的根本原因是消息队列持有 Message,Message 持有 Handler,非静态内部类 Handler 持有外部类 Activity 引用。当 Activity 销毁时,如果消息队列中还有待处理的消息,整个引用链无法释放导致泄漏。解决方案:一是使用静态内部类加弱引用,二是在 Activity 销毁时移除所有消息。推荐使用静态内部类方式,既能避免泄漏又能保证消息正常处理。

详细分析:

泄漏链路:

MessageQueue → Message → Handler → Activity
     ↑                                  ↓
     └──────────── 无法释放 ─────────────┘

错误示例:

public class LeakActivity extends AppCompatActivity {
    // 非静态内部类持有 Activity 引用
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // 处理消息
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 发送延迟消息
        handler.sendEmptyMessageDelayed(1, 60000); // 60秒后执行
    }

    // Activity 销毁时,消息队列中的消息仍持有 Handler 引用
    // Handler 持有 Activity 引用,导致 Activity 无法回收
}

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

public class SafeActivity extends AppCompatActivity {
    private TextView textView;
    private SafeHandler handler;

    private static class SafeHandler extends Handler {
        private final WeakReference<SafeActivity> weakReference;

        public SafeHandler(SafeActivity activity) {
            weakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            SafeActivity activity = weakReference.get();
            if (activity != null) {
                activity.textView.setText("更新 UI");
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        textView = findViewById(R.id.tv_text);
        handler = new SafeHandler(this);
        handler.sendEmptyMessageDelayed(1, 60000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacksAndMessages(null); // 移除所有消息
    }
}

解决方案2:Activity 销毁时移除消息

@Override
protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacksAndMessages(null); // 参数为 null 表示移除所有消息
}

追问1:为什么使用静态内部类就不会持有外部类引用?

答案: Java 中非静态内部类会隐式持有外部类的引用(通过 OuterClass.this),而静态内部类不会。静态内部类在编译后是一个独立的类,不依赖于外部类实例,因此不会持有外部类引用。如果需要访问外部类,必须显式传递引用,使用弱引用可以避免强引用导致的泄漏。

追问2:removeCallbacksAndMessages(null) 为什么能移除所有消息?

答案:

// Handler.java (Android 12)
public final void removeCallbacksAndMessages(@Nullable Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

// MessageQueue.java
void removeCallbacksAndMessages(Handler h, Object object) {
    synchronized (this) {
        Message p = mMessages;

        // 移除链表头部匹配的消息
        while (p != null && p.target == h && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // 移除链表中间匹配的消息
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

object == null 时,条件 object == null || p.obj == object 始终为 true,会移除所有 target == h 的消息。


问题5:Looper.loop() 是死循环,为什么不会卡死主线程?

标准答案(30秒): Looper.loop() 虽然是死循环,但不会卡死主线程,原因有三点:第一,loop 内部调用 MessageQueue.next() 时,如果没有消息会通过 epoll 机制进入阻塞状态释放 CPU,不会空转;第二,Android 应用的事件驱动模型决定了主线程必须持续运行,所有 UI 事件、生命周期回调都通过消息机制执行;第三,真正耗时的操作应该在子线程执行,主线程只负责快速处理 UI 相关任务,单个消息执行时间短不会阻塞循环。

详细分析:

关键机制1:epoll 阻塞

// MessageQueue.java (Android 12)
Message next() {
    for (;;) {
        nativePollOnce(ptr, nextPollTimeoutMillis); // 阻塞等待

        synchronized (this) {
            Message msg = mMessages;
            if (msg != null) {
                if (now < msg.when) {
                    // 计算阻塞时间
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    return msg; // 返回消息
                }
            } else {
                nextPollTimeoutMillis = -1; // 无消息,无限等待
            }
        }
    }
}

阻塞时机:

  • 队列为空:nextPollTimeoutMillis = -1,无限阻塞
  • 有延迟消息:nextPollTimeoutMillis = msg.when - now,阻塞到消息执行时间
  • 有即时消息:立即返回,不阻塞

关键机制2:事件驱动模型

Android 主线程的所有任务都是通过消息触发:

事件类型触发方式消息来源
点击事件用户触摸屏幕InputEventReceiver
生命周期AMS 调用ActivityThread.H
View 绘制请求刷新Choreographer
广播接收系统发送ActivityThread.H

主线程消息处理示例:

// ActivityThread.java (Android 12)
class H extends Handler {
    public static final int LAUNCH_ACTIVITY = 100;
    public static final int PAUSE_ACTIVITY = 101;
    public static final int RESUME_ACTIVITY = 107;

    public void handleMessage(Message msg) {
        switch (msg.what) {
            case LAUNCH_ACTIVITY:
                handleLaunchActivity((ActivityClientRecord) msg.obj);
                break;
            case PAUSE_ACTIVITY:
                handlePauseActivity((IBinder) msg.obj, false, ...);
                break;
            // ...
        }
    }
}

追问1:如果 Looper.loop() 退出会发生什么?

答案: 主线程的 Looper 退出会导致应用崩溃。ActivityThread.main() 方法在 Looper.loop() 后面有一行代码:

public static void main(String[] args) {
    // ...
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

如果主线程 Looper 退出循环,会抛出 RuntimeException 导致应用崩溃。实际上主线程 Looper 创建时设置了 quitAllowed = false,不允许退出。

追问2:epoll 机制和 Object.wait() 有什么区别?

答案:

对比项epollObject.wait()
层级Linux 系统调用Java 对象方法
唤醒方式I/O 事件(写管道)notify/notifyAll
性能更高效,内核态阻塞用户态阻塞
应用场景I/O 多路复用线程同步

MessageQueue 使用 epoll 而非 wait 的原因:

  1. 跨进程唤醒:系统服务可以通过写管道唤醒应用进程
  2. Native 层集成:与 Native 层 Looper 统一使用 epoll
  3. 高性能:epoll 在高并发场景下性能优于 wait

问题6:Handler 的消息分发优先级是什么?

标准答案(30秒): Handler 处理消息有三级优先级:最高优先级是 Message 自带的 callback,即通过 post 方法传入的 Runnable;第二优先级是 Handler 构造时传入的 Callback 接口,如果 handleMessage 返回 true 会拦截后续处理;最低优先级是 Handler 子类重写的 handleMessage 方法。这种设计提供了灵活的消息处理机制,既能通过 post 简化单次任务,又能通过 Callback 实现拦截逻辑,还能通过继承实现复杂处理。

源码验证:

// Handler.java (Android 12)
public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg); // 优先级1
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return; // 优先级2,返回 true 则拦截
            }
        }
        handleMessage(msg); // 优先级3
    }
}

实战应用:

// 优先级1:post 方式
handler.post(() -> {
    Log.d("Priority", "优先级1:msg.callback");
});

// 优先级2:Callback 拦截
Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        Log.d("Priority", "优先级2:mCallback");
        // 返回 true 拦截,不会执行优先级3
        // 返回 false 继续执行优先级3
        return true;
    }
});

// 优先级3:子类重写
Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        Log.d("Priority", "优先级3:handleMessage");
    }
};

追问:为什么 mCallback 返回 true 可以拦截 handleMessage?

答案: 这是一种责任链模式的变体。通过返回值控制是否继续传递消息,提供了灵活的扩展机制。典型应用场景:

  1. 统一拦截处理:例如全局日志记录、埋点统计
  2. 条件处理:根据消息类型决定是否继续处理
  3. 测试 Mock:测试时替换处理逻辑
// 示例:统一日志拦截
Handler handler = new Handler(msg -> {
    Log.d("Handler", "收到消息: " + msg.what);
    return false; // 返回 false,继续执行子类的 handleMessage
}) {
    @Override
    public void handleMessage(Message msg) {
        // 实际处理逻辑
    }
};

七、总结与展望

7.1 核心要点回顾

四大核心类职责:

  1. Handler:发送消息、处理消息
  2. Message:消息载体、对象池复用
  3. MessageQueue:消息队列、阻塞唤醒
  4. Looper:消息循环、线程隔离

关键机制:

  1. 线程切换:Handler 绑定 Looper,消息在 Looper 线程执行
  2. 阻塞唤醒:epoll 机制,无消息时阻塞,有消息时唤醒
  3. 消息复用:Message 对象池,避免频繁创建
  4. 内存泄漏:静态内部类 + 弱引用 + 及时移除消息

7.2 进阶学习方向

深入理解本文内容后,建议学习:

  1. Handler 使用方式:sendMessage、post、postDelayed 等 API 详解
  2. Handler 工作流程:完整源码分析,消息发送到处理的全流程
  3. MessageQueue 详解:同步屏障、IdleHandler、Native 层实现
  4. Looper 机制:ThreadLocal 原理、epoll 机制、主线程 Looper 特殊性
  5. HandlerThread 与 IntentService:线程封装与应用

7.3 实战建议

日常开发注意事项:

  1. 优先使用 Message.obtain():避免频繁创建对象
  2. 及时移除消息:Activity 销毁时调用 removeCallbacksAndMessages(null)
  3. 避免内存泄漏:使用静态内部类或在 onDestroy 清理
  4. 选择合适的线程:耗时操作在子线程,UI 操作在主线程
  5. 使用 HandlerThread:需要子线程 Looper 时优先使用封装好的 HandlerThread

面试准备:

  1. 熟练画出四大核心类关系图
  2. 能口述完整的消息处理流程
  3. 理解 epoll 阻塞唤醒机制
  4. 掌握内存泄漏原因及解决方案
  5. 了解 ThreadLocal、消息池等细节实现

参考资料

源码路径(基于 Android 12):

  • frameworks/base/core/java/android/os/Handler.java
  • frameworks/base/core/java/android/os/Message.java
  • frameworks/base/core/java/android/os/MessageQueue.java
  • frameworks/base/core/java/android/os/Looper.java
  • frameworks/base/core/java/android/os/HandlerThread.java
  • frameworks/base/core/jni/android_os_MessageQueue.cpp

官方文档: