Android 消息循环流程图如下所示:
总体来说:
Handler 发送的消息由 MessageQueue 存储管理,并由 Looper 负责回 调消息到 handleMessage()。
线 程 的 转 换 由 Looper 完 成 , handleMessage() 所 在 线 程 由 Looper.loop() 调用者所在线程决定。
主要涉及的角色如下所示:
message:消息。
MessageQueue:消息队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message。读取会自动删除消息,单链表维护,插入和删除 上有优势。在其 next()方法中会无限循环,不断判断是否有消息,有就返 回这条消息并移除。
Looper:消息循环器,负责关联线程以及消息的分发,在该线程下从 MessageQueue 获取 Message,分发给 Handler,Looper 创建的时候会
创建一个 MessageQueue,调用 loop()方法的时候消息循环开始,其中 会不断调用 messageQueue 的 next()方法,当有消息就处理,否则阻塞在 messageQueue 的 next()方法中。当 Looper 的 quit()被调用的时候会调用 messageQueue 的 quit(),此时 next()会返回 null,然后 loop()方法也就跟 着退出。
Handler:消息处理器,负责发送并处理消息,面向开发者,提供 API, 并隐藏背后实现的细节。
整个消息的循环流程还是比较清晰的,具体说来:
1 、 Handler 通 过 sendMessage() 发 送 消 息 Message 到 消 息 队 列 MessageQueue。
2、Looper 通过 loop()不断提取触发条件的 Message,并将 Message 交 给对应的 target handler 来处理。
3、target handler 调用自身的 handleMessage()方法来处理 Message。 事实上,在整个消息循环的流程中,并不只有 Java 层参与,很多重要的工作都 是在 C++层来完成的。我们来看下这些类的调用关系。
注:
在这些类中 MessageQueue 是 Java 层与 C++层维系的桥梁,MessageQueue 与 Looper 相关功能都通过 MessageQueue 的 Native 方法来完成,而其他虚线连接 的类只有关联关系,并没有直接调用的关系,它们发生关联的桥梁是 MessageQueue。
常见面试题:
1.Handler 引起的内存泄露原因以及最佳解决方案
Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。 这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就 导致 Activity 泄露。
解决:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并在 Acitivity 的 onDestroy()中调用 handler.removeCallbacksAndMessages(null)及时 移除所有消息。
参考代码如下:
private static class SafeHandler extends Handler {
private WeakReference ref;
public SafeHandler(HandlerActivity activity) {
this.ref = new WeakReference(activity);
}
@Override
public void handleMessage(final Message msg) {
HandlerActivity activity = ref.get();
if (activity != null) {
activity.handleMessage(msg);
}
}
}
并且再在 Activity.onDestroy() 前移除消息,加一层保障:
@Override
protected void onDestroy() {
safeHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
2.为什么我们能在主线程直接使用 Handler,而不需要创建 Looper ?子线程使用Hander的步骤?
通常我们认为 ActivityThread 就是主线程。事实上它并不是一个线程,而是主 线程操作的管理者。在 ActivityThread.main() 方法中调用了 Looper.prepareMainLooper() 方法创建了 主线程的 Looper ,并且调用了 loop() 方法,所以我们就可以直接使用 Handler 了。 因此我们可以利用 Callback 这个拦截机制来拦截 Handler 的消息。如大部分 插件化框架中 Hook ActivityThread.mH 的处理。
在 ActivityThread.main() 方法中有如下代码:
//android.app.ActivityThread
public static void main(String[] args) {
//...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper.prepareMainLooper(); 代码如下:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
可以看到在 ActivityThread 里 调用了 Looper.prepareMainLooper() 方法创建了 主线程的 Looper ,并且调用了 loop() 方法,所以我们就可以直接使用 Handler 了。
所以子线程使用handler,创建Looper必须使用prepare()---->loop()--->quit();
3.主线程的 Looper 不允许退出
主线程不允许退出,退出就意味 APP 要挂。
调用Looper.quit()最终会调用MessageQueue的quit()方法:
MessageQueue中代码如下:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
、、、、、、、、、、、、、、、、
}
4.Handler 里藏着的 Callback 能干什么?
来看看 Handler.dispatchMessage(msg) 方法:
public void dispatchMessage(Message msg) {
//这里的 callback 是 Runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果 callback 处理了该 msg 并且返回 true, 就不会再回调 handleMessage
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到 Handler.Callback 有优先处理消息的权利 ,当一条消息被 Callback 处理并拦截(返回 true),那么 Handler 的 handleMessage(msg)
方法就不会被调用了;如果 Callback 处理了消息,但是并没有拦截,那么就意味着一个消息可以同时被 Callback 以及 Handler 处理。
这个就很有意思了,这有什么作用呢?
我们可以利用 Callback 这个拦截机制来拦截 Handler 的消息!
场景:Hook [ActivityThread.mH], 在 ActivityThread 中有个成员变量 mH
,它是个 Handler,又是个极其重要的类,几乎所有的插件化框架都使用了这个方法。
5.创建 Message 实例的最佳方式
为了节省开销,Android 给 Message 设计了回收机制,所以我们在使用的时候 尽量复用 Message ,减少内存消耗:
通过 Message 的静态方法 Message.obtain();
通过 Handler 的公有方法 handler.obtainMessage()。
总结发言:
- Handler 的背后有 Looper、MessageQueue 支撑,Looper 负责消息分发,
- MessageQueue 负责消息管理;
- 在创建 Handler 之前一定需要先创建 Looper;
- Looper 有退出的功能,但是主线程的 Looper 不允许退出;
- 异步线程的 Looper 需要自己调用 Looper.myLooper().quit(); 退出;
- Runnable 被封装进了 Message,可以说是一个特殊的 Message;
- Handler.handleMessage() 所在的线程是 Looper.loop() 方法被调用的线程,也可以说成
Looper 所在的线程,并不是创建 Handler 的线程; - 使用内部类的方式使用 Handler 可能会导致内存泄露,即便在 Activity.onDestroy 里移除延时消息,必须要写成静态内部类;
参考:https://juejin.im/post/6844903783139393550