android 多线程学习1:一些基础
android 多线程学习2:线程的创建与方法分析
android 多线程学习3:synchronized与volatile与线程安全对象
android 多线程学习4:线程池ThreadPoolExecutor
android 多线程学习5:AsyncTask
android 多线程学习6:HandlerThread
android 多线程学习7:Handler消息处理机制
为什么使用Handler
为避免多个线程操作UI造成混乱,android规定只许主线程即 UI 线程可以更新 UI 组件。因此在实际开发中,无论有多少个并发线程,最终都必须回到UI线程来进行UI操作,Handler便是解决线程间通信的一种方式。
Handler 的消息流转
Thread
: 线程。通常是UI主线程。
Looper
: 一个线程可以产生一个 Looper 对象,用于创建MessageQueue并循环取出MessageQueue里面的Message,并交给相应的Handler进行处理。
MessageQueue
: 消息队列,用来存放Handler发送的消息,按照先进先出执行,内部使用的单链表的结构。
Message
: 消息的载体。
Handler
: 负责发送消息和处理消息。push新Message到 MessageQueue 里 ; 或者接收 Looper 从 MessageQueue 所取出的Message。
所以,Handler实例 post或send Message 进入 MessageQueue,Looper循环从MessageQueue取出Message,通过Handler的dispatchMessage进行消息分发,以此实现跨线程通信。
Handler 机制分析
-
Handler的构造方法
由以上源码可见,
1. mLooper、mQueue的实例都是在Looper中维护的
2. 子线程创建Handler实例必须先调Looper.prepare()
那么问题来了:
1. Looper.myLooper()只是从 sThreadLocal.get()
,那么是什么时候set进去的?
由下图Looper源码可知:是Looper.prepare()方法创建了Looper实例,并放入sThreadLocal中。而且,一个线程中只能执行一次Looper.prepare(),创建一个Looper实例。
2. 为什么UI线程里实例话不需要先调Looper.prepare() ?
由下图Looper源码可知:Looper.prepareMainLooper()
是在ActivityThread.java 的main() 方法中就已经调用了,所以在主线程可以直接new Handler()
。而且,主线程的Looper实例sMainLooper在Looper类中是作为静态变量存储的。
3. ThreadLocal 是什么?
ThreadLocal是一个线程内部的存储类,可以在指定线程内 set() 存储数据,数据存储以后,只有指定线程 get() 获取数据。
简单来讲,ThreadLocal维护了一个map,key是当前线程号,value是需要存储的值。(所以在某一线程多次调用ThreadLocal的set()是会覆盖前值的)
ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。
3. Looper是怎么循环从MessageQueue中取消息的?
通常 调用 Looper.loop()
就会开启消息循环,从 MessageQueue 逐个取出消息,再由msg.target.dispatchMessage(msg)
进行消息分发。
msg.target 就是Handler实例,是在sendMessage 时就在handler中为msg.target赋值了。
另外,无论是通过handler的send系列方法发送Message,还是通过handler的post系列方法发送Runnable,最后都会调用
enqueueMessage
来消息入列。
其实,handler的post系列方法也是转换成Message来send的。
基本就这样了~
(部分内容参考于网络,如有不妥,请联系删除~)