Android源码分析之Message
准备开始写点东西,算是对自己阅读源码的一个记录/笔记,也希望能对同样感兴趣的人有所帮助,希望
能坚持下去,嘿嘿。
在Android的开发中,我们经常用到Handler.postXXX方法,或者View.postXXX方法,用来在下一次looper
到来时执行。我是那样的人,什么事情最好能够知道下内部实现机理是什么,否则我在用它的时候可能会觉得不爽,
或者说不自然,不太愿意去用。典型例子就是我始终不太愿意用Android引入的SparseArray<E>,而是一直坚持
Java的HashMap<Key, Value>,直到我自己读了SparseArray<E>的源码,才开始放心大胆的使用。
其实说来也巧,为什么忽然决定写这个,是有一天看到了类似这样的代码:
private Runnable mRunnable = new Runnable() { @Override public void run() { // do something... removeCallbacks(this); } }; postDelayed(mRunnable, SOME_DELAY_IN_MILLIMS);
第一直觉告诉我run方法里的removeCallbacks(this);调用显然是多余的。通过通读代码发现的确是如此,因为任何Message
(即使post的是Runnable也会被包装到Message里)在被处理之前都已经从MessageQueue里取出来了(delete掉了,所以客户端
代码大可不必有这样的代码)。这里顺便提下慎用View.removeCallbacks的返回值,看源码:
1 /** 2 * <p>Removes the specified Runnable from the message queue.</p> 3 * 4 * @param action The Runnable to remove from the message handling queue 5 * 6 * @return true if this view could ask the Handler to remove the Runnable, 7 * false otherwise. When the returned value is true, the Runnable 8 * may or may not have been actually removed from the message queue 9 * (for instance, if the Runnable was not in the queue already.) 10 * 11 * @see #post 12 * @see #postDelayed 13 * @see #postOnAnimation 14 * @see #postOnAnimationDelayed 15 */ 16 public boolean removeCallbacks(Runnable action) { 17 if (action != null) { 18 final AttachInfo attachInfo = mAttachInfo; 19 if (attachInfo != null) { 20 attachInfo.mHandler.removeCallbacks(action); 21 attachInfo.mViewRootImpl.mChoreographer.removeCallbacks( 22 Choreographer.CALLBACK_ANIMATION, action, null); 23 } else { 24 // Assume that post will succeed later 25 ViewRootImpl.getRunQueue().removeCallbacks(action); 26 } 27 } 28 return true; 29 }
我们可以看到这个方法always返回true,所以不要基于它的返回值做任何事情,还有它的返回值的意义也需要格外留意下。
我在第一次看到这个方法时就自以为然的觉得返回值肯定代表了Runnable action有没有成功地从MessageQueue中移除,true代表成功
移除了,false代表移除失败,呵呵,你想错了。仔细看看方法的doc,人家说的是return true表示这个view可以让它的Handler
去处理这件事情,并没提及处理的结果,而且即使返回true的时候也不能说明Runnable就已经从MessageQueue中移除了,
比如说此时Runnable已经不在MessageQueue中了;其他情况都是返回false。这里顺便看眼View.postDelayed方法:
1 public boolean postDelayed(Runnable action, long delayMillis) { 2 final AttachInfo attachInfo = mAttachInfo; 3 if (attachInfo != null) { 4 return attachInfo.mHandler.postDelayed(action, delayMillis); 5 } 6 // Assume that post will succeed later 7 ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); 8 return true; 9 }
这里不论是postDelayed还是removeCallbacks方法都首先检查了自己的mAttachInfo,如果非空才delegate给attachInfo的Handler处理,所以
你尽量不要过早(mAttachInfo还没初始化完毕)的调用这些方法,在早些版本的Android中runnable不会被执行,参考这个问题:
http://stackoverflow.com/questions/4083787/runnable-is-posted-successfully-but-not-run
我现在分析的是Android4.4的源码,具体是怎样,我现在家里还没法验证,等周一到公司了验证下,然后在update下,当然感兴趣的同学可以在google中搜索view post runnable not run或者自行验证。不过依现在的代码来看即使mAttachInfo是null,也会执行ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); 所以可能在较新的平台上不是问题(有待考证)。目前我还没来得及分析它的源码(之后会写的)。
好了说了一大堆了,开始正题。作为开始我今天挑了一个最简单的开始分析,嘿嘿,那就是Message.java文件。
其实说白了,Message就是一个数据类,持有data的。基本的数据字段我就不介绍了,都能望文生义。看下几个我觉得有必要的字段:
1 /*package*/ Handler target; 2 3 /*package*/ Runnable callback; 4 5 // sometimes we store linked lists of these things 6 /*package*/ Message next; 7 8 private static final Object sPoolSync = new Object(); 9 private static Message sPool; 10 private static int sPoolSize = 0; 11 12 private static final int MAX_POOL_SIZE = 50;
target是消息的处理者,在以后Looper.loop()方法中Message被从MessageQueue取出来后会调用msg.target.dispatchMessage(msg);
callback是消息要执行的动作action。这里提前插播下Handler的dispatchMessage方法:
1 public void dispatchMessage(Message msg) { 2 if (msg.callback != null) { 3 handleCallback(msg); 4 } else { 5 if (mCallback != null) { 6 if (mCallback.handleMessage(msg)) { 7 return; 8 } 9 } 10 handleMessage(msg); 11 } 12 }
我们可以看到Handler在分发消息的时候,Message自身的callback优先级高,先被调用如果非空的话(callback的run方法直接被调用)。
next表示消息队列中的下一个Message,类似单链表的概念。
剩下的pool相关的字段都是Message引入的重用(reuse)所要用到的变量,sPoolSync是对象锁,因为Message.obtain方法会在任意
线程调用;sPool代表接下来要被重用的Message对象;sPoolSize表示有多少个可以被重用的对象;MAX_POOL_SIZE显然是pool的上限,
这里hardcode是50。这里我要分析的就2个方法,
obtain和recycle,代码如下:
1 /** 2 * Return a new Message instance from the global pool. Allows us to 3 * avoid allocating new objects in many cases. 4 */ 5 public static Message obtain() { 6 synchronized (sPoolSync) { 7 if (sPool != null) { 8 Message m = sPool; 9 sPool = m.next; 10 m.next = null; 11 sPoolSize--; 12 return m; 13 } 14 } 15 return new Message(); 16 } 17 18 /** 19 * Return a Message instance to the global pool. You MUST NOT touch 20 * the Message after calling this function -- it has effectively been 21 * freed. 22 */ 23 public void recycle() { 24 clearForRecycle(); 25 26 synchronized (sPoolSync) { 27 if (sPoolSize < MAX_POOL_SIZE) { 28 next = sPool; 29 sPool = this; 30 sPoolSize++; 31 } 32 } 33 }
首先我们来看obtain方法,第一次调用也就是说没有什么东西可以重用,这时sPool是null,直接new一个Message对象返回,等到Message对象使用完毕(
在Looper.loop方法最后有msg.recycle();这样的代码),它的recycle会被调用,在recycle里首先会调用clearForRecycle方法,它只是把各个字段置空或清零。
接下来sPoolSize没到上限,next保存下sPool的旧值(也就是在当前Message回收利用之前上一个要被回收利用的对象),然后sPool被更新成新值,即当前Message,sPoolSize加1,表示又多了一个可以重用的Message对象。之后在等到obtain被调用的时候就不是直接return一个new Message了,因为我们已经有可以重用的Message对象了。将sPool的值设置给我们要返回的Message m对象,接着sPool被更新成上一个要被重用的Message对象(相比recycle是反向过程),最后设置m的next字段为空(m.next会在重新入队列的时候被设置成合适的值),相应的sPoolSize减1,表示可重用的对象少了一个,最后返回重用的对象m。
基于有这么个回收再利用的机制,Android建议我们调用Message的obtain方法来获得一个Message对象,而不是调用ctor,因为很可能会省掉分配一个新对象的开销。
Message类的分析就到这了,以后会陆续分析下常见于Android开发中的类。。。(由于本人水平有限,欢迎批评指正)
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。