Handler机制流程梳理、源码分析
2016-09-20 19:51
441 查看
Handler机制流程梳理源码分析
Handler机制的相关类
Looper与MessageQueue
LooperprepareMainLooper
Looper与MessageQueue以及当前线程的绑定
Looperloop
Message
创建Message对象
Message和Handler的绑定
Handler
Handler的构造方法
Handler发送消息
Handler处理消息
Handler、Looper、MessageQueue、Message
那么这几个类的作用究竟是什么呢,他们之间又有什么关系,我们接下来继续看。
那么这几个方法中都做了些什么,我们接下来看一下。
在这个方法中,首先判断了是否已经创建了Looper对象,没有的话就会调用Looper的构造方法,并set给sThreadLocal。
已经创建过得话,即
通过看源码我们可以发现,Handler的obtainMessage()方法,内部就是调用了Message的obtain(Handler)方法,那么这么创建有什么好处呢?首先我们知道,直接通过new创建每次都会创建一个新的对象,这样不停的创建新的对象,会消耗资源。而使用Message.obtain()方法,第一次获取的时候,sPool为空,所以直接返回一个new Message()对象。
而在上面Looper的源码中,当handler进行dispatchMessage方法后,就会对message进行回收,这时候进入 recycleUnchecked()方法中,可以看到将使用过得message对象赋值给了sPool,这时候sPool就不为空了,而再次调用message.obtain()方法的时候,就避免了直接创建新的message对象,而是可以从回收的取出一个来进行复用。
因此在创建Message对象的时候,任何时候都推荐使用上面的2种方法,而不是直接使用new关键字来进行创建。
那么通过Message.obtain()这个无参的方法创建的Message对象,是怎么和Handler完成绑定的呢?
我们可以先看一段Handler的源码,在发送消息的时候,sendMessage通过一系列的调用,最终会调用enqueueMessage方法,在这里,就可以看到,发送的message即msg,在发送的时候,将this即hanlder对象与Message绑定到了一块。
- sendMessage(Message)
sendMessage方法通过一系列重载方法的调用,sendMessage调用sendMessageDelayed,继续调用sendMessageAtTime,继续调用enqueueMessage,继续调用messageQueue的enqueueMessage方法,将消息保存在了消息队列中,而最终由Looper取出,交给Handler的dispatchMessage进行处理。
我们可以看到在dispatchMessage方法中,message中callback是一个Runnable对象,如果callback不为空,则直接调用callback的run方法,否则判断mCallback是否为空,mCallback在Handler构造方法中初始化,在主线程通直接通过无参的构造方法new出来的为null,所以会直接执行后面的handleMessage()方法。
post(Runnable)
post(Runnable)方法,实际上执行的是sendMessageDelayed方法,先将Runnable对象赋给了Message对象中的callback,然后通过sendMessage方法向下传递,中间过程同上面的相同,在dispacthMessage方法中,调用了handleCallback方法,在handleCallback方法中,最终执行Runnable对象的run()方法。
Handler机制的相关类
Looper与MessageQueue
LooperprepareMainLooper
Looper与MessageQueue以及当前线程的绑定
Looperloop
Message
创建Message对象
Message和Handler的绑定
Handler
Handler的构造方法
Handler发送消息
Handler处理消息
Handler机制流程梳理、源码分析
在上几篇文章中我们已经介绍过了Android中使用线程、线程池以及异步任务的方法,通过对AsyncTask的源码分析,我们知道它的底层实现就是通过线程以及Handler机制来完成的,Handler机制也是Android提供的一种针对异步操作的方式,可以实现主线程与子线程之间的通信,接下来本篇文章就针对Android中的Handler机制进行解剖,总结。Handler机制的相关类
首先我们看一下Handler的重载的构造方法,其中无参的构造方法又默认调用下面的这个构造方法。而这个构造方法中又做了那些事情呢?我们继续看,首先通过Looper.myLooper()初始化Looper对象mLooper,然后又初始化了MessageQueue对象mQueue。加上Handler发送的消息,整个Handler机制涉及到了如下几个类:Handler、Looper、MessageQueue、Message
public Handler(Callback callback, boolean async) { //前面省略 mLooper = Looper.myLooper();//获取Looper,**注意不是创建Looper**! if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;//创建消息队列MessageQueue mCallback = callback; //初始化了回调接口 mAsynchronous = async; }
那么这几个类的作用究竟是什么呢,他们之间又有什么关系,我们接下来继续看。
Looper与MessageQueue
使用过Handler的同学们知道,在主线程使用Handler的时候,并没有手动的创建Looper以及MessageQueue,实际上在主线程ActivityThread中,系统已经为我们创建了,源码如下,在ActivityThread的main方法中:public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); //其他代码省略... Looper.prepareMainLooper(); //初始化Looper以及MessageQueue ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); //开始轮循操作 throw new RuntimeException("Main thread loop unexpectedly exited"); }
那么这几个方法中都做了些什么,我们接下来看一下。
Looper.prepareMainLooper():
这个方法主要是初始化Looper对象以及MessageQueue对象,首先执行了Looper的prepare(boolean quitAllowed)方法:public static void prepareMainLooper() { prepare(false);// synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
在这个方法中,首先判断了是否已经创建了Looper对象,没有的话就会调用Looper的构造方法,并set给sThreadLocal。
已经创建过得话,即
if (sThreadLocal.get() != null),就会抛异常,这也体现了,一个线程里最多只能创建一个Looper对象以及一个MessageQueue(主线程已为我们创建好,而子线程默认没有)。
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
Looper与MessageQueue以及当前线程的绑定:
再看Looper的构造方法,在构造方法中创建了消息队列MessageQueue,并且绑定了当前的线程。private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
Looper.loop():
首先里面调用了sThreadLocal.get()获得刚才创建的Looper对象,后面进入一个死循环,从消息队列不断的取消息,如果有消息则交给Handler进行dispatchMessage,否则进入阻塞状态。public static void loop() { final Looper me = myLooper();//里面调用了sThreadLocal.get()获得刚才创建的Looper对象 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); }//如果Looper为空则会抛出异常 final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { //这是一个死循环,从消息队列不断的取消息 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } d8c6 msg.target.dispatchMessage(msg);//msg.target就是绑定的Handler,详见后面Message的部分,Handler开始dispatchMessage(msg) //后面代码省略..... msg.recycleUnchecked(); } }
Message
Message,即消息,实现了Parcelable接口,Message是怎么携带“消息内容”的呢?Message里面有4个变量,3个int型,what,arg1,arg2,以及一个Object型的变量obj,其中我们可以将需要传递的数据赋值obj,而在handleMessage方法中,就可以拿到我们所需要的数据进行处理了,而what可以作为一个标识符,来判断接收到的message对象里面存储的是什么数据类型,这样就可以针对不同的数据分别作出相应的操作了。创建Message对象
为了发送消息,我们首先要获得Message对象,有些同学可能说了直接new一个出来就好了?实际上new出来也是可以的,但是有一种更好的方式。Message的获得由2种方式,一种是Handler的obtainMessage()方法,另一种是Message类的静态方法,Message.obtain()。public final Message obtainMessage() { return Message.obtain(this); }
通过看源码我们可以发现,Handler的obtainMessage()方法,内部就是调用了Message的obtain(Handler)方法,那么这么创建有什么好处呢?首先我们知道,直接通过new创建每次都会创建一个新的对象,这样不停的创建新的对象,会消耗资源。而使用Message.obtain()方法,第一次获取的时候,sPool为空,所以直接返回一个new Message()对象。
public static Message obtain(Handler h) { Message m = obtain();//调用重载的obtain方法 m.target = h;//并绑定的创建Message对象的handler return m; } public static Message obtain() { synchronized (sPoolSync) {//sPoolSync是一个Object对象,用来同步保证线程安全 if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
而在上面Looper的源码中,当handler进行dispatchMessage方法后,就会对message进行回收,这时候进入 recycleUnchecked()方法中,可以看到将使用过得message对象赋值给了sPool,这时候sPool就不为空了,而再次调用message.obtain()方法的时候,就避免了直接创建新的message对象,而是可以从回收的取出一个来进行复用。
void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
因此在创建Message对象的时候,任何时候都推荐使用上面的2种方法,而不是直接使用new关键字来进行创建。
Message和Handler的绑定
通过这段代码,可以看到通过obtainMessage()创建的时候,就将Handler赋给了Message对象的target,完成了绑定。public static Message obtain(Handler h) { Message m = obtain();//调用重载的obtain方法 m.target = h;//并绑定的创建Message对象的handler return m; }
那么通过Message.obtain()这个无参的方法创建的Message对象,是怎么和Handler完成绑定的呢?
我们可以先看一段Handler的源码,在发送消息的时候,sendMessage通过一系列的调用,最终会调用enqueueMessage方法,在这里,就可以看到,发送的message即msg,在发送的时候,将this即hanlder对象与Message绑定到了一块。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this;//将handler与message完成绑定 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
Handler
我们通过上面的源码,已经知道了Looper将Message对象从MessageQueue取出来,最终交给了Handler进行派发,下面我们来看一下Handler的源码,了解一下是怎么实现的。Handler的构造方法:
在主线程通过无参的构造方法创建,而无参的构造方法调用重载的构造方法,并在此拿到了这个线程中对应的Looper以及MessageQueue,这样就完成了Handler与Looper、MessageQueue之间的绑定。public Handler() { this(null, false); //无参的构造方法调用重载的构造方法 } public Handler(Callback callback, boolean async) { //此处代码省略... mLooper = Looper.myLooper();//拿到了当前线程的绑定的Looper if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;//拿到消息队列 mCallback = callback; mAsynchronous = async; }
Handler发送消息:
Handler发送消息的重载方法很多,但是主要只有2种。- sendMessage(Message)
sendMessage方法通过一系列重载方法的调用,sendMessage调用sendMessageDelayed,继续调用sendMessageAtTime,继续调用enqueueMessage,继续调用messageQueue的enqueueMessage方法,将消息保存在了消息队列中,而最终由Looper取出,交给Handler的dispatchMessage进行处理。
我们可以看到在dispatchMessage方法中,message中callback是一个Runnable对象,如果callback不为空,则直接调用callback的run方法,否则判断mCallback是否为空,mCallback在Handler构造方法中初始化,在主线程通直接通过无参的构造方法new出来的为null,所以会直接执行后面的handleMessage()方法。
public void dispatchMessage(Message msg) { if (msg.callback != null) {//callback在message的构造方法中初始化或者使用handler.post(Runnable)时候才不为空 handleCallback(msg); } else { if (mCallback != null) {//mCallback是一个Callback对象,通过无参的构造方法创建出来的handler,该属性为null,此段不执行 if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg);//最终执行handleMessage方法 } } private static void handleCallback(Message message) { message.callback.run(); }
post(Runnable)
post(Runnable)方法,实际上执行的是sendMessageDelayed方法,先将Runnable对象赋给了Message对象中的callback,然后通过sendMessage方法向下传递,中间过程同上面的相同,在dispacthMessage方法中,调用了handleCallback方法,在handleCallback方法中,最终执行Runnable对象的run()方法。
private static Message getPostMessage(Runnable r) { Message m = Message.obtain();//将Runnable对象封装成Message对象 m.callback = r; return m; } public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0);//sendMessage方法继续发送 } public void dispatchMessage(Message msg) { if (msg.callback != null) {//callback不为空,调用handleCallback方法 handleCallback(msg); } } private static void handleCallback(Message message) { message.callback.run();// message.callback即上面的post()中的Runnable对象,最终执行了run方法。 }
Handler处理消息:
在handleMessage(Message)方法中,我们可以拿到message对象,根据不同的需求进行处理,整个Handler机制的流程就结束了。相关文章推荐
- Mybatis执行dao接口方法的流程梳理及源码分析
- handler机制 源码分析 梳理
- uCrop源码思路分析--流程梳理
- Android电话拨打流程源码分析
- struts2流程和源码分析
- 从源码分析View绘制流程
- Android系统分析之View绘制流程与源码分析
- Mybatis工作机制源码分析—一次select请求处理流程
- MySQL源码分析及核心内幕之4 -- 源码服务端main函数开始及启动流程
- HBase Split流程源码分析
- Android O: 触摸事件传递流程源码分析(上)
- Android O Touch事件处理流程源码分析
- Android应用层View绘制流程与源码分析
- Spring 源码分析《Bean的获取与创建流程》
- Android恢复出厂设置流程分析【Android源码解析十】
- Vue2源码分析-逻辑梳理
- Android电话拨打流程源码分析
- Android输入事件流程中的EventHub分析及源码演示
- Activity启动流程源码分析之Launcher启动(二)
- Android 7.0 Gallery图库源码分析2 - 分析启动流程