Handler分析
一、概念
Handler是Android消息处理的一种上层接口,是一种更新UI的机制。通过发送和处理Message和Runnable对象来关联相对应线程的MessageQueue
二、使用方法
post(Runnable run):内部实现仍是sendMessageDelayed(Message msg, long delayMillis)
sendMessage(Message msg)
三、源码分析
Handler、MessageQueue、Message、Looper、ThreadLocal关系图示
Looper类分析
整体类结构
2、构造方法
首先Looper的构造方法是私有的,其次Looper构造方法中初始化了MessageQueue和Thread两个final包级变量,也就是说当Looper创建时,该Looper会持有一个当前的线程Thread对象和MessageQueue对象。那么从这里可以知道,Looper的构造方法必然在Looper类内部调用,从Looper类内部找到Looper构造的地方。
从上面代码可以看出,创建Looper的最终是prepare()方法,从这里面可以看到一个运行时异常抛出,从这个异常的Message我们可以得到,一个Looper只能关联一个线程。而这个线程关联由Looper中定义的一个static final限定的ThreadLocal有关(命名方式为sTreadLocal)。(Q1:sThreadLocal和mThread各有什么作用呢?)。
在这里还有一个prepareMainLooper方法,根据方法的注释,该方法创建每个应用程序的主线程,它是被安卓环境调用和创建的,开发者不能调用该方法,这里可以看到synchronized锁加在Looper.class上,所有的类对象只有一个class对象,这可以称为全局锁,一旦一个线程持有了这个对象,其他线程则无法获取该class对象锁,而获取这个class对象锁的线程就是主线程。sMainLooper就保存着主线程的looper对象实例,它是静态私有的。
上面分析了Looper和线程之间的操作关系,下面分析以下,Looper类对MessageQueue的操作,Looper类中对MessageQueue的操作主要在loop()方法中:
首先loop方法获取了Looper对象,如果线程和looper没有绑定时无法操作,looper中的MessageQueue的。之后获取了MessageQueue对象,重置了该线程的进程标志,确保在自己的当前本地进程中调用。第二次重置,同时返回了一个加密令牌ident。之后开启了一个for死循环。循环中的内容如下:
循环过程中,先在MessageQueue获取Message对象,如果Message为空则返回。之后时获取当前Looper的Print对象,这个对象时用来控制台打印Message分发的一些信息。slowDispatchThresholdMs一旦被设置,在分发Message的过程中,如果超过这个时间,则会打印一些警告。之后的traceTag和Trace是将一些信息压入系统Trace缓存中,用于系统分析日志。这里重点代码是msg.target.dispatchMessage(msg)。这里就是最终Message分发的地方,调用的target即是Handler对象。
消息分发之后,处理slowDispatchThresholdMs>0的情况下,若超时,则打印警告信息。之后则是比较了之前的加密令牌,如果进程有变化,则抛出运行时异常。最后对分发后的message进行了回收(Q2:回收具体做了什么呢)。
Message类分析
Message类是一个实体类,实现了Parcelable接口。定义了一些常用的属性和方法。从图中可以找到Q2的答案,即当Message对象在回收池中时,将其标记为已经被消费的状态,回收方法初始化了Message对象的细节信息。
在Message类中,Android还对Message对象做了优化处理,这里有一个消息池,这里消息池最大的存储为50,所以建议在创建Message对象时,尽量使用obtain的工厂方法来获取Message对象,这里Android做了优化,可以保证Message的重复利用。Message类还实现了序列化接口和方法,说明Message可以使用Intent、Bundle传输。(Q3:Message和Messager有什么区别?)。
MessageQueue分析
整体上来说MessageQueue实现了一个Message队列(先入先出)。我们可以先分析主要的两个入队(enqueueMessage)和出队(next)方法
入队分析:
enqueueMessage是包级函数,它是被Handler调用,从而进行入队操作。这里入队操作时线程安全的。首先入队操作对Message对象进行了检查,target必须不为空,并且不能是已标记为已使用的Message。否则会抛出运行时异常。如果放弃处理消息(mQuitting==true),则对入队message进行回收处理,并返回false。
入队时,首先标记message为可用状态,之后判断如果是否需要创建新的头节点,作为头节点入队的条件是,Message是同步的或者比之前的异步头节点时间要求及时性更高的。否则进入for循环,进行入队操操作。循环体中则进行了按照时间排序的入队操作。在整个入队过程中,有一个needWake标记,该标记用于唤起目标线程锁。
出队分析:
出队操作是MessageQueue的next方法。在该方法中首先查看是否消息队列已经取消,取消则返回空,否则开启循环出队msg消息。循环体中flushPendingCommand方法是确保任何挂起的对象引用已经释放,以防止该进程持续保留超过所需时间的对象,因为循环体可能执行较长时间的操作。
之后同步代码块屏蔽了非空但是没有处理target为空的message。一般情况下我们都有自己定义的Handler。下一步就是达到要求(立即处理的消息或者已经到时间的消息)的消息出队操作。可以看到最后头节点mMessages指向了下一个message对象,出队的Message对象的next置为空,是为了防止用户对该Message进行操作,返回的message标记为了已使用状态。
之后的一系列代码则是如果mQuitting == true,则调用dispose()方法,销毁本地底层MessageQueue。如果mQuitting == false,则检查用户是否在没有消息时设置了IdleHandler,如果没有设置则调用continue方法继续循环查询,如果设置了IdleHandler,则执行Idlehandler接口中的方法,返回值的含义是是否保留在MessageQueue中这个IdleHandler操作。IdleHandler方法可以用于处理一些垃圾回收或者其他用户自定义的任务(具体可以参考ActivityThread)。下面是ActivityThread中实现IdleHandler的代码。
还有一点需要说明的是,在MessageQueue中有Native代码,这些代码主要是用来本地检测消息队列是否有消息和线程锁的唤醒操作。
Handler类分析
对于Handler我们可以从Handler中定义的成员变量开始分析。Handler定义了Looper、MessageQueue、Callback三个final变量,还有一个IMessager信使接口。从Looper和MessageQueue类的分析中我们可以得到,MessageQueue是Looper控制的,那么Handler中的MessageQueue是怎么来的呢?由于Looper、MessageQueue、Callback都是final变量,所以下一步我们就要从Handler的构造方法中分析这个问题。
Handler的构造方法,从具体内容上来说有两种构造方法,一种是Looper为主线程的,一种是自定义Looper的。具体如下:
方式一:
方式二:
不管是方式一,还是方式二,我们都可以看到MessageQueue的方式都是Looper中获取的,只不过第一种方式是主线程的Looper,第二种方式是用户传入的Loope。也就是说Handler通过Looper和MessageQueue建立的联系,Looper的loop()方法用来处理获取MessageQueue的Message消息,并转发给Message中Target(Handler)的dispatchMessage处理消息。而我们使用Handler去发送消息给消息队列时,无论是使用post的方式还是使用sendMessage的方式,它们都是sendMessageAtTime(Message, long)的封装,最后都会调用到enqueueMessage(MessageQueue, Message, long)方法,而这个方法调用的是MessageQueue的enqueueMessage(Message, long),即MessageQueue的入队操作。
下面分析Handler中的消息分发处理方法dispatchMessage(Message)。
从dispatchMessage方法中我们可以看出,首先判断msg.callback如果不为空则交给HandlerCallback(Message)处理,也就是msg中传入了Runnable回调接口(这种方式的Message只能通过Message.obtain(Handler, Runnable)来获取),则会调用Message回调接口中的run方法进行处理。如果没有查看Handler中是否设置Callback回调,如果设置则调用回调接口的handleMessage(Message)方法进行处理。如果仍未设置,则调用Handler类中的handlerMessage(Message)方法进行处理(一般子类要实现这个方法,Handler类中只是一个空方法)。
在Handler中的其他一些关于Message移除和判断是否有Message类的方法,都是对MessageQueue的操作的。
Handler的一些主要方法都介绍完了,这里还遗留了一个问题Q4:Handler中的Messager是做什么的?这里其实Messager与跨进程通信有关,Q3和Q4在跨进程通信IPC分析中再具体分析。
那么还遗留了一个问题就是Q3,ThreadLocal类分析如下
ThreadLocal类分析
这里主要分析Looper和ThreadLocal的关系和作用,首先在Looper构造方法中我们可以得到mThread主要是保存了Looper当前持有的线程信息,也就是说mTread是一个线程对象。而ThreadLocal则是用于保证每个线程只能有一个线程类。ThreadLocal中实现了一个弱引用的线程池。我们首先分析set(T)方法。可以看到该方法获取了当前线程TreadLocalMap并将Looper设置给了这个Map,这样当前线程thread、静态final对象sThreadLocal、Looper就联系在了一起。再看get(T)方法。
通过线程获取了当前线程的TreadLocalMap,并从中获取了挡墙sThreadLocal对应的Looper。
由于sThreadLocal是静态且唯一的,那么获取的Looper也是唯一的。在Looper.prepare方法中我们也可以看到一旦从sThreadLocal中获取到了当前Looper,那么就会报运行时异常。如果没有获取到,就把当前Looper放置进去。总结如下,mThread对应着一个sThreadLocal,sTreadLocal中保留着一个ThreadLocalMap,这个Map的Key就是sThread对象本身,所以获取得到的Value也就是唯一的,从而保证了Loope的唯一性。但是由于设置过程可以覆盖,所以我们需要先get判断是否为已经含有了一个Looper。从myLooper中我们也可以看出,获取当前线程的Looper方式就是从sTreadLocal中获取的。