Android 中的消息处理机制

这篇文章是我研究 Android 消息处理机制及其源码后写的小笔记,本是整理思绪用的,现拿出来分享一下。


Message

Message 是一个包装消息的类,维护 what:intarg1:intarg2:intobj:Object 等几个表示数据的公有变量,以及 data:Bundle 这个打包的数据、target:Handler 这个用来处理自身的 Handler 对象和 callback:Runnable 这个让 target 处理的回调对象。

Handler

Handler 用于处理 Looper 分发的消息,在创建其对象时必须绑定一个 Looper 对象,构造函数参数为空时会自动获取当前线程维护的 Looper 对象。它自身有一个可被重写的成员方法 void handleMessage(Message msg) 以及一个含有同名方法的类内接口 Callback,所以说可以通过继承 Handler 类或者赋予 Callback 对象的方式来让程序员决定如何处理消息。

Handler 有一个签名为 void dispatchMessage(Message msg) 成员方法,用来给系统(Looper.loop())调用处理消息。该成员方法的逻辑是优先执行参数的 callback 成员,其为空时执行自身注册的 Callback 对象中的 void handleMessage(Message msg) 方法,Callback 对象也为空时才处理继承来的 void handleMessage(Message msg) 方法。

对 Handler 对象调用 boolean sendMessage(Message msg) 这个成员方法来向 Looper 中的 MessageQueue 发送消息,或者调用 boolean sendEmptyMessage(int what)boolean post(Runnable r) 等成员方法让系统帮忙包装一个 Message 对象并发送它。无论哪种发送消息的方法最后都会通过互相委托将 Message 对象的 target:Handler 成员设为自身,即“发出消息者一定是处理消息者”。

Looper

Looper 处于某一个线程中,通过循环使得该线程持续运行,它维护一个 MessageQueue 对象,并提供几个静态方法作为接口:static void prepare() 初始化循环;static void loop() 执行循环——它不断地从 MessageQueue 对象中拿取 msg:Message,然后调用 msg.target.dispatchMessage(msg) 来处理消息。

如何使用

要想让某个线程能够处理耗时较长的异步消息,就必须保证该线程持续运行(消息发来而线程却已经结束了那岂不是处理了个寂寞)。程序员首先应当在需要处理消息的线程上使用 Looper.prepare()Looper.loop() 来开启循环和消息队列,然后构造一个对象 handler:Handler,使其绑定到这个循环上,此时可为其注册默认的处理函数。现在无论在何处,只要有 handler 的引用就可以调用 handler.sendMessage(msg) 等方法来发送消息,使得 handler 能够在其所处的线程中处理消息。

Android 程序中,为了防止子线程中改变 UI 后使主线程的运行出现错误,更新 UI 的方法(比如发送一个 Toast,或是更新某个 TextView 控件上的文本)被限制在了主线程中,配套的解决方案就是上述的 Message-Handler-Looper 消息处理机制,这套机制可以线程安全地更新 UI。另外,主线程是默认开启一个 Looper 并维护一个私有 Handler 成员的,可以用 Activity 提供的 void runOnUiThread(Runnable action) 方法向主线程注册回调函数,系统会将其包装为 Message 对象并发送给 Handler 对象处理,这样子线程只需要拿到 Activity 对象的引用就可以让主线程更新界面。

本笔记记录的知识应用于我的项目 DiseaseCharts 中用于表示刷新成功与否的 void printMessage(final String msg) 方法以及各个图表数据刷新后播放动画的 void animate() 方法。这套机制适用但不限于解决异步请求与界面更新的问题,其中的思路非常值得学习,也希望我踩过的坑能让你少走一点弯路。


勿做约定。这是第七片星星。