转载请注明出处:juejin.im/post/5c9896…面试
以前在郭神的订阅号,看到了这一篇关于Handler
的投稿文章bash
介绍得很详细,分析源码的流程也很清晰。ide
从 Message
的对象获取方式,到 Handler
的 sendMessage
方法解析,再到 enqueueMessage
方法解析。函数
在 enqueueMessage
中没有看到回调方法 handleMessage
,结果是 dispatchMessage
方法调用了 handleMessage
。oop
再看 Handler
和 Looper
类的关系,又发现 loop
方法中调用了 dispatchMessage
,最后分析了 loop
方法,得出结论,MessageQueue
消息队列最后是在这个方法执行的。post
这位做者大佬也是事无巨细,在重要的方法和代码处都加了注释,使得读者理解起来更轻松,值得学习。学习
但因为我的理解能力有限,部分地方还存在疑惑,因而就本身动手丰衣足食,结合面试中可能会问到的一些问题来作了学习。ui
因而有了此文。spa
面试官最爱问的一个问题
首先咱们了解一下
Handler
—— 处理者
Message
—— 消息
MessageQueue
—— 消息队列
Looper
—— 循环者
因而,咱们大概能够这样描述:
消息处理者 Handler
从子线程中发送消息 Meesage
到 消息队列 MessageQueue
中,消息队列 MessageQueue
会对消息进行排序,循环者 Looper
循环地从消息队列 MessageQueue
中取出消息,回调给主线程中的消息处理者 Handler
, 在 handleMessage
方法处理结果。完成一次消息异步发送的流程。
而后,走一遍流程:
public Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//TODO..
}
}
复制代码
handler.sendMessage(m);
handler.post(r);
复制代码
queue.enqueueMessage(msg, uptimeMillis);
复制代码
MessageQueue
中入队方法 enqueueMessage()
,出队方法 next()
boolean enqueueMessage(Message msg, long when) {...
Message next() {...
复制代码
next()
在循环者 Looper
的 loop
方法中被调用
获取到消息后,调用 dispatchMessage(msg)
来分发消息
public static void loop() {...
msg.target.dispatchMessage(msg);
...
复制代码
最终回到了 Handler 类中的 dispatchMessage
方法,若是 Message
是一个纯粹的消息体,那将会调用 handleMessage
方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
到此,走完了此次流程。
我想你大概能知道,如何组织本身的语言回答面试官了。
但,可能多数面试官不止于此。
继续问道...
这个问题,不太难理解,Ctrl+鼠标左键,看过源码的都能知道。咱们跟着源码走一遍
handler.post(new Runnable() {...
复制代码
这里实际调用了 sendMessageDelayed(Message msg, long delayMillis)
将 Runnable
转成了 Message
对象,如何转换的呢
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
复制代码
能够看到 Message
中的 callback
属性是一个 Runnable
对象
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
复制代码
这个问题自己多是有问题的...
loop
方法中为何要用死循环?先说 ActivityThread
,虽然类名以Thread
结尾,但它并无继承 Thread
,它并非一个线程类。
ActivityThread
是Android
应用程序的入口,也就是任何一个进程的主线程入口。
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");
复制代码
这就是一个Java 应用,main
函数是入口,当执行到 loop
方法就开始死循环,若是死循环结束,那将会报错。
能够看到就是这样设计的。目的就是为了咱们点击应用的图标时不会一闪而过,而是正确地打开页面,执行生命周期,响应各类输入事件。
咱们的代码就是在主线程 loop
函数中的死循环中被执行的,因此这和 ANR 是两个不一样的东西。
至于 Looper
会被阻塞,这个更深刻一些,不在这里讨论。
1.输入事件(按键和触摸事件)5s内没被处理。
2.BroadcastReceiver
的事件(onRecieve方法)在规定时间内没处理完(前台广播为10s,后台广播为60s)。
3.service
前台20s后台200s未完成启动。
4.ContentProvider
的publish
在10s内没进行完。
因此它不包括 Looper.loop()
的死循环。
一直想总结一下本身对 Handler
的认识,以加强记忆,帮助学习和理解。
直到今天才完成。
关于 Handler
,它实现了线程之间的通讯,而线程只是CPU调度的最小单位。了解了 Handler
的原理只是看到冰山一角。要想了解更多的通讯方式,还有很长的路要走。
Linux已经拥有的进程间通讯IPC手段包括(Internet Process Connection): 管道(Pipe)、信号(Signal)和跟踪(Trace)、插口(Socket)、报文队列(Message)、共享内存(Share Memory)和信号量(Semaphore)。而 Binder 是主要的IPC方式。
记录在此,仅为学习!
感谢您的阅读!欢迎指正!
参考: