📊 Handler 消息机制关系图
一、核心组件关系
Thread
└── Looper(每个线程一个)
└── MessageQueue(每个Looper一个)
└── Message(多个消息,链表结构)
└── Handler(可以多个,共享同一个Looper)
二、消息流程(核心)
Handler.sendMessage()
↓
MessageQueue.enqueueMessage() [按时间排序]
↓
Looper.loop() [循环取出消息]
↓
MessageQueue.next() [可能阻塞等待]
↓
Handler.dispatchMessage() [分发消息]
↓
Handler.handleMessage() [处理消息]
三、postMessage系列方法详细调用链
Handler 消息发送方法体系
│
├─── sendMessage 系列(Message 对象)
│ │
│ ├── sendMessage(Message msg)
│ │ └──→ sendMessageDelayed(msg, 0)
│ │ └──→ sendMessageAtTime(msg, now)
│ │ └──→ MessageQueue.enqueueMessage(msg, when)
│ │
│ ├── sendMessageDelayed(Message msg, long delayMillis)
│ │ └──→ sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
│ │ └──→ MessageQueue.enqueueMessage(msg, when)
│ │
│ ├── sendMessageAtTime(Message msg, long uptimeMillis)
│ │ └──→ MessageQueue.enqueueMessage(msg, uptimeMillis)
│ │
│ ├── sendMessageAtFrontOfQueue(Message msg)
│ │ └──→ MessageQueue.enqueueMessage(msg, 0) [最高优先级]
│ │
│ ├── sendEmptyMessage(int what)
│ │ └──→ sendMessage(Message.obtain().apply { this.what = what })
│ │
│ ├── sendEmptyMessageDelayed(int what, long delayMillis)
│ │ └──→ sendMessageDelayed(Message.obtain().apply { this.what = what }, delayMillis)
│ │
│ └── sendEmptyMessageAtTime(int what, long uptimeMillis)
│ └──→ sendMessageAtTime(Message.obtain().apply { this.what = what }, uptimeMillis)
│
├─── post 系列(Runnable 对象)
│ │
│ ├── post(Runnable r)
│ │ └──→ getPostMessage(r) [Message.obtain(), callback = r]
│ │ └──→ sendMessageDelayed(message, 0)
│ │ └──→ sendMessageAtTime(message, now)
│ │
│ ├── postDelayed(Runnable r, long delayMillis)
│ │ └──→ getPostMessage(r)
│ │ └──→ sendMessageDelayed(message, delayMillis)
│ │ └──→ sendMessageAtTime(message, now + delayMillis)
│ │
│ ├── postAtTime(Runnable r, long uptimeMillis)
│ │ └──→ getPostMessage(r)
│ │ └──→ sendMessageAtTime(message, uptimeMillis)
│ │
│ └── postAtFrontOfQueue(Runnable r)
│ └──→ getPostMessage(r)
│ └──→ sendMessageAtFrontOfQueue(message)
│
└─── 消息取消方法
│
├── removeMessages(int what)
├── removeMessages(int what, Object object)
├── removeCallbacks(Runnable r)
└── removeCallbacksAndMessages(Object token)
四、消息处理完整流程图
【消息发送阶段】
Handler.sendMessage() / post()
↓
sendMessageDelayed() / sendMessageAtTime()
↓
MessageQueue.enqueueMessage(msg, when)
├── 设置 msg.target = Handler
├── 设置 msg.when = 执行时间
├── 按时间排序插入队列(when小的在前)
└── nativeWake() [唤醒等待线程]
【消息循环阶段】
Looper.loop()
↓
for (;;) {
Message msg = MessageQueue.next()
├── 如果队列为空 → 阻塞等待(nativePollOnce)
├── 如果消息未到执行时间 → 阻塞等待到执行时间
└── 如果消息到执行时间 → 返回消息
↓
msg.target.dispatchMessage(msg)
├── 如果 msg.callback != null(Runnable消息)
│ └──→ handleCallback(msg) → Runnable.run()
│
├── 如果 mCallback != null
│ └──→ mCallback.handleMessage(msg)
│ ├── 返回true → 消息处理完成
│ └── 返回false → 继续下一步
│
└── handleMessage(msg) [子类实现]
↓
msg.recycleUnchecked() [回收消息到对象池]
}
五、postMessage系列方法执行时间分类
时间轴示例(假设当前时间 = 1000ms):
立即执行(when = now):
post() ────────────────────────────────────────┐
sendMessage() ─────────────────────────────────┤
sendEmptyMessage() ────────────────────────────┤
│
▼ when = 1000ms
延迟执行(when = now + delay):
postDelayed(r, 2000) ───────────────────────────┐
sendMessageDelayed(msg, 2000) ──────────────────┤
sendEmptyMessageDelayed(what, 2000) ────────────┤
│
▼ when = 3000ms
指定时间执行(when = 指定时间):
postAtTime(r, 5000) ────────────────────────────┐
sendMessageAtTime(msg, 5000) ───────────────────┤
sendEmptyMessageAtTime(what, 5000) ─────────────┤
│
▼ when = 5000ms
高优先级(when = 0,插入队列最前面):
postAtFrontOfQueue(r) ──────────────────────────┐
sendMessageAtFrontOfQueue(msg) ─────────────────┤
│
▼ when = 0(最高优先级)
六、Handler.dispatchMessage() 详细流程
Handler.dispatchMessage(Message msg)
│
├── 检查 msg.callback != null?
│ │
│ ├── 是(Runnable消息)
│ │ └──→ handleCallback(msg)
│ │ └──→ msg.callback.run()
│ │ [直接执行Runnable]
│ │
│ └── 否(Message消息)
│ │
│ ├── 检查 mCallback != null?
│ │ │
│ │ ├── 是(有Callback)
│ │ │ └──→ mCallback.handleMessage(msg)
│ │ │ │
│ │ │ ├── 返回true → 消息处理完成,结束
│ │ │ │
│ │ │ └── 返回false → 继续下一步
│ │ │
│ │ └── 否(无Callback)
│ │ └──→ 继续下一步
│ │
│ └── handleMessage(msg)
│ [子类实现,处理消息]
1.1 Handler 基础概念
1. 什么是Handler?Handler的作用是什么?
完整答案:
Handler的定义: Handler是Android中用于线程间通信的机制,它允许你在一个线程中发送消息,然后在另一个线程中处理这些消息。
Handler的作用:
-
线程间通信:
- 从子线程发送消息到主线程
- 从主线程发送消息到子线程
- 实现不同线程之间的数据传递
-
延迟执行任务:
- 使用
postDelayed()延迟执行任务 - 实现定时任务
- 使用
-
消息队列管理:
- 管理消息的发送和处理
- 保证消息按顺序执行
-
UI更新:
- 在子线程中执行耗时操作后,通过Handler更新UI
- 避免在非主线程直接操作UI
代码示例:
// 在主线程创建Handler
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 在主线程处理消息
switch (msg.what) {
case 1:
textView.setText((String) msg.obj);
break;
}
}
};
// 在子线程发送消息
new Thread(() -> {
String result = doHeavyWork();
Message message = Message.obtain();
message.what = 1;
message.obj = result;
handler.sendMessage(message);
}).start();
总结: Handler是Android线程间通信的核心机制,用于在不同线程间传递消息和处理任务。
2. Handler、Message、MessageQueue、Looper之间的关系是什么?
完整答案:
四者的关系:
Handler(消息发送者和处理者)
↓ 发送
Message(消息对象)
↓ 放入
MessageQueue(消息队列)
↓ 取出
Looper(消息循环器)
↓ 分发
Handler(处理消息)
详细关系:
-
Handler:
- 负责发送消息和处理消息
- 持有Looper和MessageQueue的引用
- 是消息机制的入口和出口
-
Message:
- 消息的载体,包含要传递的数据
- 由Handler创建和发送
- 存储在MessageQueue中
-
MessageQueue:
- 消息队列,存储待处理的消息
- 按时间顺序排列消息
- 由Looper持有
-
Looper:
- 消息循环器,不断从MessageQueue中取出消息
- 将消息分发给对应的Handler处理
- 每个线程只有一个Looper
关系图:
Thread
└── Looper(每个线程一个)
└── MessageQueue(每个Looper一个)
└── Message(多个消息)
└── Handler(可以多个,共享同一个Looper)
总结: Handler负责发送和处理消息,Message是消息载体,MessageQueue存储消息,Looper循环取出消息并分发。四者协同工作,实现Android的消息机制。
3. Handler的工作原理是什么?
完整答案:
Handler的工作原理流程:
1. Handler发送消息
Handler.sendMessage()
↓
2. 消息放入队列
MessageQueue.enqueueMessage()
↓
3. Looper循环取出消息
Looper.loop() → MessageQueue.next()
↓
4. 消息分发给Handler
Handler.dispatchMessage()
↓
5. Handler处理消息
Handler.handleMessage()
详细步骤:
步骤1:发送消息
Handler handler = new Handler();
handler.sendMessage(message);
步骤2:消息入队
// Handler内部调用
MessageQueue.enqueueMessage(message, when);
// 消息按时间排序插入队列
步骤3:Looper循环
// Looper.loop()不断循环
for (;;) {
Message msg = queue.next(); // 从队列取出消息(可能阻塞)
if (msg == null) {
return; // 队列退出
}
msg.target.dispatchMessage(msg); // 分发消息
}
步骤4:分发消息
// Handler.dispatchMessage()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 如果是Runnable,直接执行
handleCallback(msg);
} else {
if (mCallback != null) {
// 如果有Callback,先调用Callback
if (mCallback.handleMessage(msg)) {
return;
}
}
// 最后调用handleMessage()
handleMessage(msg);
}
}
步骤5:处理消息
// Handler.handleMessage()
@Override
public void handleMessage(Message msg) {
// 处理消息
}
总结: Handler通过MessageQueue存储消息,Looper循环取出消息,Handler处理消息,形成一个完整的消息处理流程。
1.2 Message 基础
4. 什么是Message?
完整答案:
Message的定义: Message是Handler机制中消息的载体,用于在不同线程之间传递数据。
Message的结构:
public final class Message implements Parcelable {
public int what; // 消息类型标识
public int arg1; // 参数1(int类型)
public int arg2; // 参数2(int类型)
public Object obj; // 传递的对象
public Messenger replyTo; // 用于跨进程通信
public long when; // 消息执行时间
Bundle data; // 传递的数据(Bundle)
Handler target; // 处理消息的Handler
Runnable callback; // 如果是Runnable消息
Message next; // 下一个消息(链表结构)
}
创建Message的方式:
// 方式1:使用obtain()(推荐,复用Message对象)
Message msg1 = Message.obtain();
msg1.what = 1;
msg1.obj = "数据";
// 方式2:使用obtain(Handler, what)
Message msg2 = Message.obtain(handler, 2);
// 方式3:直接new(不推荐,无法复用)
Message msg3 = new Message();
总结:
Message是消息的载体,包含要传递的数据。应该使用Message.obtain()获取Message对象,而不是直接new。
5. Message.obtain()和new Message()的区别是什么?
完整答案:
主要区别:
| 特性 | Message.obtain() | new Message() |
|---|---|---|
| 对象来源 | 从对象池获取(可能复用) | 直接创建新对象 |
| 性能 | 更高效(复用对象) | 较低(频繁创建) |
| 内存 | 减少内存分配 | 增加内存分配 |
| GC压力 | 较小 | 较大 |
| 推荐使用 | ✅ 推荐 | ❌ 不推荐 |
为什么推荐obtain():
-
对象复用机制:
- obtain()从对象池获取Message对象
- 避免频繁创建和销毁对象
- 提高性能和内存利用率
-
性能优化:
- 复用对象比创建新对象更快
- 减少对象创建的开销
- 减少GC压力
总结:
始终使用Message.obtain()获取Message对象,不要使用new Message()。这是Android开发的最佳实践。
1.3 Looper 基础
6. 什么是Looper?Looper的作用是什么?
完整答案:
Looper的定义: Looper是Android消息循环机制的核心,负责从MessageQueue中取出消息并分发给对应的Handler处理。
Looper的作用:
-
消息循环:
- 不断从MessageQueue中取出消息
- 如果没有消息,会阻塞等待
- 有新消息时,唤醒并处理
-
消息分发:
- 从队列取出消息后,分发给对应的Handler
- 确保消息在正确的线程处理
-
线程绑定:
- 每个线程只能有一个Looper
- Looper与Thread绑定,通过ThreadLocal实现
-
阻塞机制:
- 当队列为空时,阻塞线程,节省CPU
- 当有新消息时,唤醒线程
Looper的核心方法:
public final class Looper {
final MessageQueue mQueue; // 消息队列
final Thread mThread; // 绑定的线程
// 开始消息循环
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); // 分发消息
msg.recycleUnchecked(); // 回收消息
}
}
}
总结: Looper是Android消息机制的核心,负责驱动整个消息循环,确保消息能够被及时处理。
7. Looper.loop()为什么不会导致ANR?
完整答案:
ANR的定义: ANR(Application Not Responding)是Android系统检测到应用无响应时弹出的对话框。
Looper.loop()不会导致ANR的原因:
- 阻塞机制:
- 当MessageQueue为空时,Looper会阻塞
- 使用epoll机制实现高效阻塞
- 不会占用CPU资源
// Looper.loop()的核心
for (;;) {
Message msg = queue.next(); // 这里会阻塞,不占用CPU
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg); // 处理消息
}
- 消息处理是快速的:
- Looper本身只是分发消息
- 如果消息处理耗时,才会导致ANR
- 不是Looper导致ANR,而是消息处理耗时
什么会导致ANR:
// ❌ 错误:在Handler中执行耗时操作
Handler handler = new Handler(Looper.getMainLooper());
handler.post(() -> {
// 耗时操作会导致ANR
try {
Thread.sleep(10000); // 阻塞10秒 → ANR
} catch (InterruptedException e) {
e.printStackTrace();
}
});
总结: Looper.loop()不会导致ANR,因为它使用阻塞机制,不占用CPU。导致ANR的是消息处理中的耗时操作,而不是Looper本身。
1.4 Handler 深入理解
8. Handler.post(Runnable)和Handler.sendMessage()的区别是什么?
完整答案:
主要区别:
| 特性 | Handler.post(Runnable) | Handler.sendMessage(Message) |
|---|---|---|
| 参数类型 | Runnable对象 | Message对象 |
| 使用方式 | 更简单 | 更灵活 |
| 数据传递 | 通过闭包传递 | 通过what、arg1、arg2、obj传递 |
| 内部实现 | 转换为Message | 直接使用Message |
| 适用场景 | 简单代码执行 | 需要传递数据的场景 |
内部实现关系:
// post()内部也是转换为Message
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;
}
使用建议:
// 场景1:需要传递数据 → 使用sendMessage
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "数据";
handler.sendMessage(msg);
// 场景2:简单执行代码 → 使用post
handler.post(() -> {
textView.setText("Hello");
});
总结:
post()更简单,适合执行简单代码;sendMessage()更灵活,适合需要传递数据的场景。两者最终都是通过MessageQueue处理。
9. Handler.postDelayed()是如何实现延迟的?
完整答案:
延迟实现的原理:
Handler通过MessageQueue按时间排序来实现延迟,消息的执行时间(when字段)设置为当前时间+延迟时间。
实现流程:
1. Handler.postDelayed(runnable, 1000)
↓
2. 计算执行时间:when = SystemClock.uptimeMillis() + 1000
↓
3. MessageQueue.enqueueMessage(msg, when)
↓
4. 按when字段排序插入队列
↓
5. MessageQueue.next()检查when
↓
6. 如果当前时间 < when,阻塞等待
↓
7. 如果当前时间 >= when,取出消息执行
源码分析:
// Handler.postDelayed()
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
// Handler.sendMessageDelayed()
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
// 计算执行时间:当前时间 + 延迟时间
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
MessageQueue的延迟处理:
// MessageQueue.next()
Message next() {
for (;;) {
long now = SystemClock.uptimeMillis();
Message msg = mMessages;
if (msg != null) {
// 检查消息是否到了执行时间
if (now < msg.when) {
// 还没到时间,计算需要等待的时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 到了执行时间,取出消息
mMessages = msg.next;
return msg;
}
} else {
// 队列为空,无限等待
nextPollTimeoutMillis = -1;
}
// 阻塞等待(使用epoll机制)
nativePollOnce(ptr, nextPollTimeoutMillis);
}
}
总结: Handler通过MessageQueue按时间排序和阻塞等待机制实现延迟,延迟精度受系统负载和消息队列影响,不是完全精确的。
10. Handler如何保证消息的顺序执行?
完整答案:
消息顺序执行的保证机制:
Handler通过MessageQueue按时间排序和单线程处理来保证消息的顺序执行。
保证机制:
-
按时间排序:
- MessageQueue按照消息的when字段(执行时间)排序
- 时间早的消息排在前面,先执行
-
单线程处理:
- 每个Looper只在一个线程中运行
- 消息在同一个线程中顺序处理
-
FIFO原则:
- 相同时间的消息,先入队的先执行
- 保证消息的先进先出
源码分析:
// MessageQueue.enqueueMessage():按时间排序
boolean enqueueMessage(Message msg, long when) {
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;
}
return true;
}
总结: Handler通过MessageQueue按时间排序和单线程顺序处理来保证消息的顺序执行,确保消息按照预期顺序处理。
11. Handler的消息队列是线程安全的吗?
完整答案:
线程安全性分析:
Handler的消息队列(MessageQueue)是线程安全的,通过synchronized同步机制保证。
线程安全保证:
- enqueueMessage()使用synchronized:
boolean enqueueMessage(Message msg, long when) {
synchronized (this) { // 同步块
// 消息入队操作
}
}
- 单线程处理:
- 每个Looper只在一个线程中运行
- 消息处理在同一个线程中,天然线程安全
多线程发送消息:
// 多个线程可以同时向同一个Handler发送消息
Handler handler = new Handler(Looper.getMainLooper());
// 线程1
new Thread(() -> {
handler.sendMessage(Message.obtain().apply { what = 1 });
}).start();
// 线程2
new Thread(() -> {
handler.sendMessage(Message.obtain().apply { what = 2 });
}).start();
// 所有消息都会安全地进入队列,按时间排序
总结: Handler的消息队列是线程安全的,多个线程可以安全地向同一个Handler发送消息,消息会按时间顺序进入队列并处理。
12. Handler的消息处理是同步的还是异步的?
完整答案:
消息处理的特性:
Handler的消息处理是同步的,消息在Handler所在的线程中顺序同步处理。
同步处理的特点:
-
顺序执行:
- 消息按时间顺序依次处理
- 一个消息处理完后,才处理下一个消息
-
阻塞处理:
- 如果消息处理耗时,会阻塞后续消息
- 可能导致消息延迟
-
单线程处理:
- 所有消息在同一个线程中处理
- 不存在并发处理
代码示例:
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
Log.d("Handler", "处理消息: " + msg.what);
// 如果这里耗时,会阻塞后续消息
try {
Thread.sleep(1000); // 阻塞1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// 发送多个消息
handler.sendMessage(Message.obtain().apply { what = 1 });
handler.sendMessage(Message.obtain().apply { what = 2 });
handler.sendMessage(Message.obtain().apply { what = 3 });
// 执行顺序:1 → 2 → 3(同步顺序执行)
// 每个消息处理1秒,总共需要3秒
总结: Handler的消息处理默认是同步的,消息按顺序在同一个线程中处理。虽然支持异步消息,但需要配合同步屏障使用,主要用于系统内部(如UI刷新)。
13. Handler的内存泄漏问题及解决方案
完整答案:
内存泄漏的原因:
Handler可能导致内存泄漏的根本原因是持有外部类引用,导致Activity/Context无法被回收。
泄漏机制:
Activity/Context
↓ 持有引用
Handler(非静态内部类)
↓ 持有引用
MessageQueue
↓ 持有引用
Message
↓ 持有引用
Handler(形成循环引用)
↓
Activity/Context 无法被回收(内存泄漏)
解决方案:
方案1:使用静态内部类 + WeakReference(推荐)
public class MainActivity extends AppCompatActivity {
private MyHandler handler;
// ✅ 静态内部类,不持有外部类引用
private static class MyHandler extends Handler {
private WeakReference<MainActivity> activityRef;
MyHandler(MainActivity activity) {
super(Looper.getMainLooper());
this.activityRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = activityRef.get();
if (activity != null && !activity.isFinishing()) {
// Activity存在,处理消息
activity.updateUI();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// ✅ 清理Handler
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
}
}
方案2:在onDestroy()中移除消息
@Override
protected void onDestroy() {
super.onDestroy();
// ✅ 移除所有消息,避免Handler持有Activity引用
handler.removeCallbacksAndMessages(null);
}
总结: Handler内存泄漏的根本原因是持有外部类引用。解决方案包括:使用静态内部类+WeakReference、在onDestroy()中移除消息、使用Callback接口、检查Activity状态等。
14. Handler如何实现线程切换?
完整答案:
线程切换原理:
Handler通过MessageQueue和Looper实现线程切换,消息在发送线程和Handler所在的线程之间传递。
核心机制:
- 消息发送:在任何线程发送消息
- 消息入队:消息进入MessageQueue(线程安全)
- 消息处理:在Handler所在的线程处理消息
实现流程:
线程A(发送消息)
↓
Handler.sendMessage()
↓
MessageQueue.enqueueMessage() [线程安全,可以跨线程]
↓
Looper.loop() [在Handler所在线程运行]
↓
Handler.handleMessage() [在Handler所在线程执行]
代码示例:
// 主线程创建Handler
Handler mainHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 在主线程执行
Log.d("Handler", "线程: " + Thread.currentThread().getName());
// 输出:main
}
};
// 子线程发送消息
new Thread(() -> {
Log.d("Thread", "发送线程: " + Thread.currentThread().getName());
// 输出:Thread-1
Message msg = Message.obtain();
msg.what = 1;
mainHandler.sendMessage(msg); // 从子线程发送消息
}).start();
// 结果:消息在子线程发送,但在主线程处理
总结: Handler通过MessageQueue和Looper实现线程切换,消息可以在任意线程发送,但在Handler所在的线程处理。这是Android中实现线程间通信的标准方式。
1.5 HandlerThread
15. HandlerThread是什么?它的作用是什么?
完整答案:
HandlerThread的定义:
HandlerThread是Android提供的带Looper的Thread,它继承自Thread,内部自动创建了Looper,方便在子线程中使用Handler。
核心特点:
- 自动创建Looper:在run()方法中自动调用
Looper.prepare()和Looper.loop() - 提供Looper:可以通过
getLooper()获取Looper - 简化使用:不需要手动创建Looper和调用loop()
使用示例:
// 使用HandlerThread(简单)
HandlerThread handlerThread = new HandlerThread("WorkerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// 处理消息
}
};
// 发送消息
Message msg = Message.obtain();
msg.what = 1;
handler.sendMessage(msg);
// 退出时
handlerThread.quit();
实际应用场景:
场景1:后台任务处理
public class BackgroundTaskManager {
private HandlerThread handlerThread;
private Handler handler;
public void init() {
handlerThread = new HandlerThread("BackgroundTask");
handlerThread.start();
handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// 在后台线程处理任务
processTask((Task) msg.obj);
}
};
}
public void destroy() {
if (handlerThread != null) {
handlerThread.quit();
}
}
}
总结: HandlerThread是带Looper的Thread,自动创建Looper,简化了在子线程中使用Handler的流程。适合后台任务处理、数据库操作等场景。
1.6 Looper 深入理解
16. 主线程的Looper是什么时候创建的?
完整答案:
主线程Looper的创建时机:
主线程的Looper是在ActivityThread的main()方法中创建的,这是Android应用程序的入口点。
创建流程:
// ActivityThread.main()
public static void main(String[] args) {
Looper.prepareMainLooper(); // 1. 准备主线程Looper
// ...
Looper.loop(); // 2. 开始消息循环
}
// Looper.prepareMainLooper()
public static void prepareMainLooper() {
prepare(false); // false表示不允许退出
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper(); // 保存主线程Looper的引用
}
}
关键点:
- 应用启动时创建:当Android应用启动时,ActivityThread.main()会被调用
- 只创建一次:主线程Looper只能创建一次,不能重复创建
- 不能退出:主线程Looper不能调用quit()退出,否则会崩溃
获取主线程Looper:
// 获取主线程Looper
Looper mainLooper = Looper.getMainLooper();
// 在主线程中创建Handler(使用主线程Looper)
Handler handler = new Handler(Looper.getMainLooper());
总结: 主线程的Looper是在ActivityThread.main()方法中通过Looper.prepareMainLooper()创建的,应用启动时自动创建,且不能退出。
17. 子线程中如何使用Handler?如何创建Looper?
完整答案:
子线程中使用Handler的方式:
在子线程中使用Handler,需要先为该线程创建Looper,然后创建Handler。
方式1:手动创建Looper(传统方式)
// 子线程中创建Handler
new Thread(() -> {
// 1. 准备Looper(为当前线程创建Looper)
Looper.prepare();
// 2. 创建Handler(使用当前线程的Looper)
Handler handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
// 在子线程处理消息
Log.d("Handler", "线程: " + Thread.currentThread().getName());
}
};
// 3. 开始消息循环(必须调用,否则消息不会处理)
Looper.loop();
}).start();
方式2:使用HandlerThread(推荐)
// 使用HandlerThread(更简单)
HandlerThread handlerThread = new HandlerThread("WorkerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// 在子线程处理消息
}
};
// 发送消息
handler.sendMessage(Message.obtain().apply { what = 1 });
// 退出时
handlerThread.quit();
Looper的创建过程:
// Looper.prepare()
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");
}
// 创建Looper并绑定到当前线程
sThreadLocal.set(new Looper(quitAllowed));
}
关键点:
- 必须先prepare():在创建Handler之前,必须先调用Looper.prepare()
- 一个线程一个Looper:每个线程只能有一个Looper,不能重复创建
- 必须调用loop():必须调用Looper.loop()才能处理消息,否则消息不会执行
- 使用HandlerThread更简单:推荐使用HandlerThread,它自动创建Looper和调用loop()
总结: 在子线程中使用Handler,需要先调用Looper.prepare()创建Looper,然后创建Handler,最后调用Looper.loop()开始消息循环。推荐使用HandlerThread简化流程。
18. Looper的退出机制是什么?如何退出Looper?
完整答案:
Looper的退出方式:
Looper有两种退出方式:quit() 和 quitSafely()。
方式1:quit() - 立即退出
// 立即退出Looper,不等待消息处理完成
looper.quit();
特点:
- 立即退出,不等待消息处理完成
- 队列中未处理的消息会被丢弃
- 可能不安全
方式2:quitSafely() - 安全退出(推荐)
// 安全退出Looper,等待消息处理完成
looper.quitSafely();
特点:
- 等待当前消息处理完成后再退出
- 队列中的延迟消息会被移除,但正在处理的消息会处理完
- 更安全
源码分析:
// MessageQueue.quit()
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return; // 已经退出
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked(); // 移除所有延迟消息
} else {
removeAllMessagesLocked(); // 移除所有消息
}
nativeWake(mPtr); // 唤醒等待线程,让loop()退出
}
}
使用示例:
// 使用HandlerThread
HandlerThread handlerThread = new HandlerThread("WorkerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// 处理消息
}
};
// 发送消息
handler.sendMessage(Message.obtain().apply { what = 1 });
// 退出时(推荐使用quitSafely())
handlerThread.quitSafely(); // 或 handlerThread.quit()
// 等待线程结束
try {
handlerThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
主线程Looper不能退出:
// ❌ 错误:主线程Looper不能退出
Looper.getMainLooper().quit(); // 会抛出异常
// IllegalStateException: Main thread not allowed to quit.
总结: Looper可以通过quit()或quitSafely()退出。quit()立即退出,quitSafely()等待消息处理完成后再退出,更安全。主线程Looper不能退出。
1.7 Handler 消息机制深入
19. Handler的同步屏障(SyncBarrier)是什么?
完整答案:
同步屏障的定义:
同步屏障(SyncBarrier)是Handler机制中的一个特殊机制,用于暂停同步消息的处理,优先处理异步消息。
同步屏障的作用:
- 暂停同步消息:插入同步屏障后,同步消息会被暂停处理
- 优先处理异步消息:只有异步消息(
setAsynchronous(true))才能通过屏障 - 主要用于UI刷新:系统在需要立即刷新UI时使用,如View绘制
使用示例:
// 获取MessageQueue
MessageQueue queue = Looper.getMainLooper().getQueue();
// 1. 创建同步屏障
int token = queue.postSyncBarrier();
// 2. 发送异步消息(可以正常处理)
Message asyncMsg = Message.obtain();
asyncMsg.setAsynchronous(true); // 设置为异步消息
handler.sendMessage(asyncMsg);
// 3. 发送同步消息(会被暂停)
Message syncMsg = Message.obtain();
handler.sendMessage(syncMsg); // 这个消息会被暂停处理
// 4. 移除同步屏障(恢复同步消息处理)
queue.removeSyncBarrier(token);
源码分析:
// MessageQueue.postSyncBarrier()
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) {
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
MessageQueue.next()中的处理:
// MessageQueue.next()
Message next() {
for (;;) {
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 遇到同步屏障,跳过同步消息,寻找异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 处理消息...
}
}
应用场景:
- View绘制:系统在需要立即刷新UI时使用同步屏障
- 动画:需要优先处理动画相关的消息
- 系统内部:主要用于系统内部,普通应用很少使用
总结: 同步屏障用于暂停同步消息的处理,优先处理异步消息。主要用于系统内部UI刷新等场景,普通应用很少直接使用。
20. 异步消息和同步消息的区别是什么?
完整答案:
消息的分类:
Handler的消息分为两种:同步消息和异步消息。
同步消息(默认):
// 默认消息都是同步消息
Message msg = Message.obtain();
msg.what = 1;
handler.sendMessage(msg); // 同步消息
特点:
- 默认所有消息都是同步消息
- 按时间顺序依次处理
- 会受同步屏障影响(遇到屏障时暂停处理)
异步消息:
// 创建异步消息
Message msg = Message.obtain();
msg.setAsynchronous(true); // 设置为异步消息
msg.what = 1;
handler.sendMessage(msg);
特点:
- 需要显式设置
setAsynchronous(true) - 可以通过同步屏障(不受屏障影响)
- 在系统内部用于UI刷新等场景
区别对比:
| 特性 | 同步消息 | 异步消息 |
|---|---|---|
| 默认类型 | ✅ 是 | ❌ 否(需要设置) |
| 设置方式 | 默认 | setAsynchronous(true) |
| 同步屏障 | 受屏障影响(暂停) | 不受影响(可通过) |
| 处理顺序 | 按时间顺序 | 按时间顺序(但不受屏障限制) |
| 使用场景 | 普通消息 | 系统内部(UI刷新等) |
| 应用使用 | ✅ 常用 | ❌ 很少使用 |
源码分析:
// Message.setAsynchronous()
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
// MessageQueue.next()
Message next() {
for (;;) {
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 同步屏障:跳过同步消息,寻找异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// ...
}
}
注意事项:
- 普通应用不需要使用:异步消息主要用于系统内部
- 需要配合同步屏障:异步消息通常需要配合同步屏障使用
- API限制:在某些版本中,普通应用可能无法使用异步消息
总结: 同步消息是默认类型,按顺序处理,受同步屏障影响。异步消息需要显式设置,不受同步屏障影响,主要用于系统内部。
21. Handler的Callback接口的作用是什么?
完整答案:
Callback接口的定义:
Handler的Callback接口是一个回调接口,用于处理消息,可以替代重写handleMessage()方法。
Callback接口的定义:
public interface Callback {
boolean handleMessage(Message msg);
}
使用方式:
方式1:使用Callback接口
// 使用Callback接口
Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// 处理消息
switch (msg.what) {
case 1:
// 处理消息
return true; // 返回true表示消息已处理,不再调用handleMessage()
default:
return false; // 返回false表示继续调用handleMessage()
}
}
});
方式2:重写handleMessage()方法
// 重写handleMessage()方法
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 处理消息
}
};
dispatchMessage()的执行顺序:
// Handler.dispatchMessage()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 1. 如果是Runnable消息,直接执行
handleCallback(msg);
} else {
if (mCallback != null) {
// 2. 如果有Callback,先调用Callback
if (mCallback.handleMessage(msg)) {
return; // 如果返回true,消息处理完成,不再调用handleMessage()
}
}
// 3. 最后调用handleMessage()(子类实现)
handleMessage(msg);
}
}
执行顺序:
dispatchMessage()
├── 1. 如果是Runnable (msg.callback != null)
│ └──→ handleCallback() → Runnable.run()
│
├── 2. 如果有Callback (mCallback != null)
│ └──→ mCallback.handleMessage()
│ ├── 返回true → 消息处理完成,结束
│ └── 返回false → 继续下一步
│
└── 3. handleMessage() (子类实现)
使用场景:
场景1:使用Lambda表达式
// 使用Lambda表达式(简洁)
Handler handler = new Handler(Looper.getMainLooper(), msg -> {
// 处理消息
return true; // 或 false
});
场景2:复用Callback逻辑
// 可以复用的Callback
Handler.Callback callback = msg -> {
// 通用处理逻辑
return false; // 继续调用handleMessage()
};
Handler handler1 = new Handler(Looper.getMainLooper(), callback);
Handler handler2 = new Handler(Looper.getMainLooper(), callback);
总结: Callback接口用于处理消息,可以替代重写handleMessage()方法。执行顺序:Runnable → Callback → handleMessage()。Callback返回true表示消息已处理,返回false表示继续调用handleMessage()。
22. 如何取消延迟消息?
完整答案:
取消延迟消息的方法:
Handler提供了多种方法来取消延迟消息,包括取消特定消息和取消所有消息。
方法1:removeMessages(int what) - 取消特定类型的消息
// 取消所有what=1的消息
handler.removeMessages(1);
// 示例
handler.sendEmptyMessageDelayed(1, 1000); // 延迟1秒发送消息
handler.removeMessages(1); // 取消what=1的所有消息
方法2:removeMessages(int what, Object object) - 取消特定消息
// 取消特定对象的消息
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "data";
handler.sendMessageDelayed(msg, 1000);
// 取消特定消息
handler.removeMessages(1, "data");
方法3:removeCallbacks(Runnable r) - 取消Runnable消息
// 取消Runnable消息
Runnable task = () -> {
// 任务代码
};
handler.postDelayed(task, 1000); // 延迟1秒执行
handler.removeCallbacks(task); // 取消Runnable任务
方法4:removeCallbacksAndMessages(Object token) - 取消所有消息
// 取消所有消息(token为null)
handler.removeCallbacksAndMessages(null);
// 取消特定token的消息
Object token = new Object();
handler.sendMessageDelayed(Message.obtain().apply { obj = token }, 1000);
handler.removeCallbacksAndMessages(token);
完整示例:
public class MessageCancelExample {
private Handler handler = new Handler(Looper.getMainLooper());
public void startTask() {
// 发送延迟消息
handler.sendEmptyMessageDelayed(1, 5000); // 5秒后执行
// 发送Runnable任务
Runnable task = () -> {
// 任务代码
};
handler.postDelayed(task, 3000); // 3秒后执行
}
public void cancelTask() {
// 取消what=1的消息
handler.removeMessages(1);
// 取消Runnable任务
handler.removeCallbacks(task);
}
public void cancelAll() {
// 取消所有消息
handler.removeCallbacksAndMessages(null);
}
@Override
protected void onDestroy() {
super.onDestroy();
// ✅ 在Activity销毁时取消所有消息,避免内存泄漏
handler.removeCallbacksAndMessages(null);
}
}
源码分析:
// Handler.removeMessages()
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
// Handler.removeCallbacks()
public final void removeCallbacks(Runnable r) {
mQueue.removeMessages(this, null, r);
}
// Handler.removeCallbacksAndMessages()
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
最佳实践:
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler(Looper.getMainLooper());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 发送延迟消息
handler.sendEmptyMessageDelayed(1, 5000);
}
@Override
protected void onDestroy() {
super.onDestroy();
// ✅ 必须取消所有消息,避免内存泄漏
handler.removeCallbacksAndMessages(null);
}
}
总结: 可以使用removeMessages()、removeCallbacks()、removeCallbacksAndMessages()等方法取消延迟消息。在Activity销毁时,应该取消所有消息,避免内存泄漏。
23. postDelayed()的延迟精度如何?为什么?
完整答案:
延迟精度的特点:
postDelayed()的延迟不是完全精确的,延迟时间会有一定的误差。
精度问题:
-
不精确的原因:
- 消息队列中的其他消息可能延迟执行
- 系统负载影响消息处理速度
- 如果前面的消息处理耗时,会延迟后续消息
-
精度范围:
- 通常在几毫秒到几十毫秒的误差
- 如果消息队列繁忙,误差可能更大
- 不能用于需要精确计时的场景
代码示例:
// 延迟1秒执行
long startTime = SystemClock.uptimeMillis();
handler.postDelayed(() -> {
long actualDelay = SystemClock.uptimeMillis() - startTime;
Log.d("Delay", "期望延迟: 1000ms, 实际延迟: " + actualDelay + "ms");
// 实际延迟可能不是精确的1000ms
}, 1000);
影响因素:
- 消息队列中的其他消息:
// 如果前面的消息处理耗时,会延迟后续消息
handler.sendMessage(Message.obtain().apply { what = 1 });
handler.postDelayed(() -> {
// 如果消息1处理耗时,这个延迟消息会延迟执行
}, 1000);
handler.handleMessage(Message msg) {
if (msg.what == 1) {
try {
Thread.sleep(2000); // 耗时2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
-
系统负载:
- CPU负载高时,消息处理速度变慢
- 可能导致延迟消息不精确
-
系统休眠:
- 系统休眠时,延迟时间不准确
- 系统唤醒后,延迟时间可能会累积
如何提高精度:
-
减少消息队列中的消息:
- 避免在消息队列中堆积大量消息
- 及时处理消息,减少队列长度
-
避免耗时操作:
- 不要在Handler中执行耗时操作
- 耗时操作应该在后台线程执行
-
使用SystemClock.uptimeMillis():
- Handler内部使用
SystemClock.uptimeMillis()计算时间 - 不受系统时间调整影响
- Handler内部使用
源码分析:
// Handler.postDelayed()
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
// Handler.sendMessageDelayed()
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
// 使用SystemClock.uptimeMillis()计算时间
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
注意事项:
-
不能用于精确计时:
- 不能用于需要精确计时的场景(如秒表、倒计时)
- 应该使用其他精确计时机制
-
误差可接受:
- 对于UI更新、一般延迟任务,误差通常可接受
- 如果误差在几十毫秒内,通常没问题
总结: postDelayed()的延迟不是完全精确的,受消息队列、系统负载等因素影响,通常有几毫秒到几十毫秒的误差。不能用于需要精确计时的场景。
1.8 MessageQueue 深入理解
24. MessageQueue的阻塞和唤醒机制是什么?
完整答案:
阻塞和唤醒机制:
MessageQueue使用**nativePollOnce()和nativeWake()**实现高效的阻塞和唤醒机制。
阻塞机制(nativePollOnce):
// MessageQueue.next()
Message next() {
for (;;) {
// 计算需要等待的时间
int nextPollTimeoutMillis = 0;
if (msg != null) {
if (now < msg.when) {
// 消息未到执行时间,计算等待时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 消息到时间了,立即返回
return msg;
}
} else {
// 队列为空,无限等待
nextPollTimeoutMillis = -1;
}
// 阻塞等待(使用epoll机制,不占用CPU)
nativePollOnce(ptr, nextPollTimeoutMillis);
}
}
唤醒机制(nativeWake):
// MessageQueue.enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
// 插入消息到队列
// ...
// 如果有线程在等待,唤醒它
if (needWake) {
nativeWake(mPtr); // 唤醒等待的线程
}
}
return true;
}
核心机制:
-
epoll机制:
nativePollOnce()使用Linux的epoll机制实现阻塞- 不占用CPU资源,高效阻塞
- 当有新消息时,通过
nativeWake()唤醒
-
阻塞时机:
- 队列为空时:无限阻塞等待
- 消息未到执行时间:阻塞到消息执行时间
- 有新消息时:立即唤醒
-
唤醒时机:
- 有新消息入队时
- 消息执行时间到了时
为什么不会导致ANR:
// Looper.loop()
for (;;) {
Message msg = queue.next(); // 这里会阻塞,不占用CPU
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg); // 处理消息
}
queue.next()会阻塞,不占用CPU- 使用epoll机制,系统级阻塞,非常高效
- 只有消息处理耗时才会导致ANR,不是阻塞导致的
总结: MessageQueue使用nativePollOnce()和nativeWake()实现阻塞和唤醒。阻塞时不占用CPU,有新消息时唤醒,这是Handler机制高效运行的关键。
1.9 Handler 构造函数和方法
25. Handler的构造函数有哪些?有什么区别?
完整答案:
Handler的构造函数:
Handler有多个构造函数,用于不同的使用场景。
构造函数1:无参构造函数(已废弃)
// ❌ 已废弃,不推荐使用
Handler handler = new Handler();
问题:
- 如果当前线程没有Looper,会抛出异常
- 不明确指定Looper,容易出错
构造函数2:指定Looper
// ✅ 推荐:明确指定Looper
Handler handler = new Handler(Looper.getMainLooper());
Handler handler = new Handler(handlerThread.getLooper());
特点:
- 明确指定Looper,更安全
- 可以在任何线程创建,使用指定的Looper
构造函数3:指定Looper和Callback
// ✅ 使用Callback接口
Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// 处理消息
return false; // 继续调用handleMessage()
}
});
特点:
- 可以指定Callback接口
- 不需要重写handleMessage()方法
构造函数4:指定Callback(已废弃)
// ❌ 已废弃
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
构造函数5:指定Looper、Callback和async
// 创建异步Handler(API 28+)
Handler handler = new Handler(Looper.getMainLooper(), null, true);
// 第三个参数true表示异步Handler
推荐使用方式:
// ✅ 方式1:明确指定Looper(最推荐)
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 处理消息
}
};
// ✅ 方式2:使用Callback接口
Handler handler = new Handler(Looper.getMainLooper(), msg -> {
// 处理消息
return false;
});
注意事项:
-
必须指定Looper:
- 推荐使用
Looper.getMainLooper()或明确指定Looper - 避免使用无参构造函数
- 推荐使用
-
子线程中使用:
- 子线程中必须明确指定Looper
- 或使用HandlerThread
总结: Handler有多个构造函数,推荐使用明确指定Looper的构造函数。可以在构造函数中指定Callback接口,避免重写handleMessage()方法。
26. postAtFrontOfQueue()和sendMessageAtFrontOfQueue()的作用是什么?
完整答案:
作用:
这两个方法用于将消息插入到队列的最前面,实现最高优先级的消息处理。
方法定义:
// post系列
public final boolean postAtFrontOfQueue(Runnable r)
// sendMessage系列
public final boolean sendMessageAtFrontOfQueue(Message msg)
实现原理:
// Handler.postAtFrontOfQueue()
public final boolean postAtFrontOfQueue(Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
// Handler.sendMessageAtFrontOfQueue()
public final boolean sendMessageAtFrontOfQueue(Message msg) {
MessageQueue queue = mQueue;
if (queue == null) {
return false;
}
return enqueueMessage(queue, msg, 0); // when = 0 表示最高优先级
}
// MessageQueue.enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// when = 0 时,插入到队列最前面
msg.next = p;
mMessages = msg;
// ...
}
// ...
}
关键点:
-
when = 0:
- 当
when = 0时,消息会插入到队列最前面 - 优先级最高,会立即处理(如果当前没有正在处理的消息)
- 当
-
立即处理:
- 消息会插入到队列头部
- 下一个消息循环时,会优先处理这个消息
使用示例:
// 发送普通消息
handler.sendMessage(Message.obtain().apply { what = 1 });
// 发送高优先级消息(插入队列最前面)
handler.sendMessageAtFrontOfQueue(Message.obtain().apply { what = 2 });
// 或使用post系列
handler.postAtFrontOfQueue(() -> {
// 高优先级任务
});
执行顺序:
队列状态:
[消息1] -> [消息2] -> [消息3]
发送高优先级消息:
sendMessageAtFrontOfQueue(消息4)
队列状态:
[消息4] -> [消息1] -> [消息2] -> [消息3]
执行顺序:消息4 -> 消息1 -> 消息2 -> 消息3
使用场景:
-
紧急任务:
- 需要立即处理的任务
- 如系统级消息、紧急UI更新
-
系统内部:
- 主要用于系统内部
- 普通应用很少使用
注意事项:
-
不能滥用:
- 频繁使用会影响消息队列的正常顺序
- 可能导致其他消息延迟
-
系统内部使用:
- 主要用于系统内部(如View绘制)
- 普通应用谨慎使用
总结: postAtFrontOfQueue()和sendMessageAtFrontOfQueue()用于将消息插入队列最前面,实现最高优先级。通过设置when=0实现,主要用于系统内部,普通应用谨慎使用。
1.10 Message 深入理解
27. Message的对象池机制是什么?如何复用Message?
完整答案:
对象池机制:
Message使用对象池机制来复用Message对象,避免频繁创建和销毁对象,提高性能和减少GC压力。
对象池实现:
// Message类中的对象池
public final class Message implements Parcelable {
// 对象池(链表结构)
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
Message next; // 用于对象池链表
// 从对象池获取Message
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(); // 对象池为空,创建新对象
}
// 回收Message到对象池
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
// ... 清除所有字段
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}
对象池工作流程:
1. 获取Message:
Message.obtain()
↓
从对象池取出(如果有)
↓
对象池为空 → 创建新对象
2. 使用Message:
handler.sendMessage(msg)
↓
消息处理完成
3. 回收Message:
msg.recycleUnchecked()
↓
清除所有字段
↓
放入对象池(如果未满)
对象池特点:
-
最大容量:MAX_POOL_SIZE = 50
- 对象池最多存储50个Message对象
- 超过容量后,对象会被GC回收
-
自动回收:
- 消息处理完成后,自动调用
recycleUnchecked() - 不需要手动回收
- 消息处理完成后,自动调用
-
线程安全:
- 使用
synchronized保证线程安全 - 多个线程可以安全地获取和回收Message
- 使用
性能优势:
-
减少对象创建:
- 复用对象,减少new操作
- 提高性能
-
减少GC压力:
- 减少对象分配和回收
- 降低GC频率
-
内存优化:
- 对象池限制最大容量
- 避免内存无限增长
使用建议:
// ✅ 推荐:使用obtain()获取Message
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "data";
handler.sendMessage(msg);
// ❌ 不推荐:直接new Message
Message msg = new Message(); // 无法复用,性能差
源码分析:
// Looper.loop()中自动回收
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg);
msg.recycleUnchecked(); // 自动回收到对象池
}
总结: Message使用对象池机制复用对象,最大容量50个。通过obtain()获取,recycleUnchecked()回收。自动回收,线程安全,提高性能,减少GC压力。
1.11 Handler 其他重要问题
28. 为什么主线程的Looper不能退出?
完整答案:
原因:
主线程的Looper不能退出,因为主线程是Android应用的生命线,退出会导致应用崩溃。
技术原因:
- 主线程Looper的特殊性:
// Looper.prepareMainLooper()
public static void prepareMainLooper() {
prepare(false); // false表示不允许退出
// ...
}
// Looper.prepare()
private static void prepare(boolean quitAllowed) {
// ...
sThreadLocal.set(new Looper(quitAllowed)); // quitAllowed = false
}
- quit()方法检查:
// MessageQueue.quit()
void quit(boolean safe) {
if (!mQuitAllowed) { // 主线程Looper的mQuitAllowed = false
throw new IllegalStateException("Main thread not allowed to quit.");
}
// ...
}
为什么不能退出:
-
应用的生命线:
- 主线程负责处理所有UI操作
- 主线程Looper退出,UI无法更新,应用无法响应
-
系统设计:
- Android系统设计主线程Looper不能退出
- 退出会导致应用崩溃
-
消息循环必需:
- 主线程需要持续处理消息
- 包括UI更新、事件处理等
尝试退出的后果:
// ❌ 尝试退出主线程Looper
try {
Looper.getMainLooper().quit();
} catch (IllegalStateException e) {
// 抛出异常:Main thread not allowed to quit.
}
子线程Looper可以退出:
// ✅ 子线程Looper可以退出
HandlerThread handlerThread = new HandlerThread("WorkerThread");
handlerThread.start();
// ...
handlerThread.quit(); // 可以退出
总结: 主线程的Looper不能退出,因为主线程是应用的生命线。系统设计不允许主线程Looper退出,退出会导致应用崩溃。子线程Looper可以退出。
29. Handler和AsyncTask的区别是什么?
完整答案:
主要区别:
| 特性 | Handler | AsyncTask |
|---|---|---|
| 类型 | 消息机制 | 异步任务框架 |
| 线程管理 | 手动管理 | 自动管理线程池 |
| 使用复杂度 | 较简单 | 较复杂 |
| 生命周期 | 需要手动管理 | 与Activity生命周期绑定 |
| 适用场景 | 线程间通信、延迟任务 | 后台任务处理 |
| 状态 | 活跃使用 | 已废弃(API 30+) |
详细对比:
1. 线程管理:
// Handler:手动管理线程
Handler handler = new Handler(Looper.getMainLooper());
new Thread(() -> {
// 后台任务
handler.post(() -> {
// UI更新
});
}).start();
// AsyncTask:自动管理线程池
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... voids) {
// 后台任务(自动在后台线程执行)
return "result";
}
@Override
protected void onPostExecute(String result) {
// UI更新(自动在主线程执行)
}
}.execute();
2. 使用复杂度:
// Handler:需要手动管理线程
Handler handler = new Handler(Looper.getMainLooper());
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(() -> {
String result = doWork();
handler.post(() -> {
updateUI(result);
});
});
// AsyncTask:封装了线程管理
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... voids) {
return doWork();
}
@Override
protected void onPostExecute(String result) {
updateUI(result);
}
}.execute();
3. 生命周期管理:
// Handler:需要手动管理
public class MainActivity extends AppCompatActivity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler(Looper.getMainLooper());
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null); // 手动清理
}
}
// AsyncTask:生命周期问题(已废弃)
// AsyncTask在Activity销毁后可能继续执行,导致内存泄漏
4. 适用场景:
Handler适用场景:
- 线程间通信
- 延迟任务
- UI更新
- 定时任务
AsyncTask适用场景(已废弃):
- 简单的后台任务
- 需要进度更新的任务
现代替代方案:
// ✅ 推荐:使用Kotlin协程
CoroutineScope(Dispatchers.IO).launch {
val result = doWork()
withContext(Dispatchers.Main) {
updateUI(result)
}
}
// ✅ 推荐:使用RxJava
Observable.fromCallable(() -> doWork())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> updateUI(result));
// ✅ 推荐:使用WorkManager(后台任务)
WorkRequest uploadRequest = new OneTimeWorkRequest.Builder(UploadWorker.class)
.build();
WorkManager.getInstance(context).enqueue(uploadRequest);
总结: Handler是消息机制,需要手动管理线程;AsyncTask是异步任务框架,自动管理线程池,但已废弃。现代开发推荐使用协程、RxJava或WorkManager。
30. Handler的消息队列大小有限制吗?
完整答案:
消息队列大小:
Handler的消息队列理论上没有硬性限制,但实际使用中应该控制消息数量。
技术限制:
-
内存限制:
- 每个Message对象占用一定内存
- 消息过多会占用大量内存
- 可能导致OOM(Out of Memory)
-
性能限制:
- 消息队列过长,查找和插入消息变慢
- 影响消息处理速度
-
实际限制:
- 虽然没有硬编码的大小限制
- 但受设备内存和性能影响
消息队列结构:
// MessageQueue使用链表结构
public final class MessageQueue {
Message mMessages; // 链表头节点
// 消息按时间排序,链表结构
// [消息1] -> [消息2] -> [消息3] -> ...
}
潜在问题:
- 内存泄漏:
// ❌ 问题:消息队列中堆积大量消息
for (int i = 0; i < 10000; i++) {
handler.sendMessage(Message.obtain().apply { what = i });
// 如果消息处理慢,会堆积大量消息
}
- 性能问题:
- 消息队列过长,插入和查找变慢
- 影响消息处理效率
最佳实践:
- 及时处理消息:
// ✅ 推荐:及时处理消息,避免堆积
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 快速处理消息,避免阻塞
processMessage(msg);
}
};
- 取消不需要的消息:
// ✅ 推荐:取消不需要的消息
handler.sendEmptyMessageDelayed(1, 5000);
// 如果不需要了,及时取消
handler.removeMessages(1);
- 控制消息数量:
// ✅ 推荐:控制消息数量
if (handler.hasMessages(1)) {
// 如果已有相同消息,不重复发送
return;
}
handler.sendMessage(Message.obtain().apply { what = 1 });
- 在Activity销毁时清理:
@Override
protected void onDestroy() {
super.onDestroy();
// ✅ 必须清理所有消息
handler.removeCallbacksAndMessages(null);
}
总结: Handler的消息队列理论上没有硬性限制,但应该控制消息数量。消息过多会导致内存占用和性能问题。应该及时处理消息、取消不需要的消息、在Activity销毁时清理。
31. Handler.obtainMessage()和Message.obtain()的区别是什么?
完整答案:
两种获取Message的方式:
Handler和Message都提供了获取Message对象的方法,它们本质相同,但使用场景不同。
方式1:Message.obtain()
// 使用Message.obtain()获取Message
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "data";
handler.sendMessage(msg);
方式2:Handler.obtainMessage()
// 使用Handler.obtainMessage()获取Message
Message msg = handler.obtainMessage(1, "data");
handler.sendMessage(msg);
// 或更简洁的写法
handler.obtainMessage(1, "data").sendToTarget();
区别对比:
| 特性 | Message.obtain() | Handler.obtainMessage() |
|---|---|---|
| 来源 | Message类的静态方法 | Handler的实例方法 |
| 参数 | 无参数 | 可以有参数(what、obj等) |
| Handler绑定 | 需要手动设置 | 自动绑定Handler |
| 使用场景 | 通用场景 | Handler专用场景 |
| 便利性 | 较繁琐 | 更便捷 |
Handler.obtainMessage()的多种重载:
// 1. 无参数
Message msg1 = handler.obtainMessage();
// 2. 指定what
Message msg2 = handler.obtainMessage(1);
// 3. 指定what和obj
Message msg3 = handler.obtainMessage(1, "data");
// 4. 指定what、arg1、arg2
Message msg4 = handler.obtainMessage(1, 100, 200);
// 5. 指定what、arg1、arg2、obj
Message msg5 = handler.obtainMessage(1, 100, 200, "data");
// 6. 指定what、obj和callback
Message msg6 = handler.obtainMessage(1, "data", callback);
源码分析:
// Handler.obtainMessage()
public final Message obtainMessage() {
return Message.obtain(this); // 自动绑定当前Handler
}
public final Message obtainMessage(int what) {
return Message.obtain(this, what);
}
public final Message obtainMessage(int what, Object obj) {
return Message.obtain(this, what, obj);
}
// Message.obtain(Handler, what)
public static Message obtain(Handler h, int what) {
Message m = obtain();
m.target = h; // 自动绑定Handler
m.what = what;
return m;
}
使用建议:
使用Handler.obtainMessage()的场景:
// ✅ 推荐:使用Handler.obtainMessage()
handler.obtainMessage(1, "data").sendToTarget();
// 或
Message msg = handler.obtainMessage(1, "data");
handler.sendMessage(msg);
使用Message.obtain()的场景:
// ✅ 适用于:需要在多个Handler间传递的消息
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "data";
// 可以发送给不同的Handler
handler1.sendMessage(msg.copyFrom(msg));
handler2.sendMessage(msg);
总结: Handler.obtainMessage()和Message.obtain()都从对象池获取Message。Handler.obtainMessage()更便捷,自动绑定Handler并可以设置参数;Message.obtain()更通用,适合需要在多个Handler间传递的场景。
32. Looper.myLooper()和Looper.getMainLooper()的区别是什么?
完整答案:
两个方法的定义:
// 获取当前线程的Looper
public static Looper myLooper() {
return sThreadLocal.get(); // 从ThreadLocal获取当前线程的Looper
}
// 获取主线程的Looper
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper; // 返回主线程Looper的引用
}
}
主要区别:
| 特性 | Looper.myLooper() | Looper.getMainLooper() |
|---|---|---|
| 返回对象 | 当前线程的Looper | 主线程的Looper |
| 线程相关 | 线程相关(每个线程不同) | 线程无关(总是主线程) |
| 获取方式 | ThreadLocal获取 | 静态变量获取 |
| 使用场景 | 子线程中获取自己的Looper | 任何线程获取主线程Looper |
| 可能返回null | 如果当前线程没有Looper | 不会返回null(应用启动时已创建) |
使用场景:
场景1:Looper.myLooper() - 获取当前线程的Looper
// 在子线程中使用
new Thread(() -> {
Looper.prepare(); // 为当前线程创建Looper
Looper myLooper = Looper.myLooper(); // 获取当前线程的Looper
Handler handler = new Handler(myLooper) {
@Override
public void handleMessage(Message msg) {
// 在当前线程处理消息
}
};
Looper.loop();
}).start();
场景2:Looper.getMainLooper() - 获取主线程的Looper
// 在任何线程中使用,获取主线程Looper
Handler mainHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 在主线程处理消息
}
};
// 从子线程发送消息到主线程
new Thread(() -> {
mainHandler.sendMessage(Message.obtain().apply { what = 1 });
}).start();
源码分析:
// Looper.myLooper()
public static Looper myLooper() {
return sThreadLocal.get(); // 从当前线程的ThreadLocalMap获取
}
// Looper.prepareMainLooper()中保存主线程Looper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper(); // 保存主线程Looper的引用
}
}
// Looper.getMainLooper()
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper; // 返回主线程Looper的引用
}
}
注意事项:
- myLooper()可能返回null:
// ❌ 如果当前线程没有Looper,myLooper()返回null
new Thread(() -> {
Looper looper = Looper.myLooper(); // 返回null,因为当前线程没有Looper
if (looper == null) {
Looper.prepare(); // 需要先创建Looper
}
}).start();
- getMainLooper()不会返回null:
// ✅ getMainLooper()不会返回null(应用启动时已创建)
Looper mainLooper = Looper.getMainLooper(); // 不会返回null
Handler handler = new Handler(mainLooper);
总结: Looper.myLooper()获取当前线程的Looper,线程相关;Looper.getMainLooper()获取主线程的Looper,线程无关。myLooper()可能返回null,getMainLooper()不会返回null。
33. sendEmptyMessage()系列方法的使用场景是什么?
完整答案:
sendEmptyMessage()系列方法:
Handler提供了sendEmptyMessage()系列方法,用于发送只包含what字段的消息,不需要传递数据。
方法定义:
// 立即发送
public final boolean sendEmptyMessage(int what)
// 延迟发送
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
// 指定时间发送
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
内部实现:
// Handler.sendEmptyMessage()
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
// Handler.sendEmptyMessageDelayed()
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what; // 只设置what字段
return sendMessageDelayed(msg, delayMillis);
}
使用场景:
场景1:简单的通知消息(不需要传递数据)
// ✅ 适用:只需要通知类型,不需要传递数据
private static final int MSG_UPDATE_UI = 1;
private static final int MSG_REFRESH = 2;
// 发送简单通知
handler.sendEmptyMessage(MSG_UPDATE_UI);
handler.sendEmptyMessageDelayed(MSG_REFRESH, 1000);
// 处理消息
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_UI:
updateUI(); // 不需要数据,直接执行
break;
case MSG_REFRESH:
refreshData();
break;
}
}
场景2:定时任务(只关心执行时间)
// ✅ 适用:定时执行任务,不需要传递数据
handler.sendEmptyMessageDelayed(MSG_CHECK_STATUS, 5000); // 5秒后检查状态
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_CHECK_STATUS) {
checkStatus(); // 检查状态,不需要外部数据
}
}
场景3:状态通知(状态通过what字段表示)
// ✅ 适用:状态通知,状态通过what字段表示
private static final int MSG_LOADING = 1;
private static final int MSG_SUCCESS = 2;
private static final int MSG_ERROR = 3;
// 发送状态通知
handler.sendEmptyMessage(MSG_LOADING);
// ... 执行操作
handler.sendEmptyMessage(MSG_SUCCESS);
// 处理消息
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_LOADING:
showLoading();
break;
case MSG_SUCCESS:
showSuccess();
break;
case MSG_ERROR:
showError();
break;
}
}
对比:sendEmptyMessage() vs sendMessage()
// 方式1:使用sendEmptyMessage()(简洁)
handler.sendEmptyMessage(1);
// 方式2:使用sendMessage()(繁琐)
Message msg = Message.obtain();
msg.what = 1;
handler.sendMessage(msg);
优势:
- 代码简洁:不需要创建Message对象
- 性能更好:方法内部使用Message.obtain()复用对象
- 使用方便:一行代码即可发送消息
适用场景总结:
| 场景 | 是否适用 | 说明 |
|---|---|---|
| 只通知类型,不传递数据 | ✅ 适用 | 如状态通知、定时任务 |
| 需要传递数据 | ❌ 不适用 | 应该使用sendMessage() |
| 需要传递多个参数 | ❌ 不适用 | 应该使用sendMessage() |
| 简单通知消息 | ✅ 适用 | 如UI刷新通知 |
| 定时任务 | ✅ 适用 | 定时执行,不需要数据 |
总结: sendEmptyMessage()系列方法用于发送只包含what字段的消息,不需要传递数据。适合简单的通知消息、状态通知、定时任务等场景。代码简洁,性能好。
34. 为什么Handler可以在子线程创建,在主线程使用?
完整答案:
核心原因:
Handler可以在子线程创建,在主线程使用,是因为Handler绑定的是Looper,而不是创建Handler的线程。
关键点:
-
Handler绑定的是Looper:
- Handler在构造时传入Looper参数
- Handler持有Looper和MessageQueue的引用
- 消息会在Looper所在的线程处理,而不是创建Handler的线程
-
Looper是线程绑定的:
- Looper通过ThreadLocal绑定到线程
- 每个线程有自己的Looper
- Looper在哪里运行,消息就在哪里处理
代码示例:
// 主线程Looper
Looper mainLooper = Looper.getMainLooper();
// 子线程中创建Handler,但绑定主线程Looper
new Thread(() -> {
// 在子线程创建Handler
Handler handler = new Handler(mainLooper) {
@Override
public void handleMessage(Message msg) {
// 消息在主线程处理(因为绑定了主线程Looper)
Log.d("Handler", "处理线程: " + Thread.currentThread().getName());
// 输出:main
}
};
// 在子线程发送消息
handler.sendMessage(Message.obtain().apply { what = 1 });
// 消息会在主线程处理
}).start();
源码分析:
// Handler构造函数
public Handler(Looper looper) {
this(looper, null, false);
}
// Handler内部保存Looper和MessageQueue
final Looper mLooper;
final MessageQueue mQueue;
// Handler.sendMessage()
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
// MessageQueue.enqueueMessage()是线程安全的
boolean enqueueMessage(Message msg, long when) {
synchronized (this) { // 线程安全
// 消息入队
}
}
// Looper.loop()在Looper所在线程运行
public static void loop() {
final Looper me = myLooper(); // 获取当前线程的Looper
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // 在Looper所在线程取出消息
msg.target.dispatchMessage(msg); // 在Looper所在线程处理消息
}
}
详细流程:
1. 子线程创建Handler(绑定主线程Looper)
子线程
↓
new Handler(Looper.getMainLooper())
↓
Handler持有主线程Looper的引用
2. 子线程发送消息
子线程
↓
handler.sendMessage(msg)
↓
MessageQueue.enqueueMessage() [线程安全]
↓
消息进入主线程的MessageQueue
3. 主线程处理消息
主线程Looper.loop()
↓
MessageQueue.next()
↓
Handler.dispatchMessage() [在主线程执行]
↓
Handler.handleMessage() [在主线程执行]
关键理解:
-
Handler不是绑定线程,而是绑定Looper:
- Handler可以绑定任何线程的Looper
- 消息会在Looper所在的线程处理
-
MessageQueue是线程安全的:
enqueueMessage()使用synchronized保证线程安全- 多个线程可以安全地向同一个Handler发送消息
-
Looper在哪里运行,消息就在哪里处理:
- 主线程Looper在主线程运行,消息在主线程处理
- 子线程Looper在子线程运行,消息在子线程处理
常见错误理解:
// ❌ 错误理解:认为Handler绑定的是创建它的线程
new Thread(() -> {
Handler handler = new Handler(); // 这样会出错,因为子线程没有Looper
}).start();
// ✅ 正确理解:Handler绑定的是传入的Looper
new Thread(() -> {
Handler handler = new Handler(Looper.getMainLooper()); // 绑定主线程Looper
// 消息会在主线程处理
}).start();
总结: Handler可以在子线程创建,在主线程使用,是因为Handler绑定的是Looper而不是创建线程。MessageQueue是线程安全的,消息会在Looper所在的线程处理。这是Handler实现跨线程通信的核心机制。
35. MessageQueue使用什么数据结构?为什么使用链表?
完整答案:
数据结构:
MessageQueue使用单链表(单向链表)存储消息。
数据结构实现:
// MessageQueue中的消息结构
public final class MessageQueue {
Message mMessages; // 链表头节点
// Message是链表节点
public final class Message {
Message next; // 指向下一个节点(链表结构)
long when; // 消息执行时间
// ... 其他字段
}
}
为什么使用链表:
-
按时间排序插入:
- 消息需要按
when字段(执行时间)排序 - 链表插入和删除操作效率高(O(1)到O(n))
- 数组插入需要移动元素,效率低(O(n))
- 消息需要按
-
动态大小:
- 消息数量不确定,会动态增减
- 链表不需要预分配空间,动态扩展
- 数组需要预分配或扩容,不够灵活
-
删除操作频繁:
- 消息处理完后需要删除
- 链表的删除操作效率高(O(1)到O(n))
- 数组删除需要移动元素,效率低(O(n))
链表插入算法:
// MessageQueue.enqueueMessage():按时间排序插入
boolean enqueueMessage(Message msg, long when) {
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// 插入到链表头部(O(1))
msg.next = p;
mMessages = msg;
} else {
// 插入到链表中间(O(n))
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break; // 找到插入位置
}
}
msg.next = p;
prev.next = msg;
}
return true;
}
链表优势:
| 操作 | 链表 | 数组 |
|---|---|---|
| 插入(头部) | O(1) | O(n) |
| 插入(中间/尾部) | O(n) | O(n) |
| 删除 | O(1)到O(n) | O(n) |
| 查找 | O(n) | O(1) |
| 内存 | 动态分配 | 连续内存 |
链表劣势:
- 查找效率低:需要遍历链表(O(n))
- 内存碎片:节点分散存储,可能产生内存碎片
- 缓存不友好:节点不连续,缓存命中率低
为什么不用数组或队列:
-
数组不适合:
- 插入需要移动元素,效率低
- 删除需要移动元素,效率低
- 需要预分配或扩容,不够灵活
-
队列不适合:
- 队列是FIFO(先进先出),但Handler需要按时间排序
- 需要支持延迟消息,按
when字段排序 - 需要支持插入到队列头部(高优先级消息)
实际性能考虑:
-
消息数量通常不多:
- 大部分情况下消息队列长度较短
- 链表的O(n)插入操作性能可接受
-
插入操作比查找更频繁:
- 消息经常插入和删除
- 查找操作相对较少(只有next()遍历)
-
延迟消息场景:
- 延迟消息需要按时间排序
- 链表可以灵活插入到合适位置
总结: MessageQueue使用单链表存储消息,因为消息需要按时间排序插入、动态大小、频繁删除。链表插入和删除效率高,适合Handler的消息队列场景。虽然查找效率低,但查找操作不频繁,整体性能可接受。
36. Handler和线程池的区别是什么?
完整答案:
主要区别:
Handler和线程池是两种不同的并发处理机制,各有适用场景。
区别对比:
| 特性 | Handler | 线程池(ExecutorService) |
|---|---|---|
| 机制 | 消息队列机制 | 线程池机制 |
| 线程管理 | 单线程顺序处理 | 多线程并发处理 |
| 任务执行 | 顺序执行(FIFO) | 并发执行 |
| 线程数量 | 每个Handler一个线程 | 可配置线程数量 |
| 适用场景 | 线程间通信、UI更新 | CPU密集型任务、并发处理 |
| 使用复杂度 | 简单 | 较复杂 |
| 性能 | 适合顺序任务 | 适合并发任务 |
详细对比:
1. 任务执行方式:
// Handler:顺序执行(单线程)
Handler handler = new Handler(Looper.getMainLooper());
handler.post(() -> task1()); // 先执行
handler.post(() -> task2()); // 后执行
handler.post(() -> task3()); // 最后执行
// 执行顺序:task1 → task2 → task3(顺序执行)
// 线程池:并发执行(多线程)
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.execute(() -> task1()); // 可能并发执行
executor.execute(() -> task2()); // 可能并发执行
executor.execute(() -> task3()); // 可能并发执行
// 执行顺序:不确定(并发执行)
2. 线程管理:
// Handler:单线程管理
Handler handler = new Handler(handlerThread.getLooper());
// 所有任务在同一个线程中顺序执行
// 线程池:多线程管理
ExecutorService executor = Executors.newFixedThreadPool(5);
// 可以配置线程数量,任务可以并发执行
3. 线程间通信:
// Handler:天然支持线程间通信
Handler mainHandler = new Handler(Looper.getMainLooper());
new Thread(() -> {
// 子线程发送消息到主线程
mainHandler.post(() -> updateUI());
}).start();
// 线程池:需要配合其他机制
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(() -> {
// 需要配合Handler或其他机制进行线程间通信
runOnUiThread(() -> updateUI());
});
使用场景:
Handler适用场景:
- 线程间通信:从子线程更新UI
- 延迟任务:定时执行任务
- 顺序任务:需要按顺序执行的任务
- UI更新:在主线程更新UI
- 定时任务:延迟执行、定时执行
线程池适用场景:
- CPU密集型任务:需要多线程并发处理
- 网络请求:多个网络请求并发执行
- 批量任务:大量任务需要并发处理
- 后台任务:长时间运行的后台任务
- 资源密集型任务:需要充分利用CPU资源
代码示例:
// 场景1:线程间通信(使用Handler)
Handler mainHandler = new Handler(Looper.getMainLooper());
new Thread(() -> {
String result = doWork();
mainHandler.post(() -> {
updateUI(result); // 在主线程更新UI
});
}).start();
// 场景2:并发任务(使用线程池)
ExecutorService executor = Executors.newFixedThreadPool(5);
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int index = i;
Future<String> future = executor.submit(() -> {
return processData(index); // 并发处理
});
futures.add(future);
}
// 场景3:顺序任务(使用Handler)
Handler handler = new Handler(handlerThread.getLooper());
handler.post(() -> step1());
handler.post(() -> step2()); // step1完成后执行step2
handler.post(() -> step3()); // step2完成后执行step3
性能对比:
-
Handler:
- 单线程执行,CPU利用率低
- 适合I/O密集型任务(如网络请求)
- 不适合CPU密集型任务
-
线程池:
- 多线程并发,CPU利用率高
- 适合CPU密集型任务
- 可以充分利用多核CPU
何时使用Handler:
- 需要线程间通信(特别是子线程到主线程)
- 需要顺序执行任务
- 需要延迟或定时执行任务
- 需要更新UI
何时使用线程池:
- 需要并发执行多个任务
- CPU密集型任务
- 批量任务处理
- 需要充分利用CPU资源
总结: Handler是消息队列机制,单线程顺序执行,适合线程间通信和顺序任务。线程池是线程池机制,多线程并发执行,适合CPU密集型任务和并发处理。两者可以配合使用,Handler用于线程间通信,线程池用于并发处理。