Android 的消息机制可以用一个邮局系统来比喻,帮助你理解各个组件如何协同工作。我们用四个核心角色来解释:Handler(快递员)、Looper(邮局分拣员)、MessageQueue(邮局信箱)、Message(信件)。
一、核心角色分工
-
Message(信件)
- 包含消息内容(比如文字、任务指令)。
- 信件可以被重复使用(节省资源,类似回收旧信封)。
-
MessageQueue(信箱)
- 存放所有待处理的信件,按接收时间排序。
- 如果信箱空了,会暂时“休眠”等待新信件。
-
Looper(分拣员)
- 不断检查信箱,取出下一封该处理的信件。
- 一个线程只有一个分拣员(比如主线程自带分拣员)。
-
Handler(快递员)
- 负责送信(发送消息到信箱)。
- 负责处理信(收到分拣员送来的信后执行任务)。
二、工作流程(以子线程更新UI为例)
-
创建邮局(Looper)
子线程默认没有分拣员,需要手动开邮局:java Copy Looper.prepare(); // 创建分拣员和信箱 Handler handler = new Handler(); // 绑定当前分拣员 Looper.loop(); // 分拣员开始工作(死循环取信) -
发送信件(Handler发消息)
子线程做完耗时任务后,通过Handler送信到主线程的信箱:java Copy handler.post(() -> { // 这封信的内容是更新UI(会在主线程执行) textView.setText("任务完成"); }); -
处理信件(Looper分拣)
- 主线程的Looper一直检查自己的信箱,发现新信件后交给对应的Handler。
- Handler根据信件内容执行任务(比如更新UI)。
三、关键细节
-
消息优先级
- 即时信件:通过
post()或sendMessage()直接发送,按时间顺序处理。 - 加急信件:标记为异步消息(如UI绘制),可插队处理(需搭配同步屏障)。
- 即时信件:通过
-
避免内存泄漏
- 如果Handler是Activity的内部类,会导致Activity无法销毁。
解决:用静态内部类 + 弱引用。
- 如果Handler是Activity的内部类,会导致Activity无法销毁。
-
复用Message
- 不要一直
new Message(),用Message.obtain()从回收池取旧信件,节省资源。
- 不要一直
-
线程切换
- Handler能跨线程因:信箱归属某个线程的Looper,但其他线程可以投递消息到它。
四、常见问题
- 为什么主线程不用手动创建Looper?
Android在启动主线程时自动调用了Looper.prepareMainLooper()。 - 主线程Looper会卡死吗?
不会。虽然Looper是死循环,但有新消息时才唤醒,没消息时休眠(类似“待机”)。 - 子线程Toast报错?
Toast内部依赖Handler,子线程没有Looper导致崩溃。需先调用Looper.prepare()。
总结
消息机制的本质是“事件驱动”:
-
Handler负责在不同线程间传递任务(像快递员)。
-
Looper和MessageQueue确保任务按顺序执行(像分拣员和信箱)。
掌握这套机制,就能优雅处理异步任务和UI更新!参考资料