内容简介:之前在郭神的订阅号,看到了这一篇关于介绍得很详细,分析源码的流程也很清晰。
之前在郭神的订阅号,看到了这一篇关于 Handler
的投稿文章
介绍得很详细,分析源码的流程也很清晰。
从 Message
的对象获取方式,到 Handler
的 sendMessage
方法解析,再到 enqueueMessage
方法解析。
在 enqueueMessage
中没有看到回调方法 handleMessage
,结果是 dispatchMessage
方法调用了 handleMessage
。
再看 Handler
和 Looper
类的关系,又发现 loop
方法中调用了 dispatchMessage
,最后分析了 loop
方法,得出结论, MessageQueue
消息队列最后是在这个方法执行的。
这位作者大佬也是事无巨细,在重要的方法和代码处都加了注释,使得读者理解起来更轻松,值得学习。
但由于个人理解能力有限,部分地方还存在疑惑,于是就自己动手丰衣足食,结合面试中可能会问到的一些问题来做了学习。
于是有了此文。
这是重点
1.Handler 的原理?
面试官最爱问的一个问题
首先我们了解一下
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); } } 复制代码
到此,走完了这次流程。
我想你大概能知道,如何组织自己的语言回答面试官了。
但,可能多数面试官不止于此。
继续问道...
2.Handler 为什么可以 post Runnable?
这个问题,不太难理解,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; } 复制代码
3.Loop 死循环为什么不会导致主线程 ANR?
这个问题本身可能是有问题的...
- 首先我们要知道
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
会被阻塞,这个更深入一些,不在这里讨论。
- 什么情况下会发生ANR?
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方式。
记录在此,仅为学习!
感谢您的阅读!欢迎指正!
参考:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。