[Android面试系列]一句话讲清楚Android消息机制
事件起因
招聘季,面试了一些Android兄弟,发现对基础概念吃的不透.
遂成此文,打算出个一句话系列,讲清楚一些android的基本概念.
让大家在面试的时候能找到心仪的工作,并且不被面试官鄙视.....
此篇内容主要将的Android消息机制相关的内容.
正式开始
Handler,Looper机制是android中的消息系统,两个线程间传递消息,(进程间也可以使用Messenger传递Message).
主要涉及的类
-
Looper
负责从MessageQueue
中获取消息及将消息分发到对应的Handler
. -
Thread
任务执行的线程环境. -
ThreadLocal
负责保存Thread
关联的Looper
. -
ThreadLocalMap
ThreadLocal
中具体保存数据的类.内部使用Entry
数组.来保存数据. -
MessageQueue
保存Message
的容器,内部实现为链表. -
Handler
向MessageQueue
中插入消息,以及对收到的Looper
分发的消息的处理. -
Message
消息载体.主要如下属性-
callback
任务的runnable对象 -
when
message
需要被dispatch
的执行时间 -
obj
message
中传递的对象.
-
进行线程间通讯的主要流程如下
- 创建
Thread
- 在
Thread.run
方法中依次调用Looper.prepare
,Looper.loop
.-
Looper.prepare
主要用来将Looper关联到当前thread的threadLocal.
执行Looper.prepare
时,会判断当前线程如果没有关联looper
时,
就会调用threadLocal.set()
方法向threadLocal
中添加一个创建的Looper
对象. -
Looper.loop
主要用来循环从Looper
的MessageQueue
中取对象.
Looper.loop
方法内部为一个无限循环的for循环.
在for循环中通过调用messqueue.next()
方法来获取到队列中的message.
-
- 创建
Handler
关联到该Looper
- 通过创建的
Handler.sendMessage
方法将message
传递到创建该Handler
所在Looper
中的MessageQueue
. -
MessageQueue
在判断其中有message.when
小于当前时间的message
时,将该消息返回给Looper.loop
-
Looper.loop
在拿到message
后,调用message.target
所关联的Handler
对象的dispatch
方法,
从而实现了在创建Handler
所在线程中执行消息的能力.
如果创建Handler
跟通过该Handler
发送Message
不在同一个线程,则也就实现了线程间通讯的能力.
流程中涉及到的一些技术点
MessageQueue保存在哪
MessageQueue
保存在Looper
中,Looper
保存在ThreadLocal
中,
ThreadLocal
保存在当前Thread
中的ThreadLocal.ThreadLocalMap
中.
MessageQueue中的保存Message的数据结构
MessageQueue
对象中保存着Message
对象,MessageQueue
中保存的Message
对象是以链表数据结构保存的.
通过Message.next指向下一个Message对象.
Message对象的缓存机制
对象的频繁创建销毁,会消耗cpu的性能,而消息传递又会频繁创建消息对象,所以message
对象是会被缓存起来的.
Message
对象通过Message.obtain()
可以获得,.obtain()
方法可以看作是一个工厂方法,
Message
内部维护着一个缓冲区,缓冲区的数据结构也可看做时链表形式.
通过Message.next方法指向下一个可被复用的Message
.
缓存区的大小为50,所以最多可缓存50个Message对象.
ThreadLocal是什么,如何实现的
ThreadLocal是一种保存变量的线程安全的类.
在多线程环境下能安全使用变量,最好的方式是在每个线程中定义一个local变量.
这样对线程中的local变量做修改就只会影响当前线程中的该变量,因此变量也就是线程安全的.
这种保证变量线程安全的思想,实际上就是ThreadLocal的实现.
Threadlocal在调用threadLocal.get方法时,会获取当前thread的threadLocalMap.
threadLocalMap是thread中的一个属性.
第一次调用ThreadLocal.get()
方法时,会先判断当前线程对应的threadlocalMap是否被创建了,
如果没创建则会创建ThreadLocalMap,并把该对象赋值给thread.sThreadLocal对象.后续再获取当前thread的threadLocalMap时,就会取该赋值对象.
ThreadLocalMap就是用来保存线程中需要保存的变量的对象了.
因为threadLocalMap是赋值给当前thread的,属于thread的内部变量,
所以每个线程的threadlocalMap就都是不同的对象,也就是上面说的threadlocal是线程安全的原因了.
ThreadLocalMap内部实际上是一个Entry[]
,用来保存Entry对象的数组.
Entry对象是继承weakReference的,其中Entry的key为ThreadLocal对象,value为threadLocal需要保存的变量值.
调用ThreadLocal.set方法时,会向threadLocalMap中添加一个Entry对象.
调用get方法时,是通过将调用的threadLocal对象本身作为key,来遍历threadLocalMap数组.
当threadLocal等于Entry[]
中的key时,则返回该Entry
中的value.
Looper中的ThreadLocal对象为啥是static的?
其实threadLocal对象是否定义为static对ThreadLocal类本身的作用来讲是没影响的.
但是因为Looper.prepare方法是定义为static的,而prepare中又需要对threadlocal进行访问,所以Looper中的threadLocal对象也就必须定义为static的了.
ThreadLocal中为啥只能保存一个Looper对象
threadLocalMap
可以保存无线多的数据,但是每个线程只能关联一个Looper
对象.
因为threadLocal
中会保存当前线程关联的Looper
对象,这个限制是Looper
做的.
Looper是如何从MessageQueue中获取将要分发的message的
通过Looper.loop
方法来获取并分发message.
Looper.loop
内部为一个无限循环的for循环.在for循环中通过调用messqueue.next()
方法来获取到队列中的message.
messagequeue.next()
方法内部会调用nativePollOnce
方法,该方法会阻塞线程.
该方法的作用简单说,就是当消息队列中没消息时,阻塞掉当前执行的线程.避免过度的cpu消耗.
关于nativePollOnce阻塞线程的解释
当消息队列中有消息时,messagequeue.next()
方法内部会判断现有的消息链表中的消息的message.when
属性是否小于当前时间.
如果小于当前时间,则next
方法会将该条消息返回给loop
中调用next
函数的地方,这样looper就拿到了将要dispatch
的消息了.
所以是通过Messagequeue
来判断message
是否能被looper
进行分发的.
Looper是如何分发message的?
当looper.loop
方法中获取到message
时,会调用message.target.dispatch
方法.
其中message.target
属性是在Handler
内部调用Handler.enqueueMessage
方法时,将当前调用方法的handler
对象设置到Message
中的.
通过message.target.dispatchMessage
方法,将该message
的执行环境切换到了该handler
对应的thread
中.
handler
可以实现handleMessage
方法来处理消息.
调用完handler.dispatchMessage
方法后,则会将该消息通过recycleUnchecked
方法,对message
进行回收.
在Message.recycleUnchecked
方法中会重置该message
对象(将message对象相关属性置空),并将该对象添加到Message
缓存区中.
加入缓存去的过程也就是将该对象加入到缓存区链表中.加入缓冲区的message
后面可通过Message.obtain
方法来获取.
结尾
这文章写了一天吧,希望能帮助到需要面试Android的同学.
喜欢的麻烦点个赞,关注下.