一、Message类:消息的载体
1)获取消息Message对象。一是直接创建消息对象。即Message message=new Message()。二是使用Message.obtain()/Handler.obtainMessage()方法获取。Handler中obtainMessage()方法实质还是调用的Message中obtain()方法。obtain()方法源码如下:
Message.obtain()就是从Message的缓存池中拿到Message对象。Message的缓存池其实使用了一个单向链表存放消息对象。sPool其实就相当于一个头指针,指向缓存池中第一个缓存的Message。假设缓存池中有三个message对象,从缓存池中获取Message对象操作如下图所示。
总结:obtain()的逻辑就是从缓存池中取空闲的Message对象,若存在就返回单向链表的头部message对象,然后指针指向下一个空闲的Message对象,最后将头部Message与链表断开。若缓存池中无空闲message对象,那就直接创建Message返回。
2)什么时候向缓存池中存放Message?
Message类中recycle()方法是用于回收用完的mesage,将此message会回收到缓存池中。recycle()源码如下:
recycle()方法的主要作用:判断当前message是否在使用,若message正在使用,则就会抛出异常;否则调用recycleUnchecked()方法。接下来看下recycleUnchecked()。recycleUnchecked()源码如下图所示。
recycleUnchecked()主要作用:清除msg的一些当前标记。若当前缓存池已经存储的数量小于规定的最大缓存个数MAX_POOL_SIZE,则继续向下执行。假设缓存池中有msg2、msg3,并且sPool指向头部msg2,此时msg1被回收执行recycle()操作(即msg.recycle()),分析如下图所示。
图中�就是将msg1的next指针指向了mPool,此时mPool指向msg2,这样就是将msg1与链表相连接。图中‚就是将msg1赋值给mPool,即将头指针指向msg1。
总结:recycle()就是将当前未使用的message放入到缓存池链表的头部。
2、Looper类:
1)ThreadLocal类
该类是以线程为作用域,,通过ThreadLocal.set()方法可以在指定线程中存储数据,数据存储以后,只有在指定的线程中通过ThreadLocal.get()获取到存储的数据,其他线程则无法获取数据。当某些数据是以线程为作用域时并且不同的线程需要不同的数据副本时,就可以考虑采用ThreadLocal类。
2)创建Looper()对象
对于Handler类来说,需要获取当前线程的looper,那么就需要使用ThreadLocal来实现Looper在线程中的存取。其源码如下:
3)Loop():只有调用该方法,消息循环系统才会起作用。
其源码部分代码如下:
★使用ThreadLocal.get()的方法拿到当前线程Looper对象。
★接下来拿到与当前looper()绑定的MessageQueue对象。
★ 当msg不为null,looper就会处理消息:msg.target.dispatchMessage(msg),此处的msg.target就是发送该消息的Handler的对象。这样handler发送的消息最终交给自己去处理消息(此looper为创建handler时所使用的looper对象,这样就把线程切换到主线程了)。
★looper的退出:looper的退出将会调用looper.quit(),quit()方法实质也是会调用MQ的quit()方法来通知消息队列退出的。当消息队列被标记为退出状态,其message会返回null。
3、MessageQueue类:消息队列
MessageQueue使用一个单链表来维护消息队列,用于将消息插入和读取。
添加Messgae并不是直接添加到MessageQueue中,而是使用的是Handler的sendMessage(msg)方法,在该方法中获取到和该线程绑定的Looper对象和MessageQueue对象,从而将该消息传入到与该线程相关联的MessageQueue。
1)插入消息:enqueueMessage()。源码如下:
当msg.target没有持有改handler的引用、该消息正在使用中或者该线程处于正在退出状态(回收消息,并且),那么就会抛出异常。除此之外,消息正常插入到消息队列中。
★第一种情况:假设该消息队列没有消息、是立即执行的消息或者当前头指针指向的message的执行时间大于即将要插入消息的执行时间(插入消息处理等待时间比链表头指针的消息要短),那么就会直接将该消息插入到链表的头结点位置。如图所示。
★第二种情况:消息队列按照消息处理等待时间顺序(从小到大)进行排列。因此插入新消息需要遍历消息队列,找到新消息插入的位置。
2)读取消息(删除消息):next。
4、Handler类:主要负责处理和发送消息。
1)构造方法
构造方法总结:拿到该线程的looper对象、以及和looper对象绑定MQ。
2)发送消息:使用sendMesssage()、post()方法发送消息。但是post()其实质都是通过send的一系列方法实现的。
handler.sendMessage(msg)
handler.Post(Runnable)
3)处理消息: