内容简介:之前在郭神的订阅号,看到了这一篇关于介绍得很详细,分析源码的流程也很清晰。
之前在郭神的订阅号,看到了这一篇关于 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方式。
记录在此,仅为学习!
感谢您的阅读!欢迎指正!
参考:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python Algorithms
Magnus Lie Hetland / Apress / 2010-11-24 / USD 49.99
Python Algorithms explains the Python approach to algorithm analysis and design. Written by Magnus Lie Hetland, author of Beginning Python, this book is sharply focused on classical algorithms, but it......一起来看看 《Python Algorithms》 这本书的介绍吧!