Android 源码分析——Looper,Messagequeue,Message,handler 初始化及 handler 机制简介

来源:互联网 发布:js回调函数是闭包吗 编辑:程序博客网 时间:2024/06/02 02:39

Handler机制算是我入门源码的第一节。看得比较仔细。体会较多。mark一下。

顺序:先科普一下Handler基本功,然后再细讲下源码

一、Handler目的:

目的:Handler机制来处理了子线程去更新UI线程控件问题。

二、handler,messagequeue,looper,message关系图:

其实各种书籍上都有这么一张图。但是主要是学习源码,所以还是自己手画一张“流程图”。


三、handler知识点总结:

(若以下总结都能理解,那么可以不再看本文后续源码分析;)

1)handler、Looper、Messagequeue初始化总结:在主UI实例化Looper或者子线程手工实例化looper。在Looper中实例化Messagequeue。在handler初始化在主线程或者子线程中,且handler初始化只获取looper对象,获取Messagequeue对象,因此必须先有Looper实例再有handler实例,否则handler无法获取looper、Messagequeue。

2)hanlder中写了操作主UI线程的handlemessage空方法、使用handler实例在子线程中发送消息Message

3)Looper初始化:一个线程只有一个Looper,Looper管理Messagequeue

4)主UI线程(ActivityThread)创建Looper对象,而Looper创建了Messagequeue对象(可通过其他方式创建looper)

5)在handler里使用queue(Messagequeue对象),equeueMessage插入消息到Messagequeue,在Looper的loop()方法里调用handler对象的dispatchMessage()分发消息。并通过handler的handlerMessage()方法使主UI改变。

6)Messagequeue对象和Message对象

四、逐步分析handler源码

1)handler、Looper、Messagequeue初始化:

由于初始化顺序必然是Looper ==> Messagequeue ==> handler。所以我也按照此顺序分析源码。

四、1)Looper获取:

当我启动一个程序。按照如下顺序:

ActivityManager的startActivity()==>ActivityThread的main()==》Looper.prepareMainLooper()实例化主UI线程Looper

以上实例源码,部分如下:

ActivityManager的startActivity()

[java] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.          * Start an activity in this task.  Brings the task to the foreground.  If this task 
  3.          * is not currently active (that is, its id < 0), then a new activity for the given 
  4.          * Intent will be launched as the root of the task and the task brought to the 
  5.          * foreground.  Otherwise, if this task is currently active and the Intent does not specify 
  6.          * an activity to launch in a new task, then a new activity for the given Intent will 
  7.          * be launched on top of the task and the task brought to the foreground.  If this 
  8.          * task is currently active and the Intent specifies {@link Intent#FLAG_ACTIVITY_NEW_TASK} 
  9.          * or would otherwise be launched in to a new task, then the activity not launched but 
  10.          * this task be brought to the foreground and a new intent delivered to the top 
  11.          * activity if appropriate. 
  12.          * 
  13.          * <p>In other words, you generally want to use an Intent here that does not specify 
  14.          * {@link Intent#FLAG_ACTIVITY_NEW_TASK} or {@link Intent#FLAG_ACTIVITY_NEW_DOCUMENT}, 
  15.          * and let the system do the right thing.</p> 
  16.          * 
  17.          * @param intent The Intent describing the new activity to be launched on the task. 
  18.          * @param options Optional launch options. 
  19.          * 
  20.          * @see Activity#startActivity(android.content.Intent, android.os.Bundle) 
  21.          */  
  22.         public void startActivity(Context context, Intent intent, Bundle options) {  
  23.             ActivityThread thread = ActivityThread.currentActivityThread();  
  24.             thread.getInstrumentation().execStartActivityFromAppTask(context,  
  25.                     thread.getApplicationThread(), mAppTaskImpl, intent, options);  
  26.         }  
此处ActivityThread.currentActivityThread();

[java] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. public static ActivityThread currentActivityThread() {  
  2.         return sCurrentActivityThread;  
  3.     }  
一个单例模式的ActivityThread,简单看下定义。
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. /** Reference to singleton {@link ActivityThread} */  
  2.     private static ActivityThread sCurrentActivityThread;  

ActivityThread的main()

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. public static void main(String[] args) {  
  2.         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");  
  3.         SamplingProfilerIntegration.start();  
  4.   
  5.         // CloseGuard defaults to true and can be quite spammy.  We  
  6.         // disable it here, but selectively enable it later (via  
  7.         // StrictMode) on debug builds, but using DropBox, not logs.  
  8.         CloseGuard.setEnabled(false);  
  9.   
  10.         Environment.initForCurrentUser();  
  11.   
  12.         // Set the reporter for event logging in libcore  
  13.         EventLogger.setReporter(new EventLoggingReporter());  
  14.   
  15.         AndroidKeyStoreProvider.install();  
  16.   
  17.         // Make sure TrustedCertificateStore looks in the right place for CA certificates  
  18.         final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());  
  19.         TrustedCertificateStore.setDefaultUserDirectory(configDir);  
  20.   
  21.         Process.setArgV0("<pre-initialized>");  
  22.   
  23.         Looper.prepareMainLooper();  
  24.   
  25.         ActivityThread thread = new ActivityThread();  
  26.         thread.attach(false);  
  27.   
  28.         if (sMainThreadHandler == null) {  
  29.             sMainThreadHandler = thread.getHandler();  
  30.         }  
  31.   
  32.         if (false) {  
  33.             Looper.myLooper().setMessageLogging(new  
  34.                     LogPrinter(Log.DEBUG, "ActivityThread"));  
  35.         }  
  36.   
  37.         // End of event ActivityThreadMain.  
  38.         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  39.         Looper.loop();  
  40.   
  41.         throw new RuntimeException("Main thread loop unexpectedly exited");  
  42.     }  
上面是整个main方法

细心可以发现这么一条主UI线程Looper初始化实例的语句:(这个代码待会儿讲Looper时一起讲,此处知道在这初始化即可)

Looper.prepareMainLooper()

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. Looper.prepareMainLooper();  
当然有实例,还得开启主UI线程Looper的轮询方法。看这句就知道。(这个也一会儿讲Looper讲)

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. Looper.loop();  
讲到这儿我们主线程的Looper已经初始化完事了。接下来讲讲重中之重Looper。

四、2)Messagequeue实例化即Looper源码分析

先不要着急Messagequeue实例化,由于这个Looper是重点,我决定按照传统的办法来看源码。按照如下顺序读源码:
构造方法Looper()==》looper().loop()轮询方法

构造方法Looper()

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. private Looper(boolean quitAllowed) {  
  2.         mQueue = new MessageQueue(quitAllowed);  
  3.         mThread = Thread.currentThread();  
  4.     }  
直接上源码可见是个private的构造方法。可见单例模式痕迹明显。肯定有对外的实例方法。接着看:
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. private static Looper sMainLooper;  // guarded by Looper.class  
实例在类初始化的时候初始化了。只需要获取即可。看看有哪些能获取实例的方法:

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. /**  
  2.      * Initialize the current thread as a looper, marking it as an  
  3.      * application's main looper. The main looper for your application  
  4.      * is created by the Android environment, so you should never need  
  5.      * to call this function yourself.  See also: {@link #prepare()}  
  6.      *///主线程用来实例化Looper的,你应该永远也用不到它。  
  7.     public static void prepareMainLooper() {  
  8.         prepare(false);  
  9.         synchronized (Looper.class) {  
  10.             if (sMainLooper != null) {  
  11.                 throw new IllegalStateException("The main Looper has already been prepared.");  
  12.             }  
  13.             sMainLooper = myLooper();  
  14.         }  
  15.     }  
prepareMainLooper很熟悉吧。对,就是它。主方法里面用的就是它。当然注释里面说了,主线程用来实例化Looper的,你应该永远也用不到它。然后提示你看prepare()

这里第一句代码看看。

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. prepare(false)  
这菊花虽然短,但是千万不要忽略了。它就是那个万恶的深藏着的Looper实例所在。

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. private static void prepare(boolean quitAllowed) {  
  2.         if (sThreadLocal.get() != null) {  
  3.             throw new RuntimeException("Only one Looper may be created per thread");  
  4.         }  
  5.         sThreadLocal.set(new Looper(quitAllowed));  
  6.     }  
如果ThreadLocal.get()不为空,可能你会有疑问sThreadLocal究竟是什么。后面我会认真考虑写关于ThreadLocal的技术博客的。现在只需要立即为一个存储数据的对象,对外公开了set,get方法即可。

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. sThreadLocal.set(new Looper(quitAllowed));  
这里的quitallowed参数明显一直没变,是false会到最开始的私有的初始化函数private looper(boolean quitallowed)。发现其实到这儿我们的Looper实例化已经完事了。quitallowed是给Messagequeue对象实例化传递的一个参数罢了。既然这样子,那就先放放,交给后面的Messagequeue去讲它的实例化方法再提。

绕了一圈,再来谈谈轮询loop()方法。

looper().loop()轮询方法

先把loop()源码奉上。在哪用到就再提一句吧,每次Looper实例化完,必须紧跟着它。因为首先要有这个轮询器(实例化),然后再将轮训器运转起来loop()。

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. /**  
  2.      * Run the message queue in this thread. Be sure to call  
  3.      * {@link #quit()} to end the loop.  
  4.      */  
  5.     public static void loop() {  
  6.         final Looper me = myLooper();  
  7.         if (me == null) {  
  8.             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  9.         }  
  10.         final MessageQueue queue = me.mQueue;  
  11.   
  12.         // Make sure the identity of this thread is that of the local process,  
  13.         // and keep track of what that identity token actually is.  
  14.         Binder.clearCallingIdentity();  
  15.         final long ident = Binder.clearCallingIdentity();  
  16.   
  17.         for (;;) {  
  18.             Message msg = queue.next(); // might block  
  19.             if (msg == null) {  
  20.                 // No message indicates that the message queue is quitting.  
  21.                 return;  
  22.             }  
  23.   
  24.             // This must be in a local variable, in case a UI event sets the logger  
  25.             Printer logging = me.mLogging;  
  26.             if (logging != null) {  
  27.                 logging.println(">>> >> Dispatching to " + msg.target + " " +  
  28.                         msg.callback + ": " + msg.what);  
  29.             }  
  30.   
  31.             msg.target.dispatchMessage(msg);  
  32.   
  33.             if (logging != null) {  
  34.                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
  35.             }  
  36.   
  37.             // Make sure that during the course of dispatching the  
  38.             // identity of the thread wasn't corrupted.  
  39.             final long newIdent = Binder.clearCallingIdentity();  
  40.             if (ident != newIdent) {  
  41.                 Log.wtf(TAG, "Thread identity changed from 0x"  
  42.                         + Long.toHexString(ident) + " to 0x"  
  43.                         + Long.toHexString(newIdent) + " while dispatching to "  
  44.                         + msg.target.getClass().getName() + " "  
  45.                         + msg.callback + " what=" + msg.what);  
  46.             }  
  47.   
  48.             msg.recycleUnchecked();  
  49.         }  
  50.     }  
现在可以一行行看代码了,先判断看看有没有轮训器?有则继续,没有就抛异常。所以,当你看到下面这个异常的时候,你就知道,你肯定没写轮训器。

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. final Looper me = myLooper();  
  2.         if (me == null) {  
  3.             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  4.         }  
不要问我me是什么,me就是Looper实例。
见myLooper()源码:一目了然。
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. public static @Nullable Looper myLooper() {  
  2.         return sThreadLocal.get();  
  3.     }  

获取Looper实例全部靠它,然后再看看这一行:简单明了,之前Looper已经实例化了Messagequeue,那么现在拿来用。

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. final MessageQueue queue = me.mQueue;  

看看下面这几行。翻译很明确,确保当前binder调用是否为远程调用。调用这个clear方法,在当前进程中。将会清除之前进程的PID和UID,

重置成当前进程UID,PID。好吧,以后写个IPC文章。

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. // Make sure the identity of this thread is that of the local process,  
  2.         // and keep track of what that identity token actually is.  
  3.         Binder.clearCallingIdentity();  
  4.         final long ident = Binder.clearCallingIdentity();  
总算到了轮询的伟大时刻了。一看就知道是个死循环。

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. for (;;) {  
  2.   
  3. }  
里面的内容虽然涉及到Messagequeue这个对象和Message对象。但是其实从源码很好看出来。首先我们简化掉这些打日志的代码,

再看如下:

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. for (;;) {  
  2.             Message msg = queue.next(); // might block  
  3.             if (msg == null) {  
  4.                 // No message indicates that the message queue is quitting.  
  5.                 return;  
  6.             }  
  7.   
  8.             msg.target.dispatchMessage(msg);  
  9.              
  10.             // Make sure that during the course of dispatching the  
  11.             // identity of the thread wasn't corrupted.  
  12.             final long newIdent = Binder.clearCallingIdentity();            
  13.   
  14.             msg.recycleUnchecked();  
  15.         }  

天呐,我对它究竟做了什么!居然只剩下四条了。所以不要害怕,一切源码都是纸老虎。而且其中还有一条是刚刚讲过的Binder重设。

这么一看。居然只剩下三条。但是,我很无耻,我只打算在这个篇章里面讲上面两条。其它都留给messagequeue和message。

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. Message msg = queue.next(); // might block  
一条是从“队列”中取出一条消息。

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. msg.target.dispatchMessage(msg);  
  另一条是,是讲这个消息分发出去。其中msg.target就是指向我们主线程中的handler对象。问我为什么知道的。一会儿我打算讲Message就知道了。

到这,很明显的每次从“队列”中取出一条,然后分发出去。我们轮询就干了这么间完全无技术含量的事。

Messagequeue和Message对象:

先讲Messagequeue吧:我估计我写的挺累,大家看的也挺累。再忍一忍。成功一大半了!最难的都看完了。就剩下一个操作类handler,

两个对象Messagequeue、Message对象。

Messagequeue对象:

先把遗留问题讲讲:

mQuitAllowed:这个参数传递过来做什么?看源码吧。

PS:以下都是Messagequeue的部分源码:

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. // True if the message queue can be quit.  
  2.     private final boolean mQuitAllowed;  
源码注释写的太明白了:就是判断Messagequeue能不能退出的。再深入一点。

[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. void quit(boolean safe) {  
  2.         if (!mQuitAllowed) {  
  3.             throw new IllegalStateException("Main thread not allowed to quit.");  
  4.         }  
  5.   
  6.         synchronized (this) {  
  7.             if (mQuitting) {  
  8.                 return;  
  9.             }  
  10.             mQuitting = true;  
  11.   
  12.             if (safe) {  
  13.                 removeAllFutureMessagesLocked();  
  14.             } else {  
  15.                 removeAllMessagesLocked();  
  16.             }  
  17.   
  18.             // We can assume mPtr != 0 because mQuitting was previously false.  
  19.             nativeWake(mPtr);  
  20.         }  
  21.     }  
Messagequeue自带退出方法。为什么不能退出?因为我们刚刚传递参数一直都是从主UI线程的Looper给的。主线程不允许Messagequeue退出,就是这么任性!既然都看到了,就简单讲讲吧。mQuitting这个标志:
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. If the loop is quitting then it must not be idling.  
简单来说就是一个loop空转的标志。这个参数从哪来的?从Looper类的方法过来的:
PS:如果Looper是自己实例化的,必须跟这Looper.loop(),之前我们已经说过,而且使用完Looper对象后通过Looper.quit()方法退出。
这个可不是我总结的:SDK说的,原文在Looper源码的prepare方法中:
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. /** Initialize the current thread as a looper.  
  2.       * This gives you a chance to create handlers that then reference  
  3.       * this looper, before actually starting the loop. Be sure to call  
  4.       * {@link #loop()} after calling this method, and end it by calling  
  5.       * {@link #quit()}.  
  6.       */  
  7.     public static void prepare() {  
  8.         prepare(true);  
  9.     }  
其实也就是说我们自己新建的Looper对象都是可以退出,且需要退出的。而主线程是不允许的。
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. public void quit() {  
  2.         mQueue.quit(false);  
  3.     }  
还有一个quitSafely()
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. public void quitSafely() {  
  2.         mQueue.quit(true);  
  3.     }  
涉及到一个是否安全退出的问题。Looper安全退出究竟是什么?
回到Messagequeue源码中来:继续看removeAllFutureMessagesLocked及removeAllMessagesLocked方法。
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. private void removeAllFutureMessagesLocked() {  
  2.         final long now = SystemClock.uptimeMillis();  
  3.         Message p = mMessages;  
  4.         if (p != null) {  
  5.             if (p.when > now) {  
  6.                 removeAllMessagesLocked();  
  7.             } else {  
  8.                 Message n;  
  9.                 for (;;) {  
  10.                     n = p.next;  
  11.                     if (n == null) {  
  12.                         return;  
  13.                     }  
  14.                     if (n.when > now) {  
  15.                         break;  
  16.                     }  
  17.                     p = n;  
  18.                 }  
  19.                 p.next = null;  
  20.                 do {  
  21.                     p = n;  
  22.                     n = p.next;  
  23.                     p.recycleUnchecked();  
  24.                 } while (n != null);  
  25.             }  
  26.         }  
  27.     }  
大概看一眼则知道:
那么我就安全退出 方法,具体分析如:
如果p.when>now,即队首的消息执行时间大于当前时间,则表明队首的消息还没分发。等同于删除所有的消息队列的消息:那么就调用removeAllMessageLocked。删除所有。
否则删除消息执行时间大于当前时间的消息。
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. private void removeAllMessagesLocked() {  
  2.         Message p = mMessages;  
  3.         while (p != null) {  
  4.             Message n = p.next;  
  5.             p.recycleUnchecked();  
  6.             p = n;  
  7.         }  
  8.         mMessages = null;  
  9.     }  
 虽然比较麻烦,可还是简单要讲下MessageQueue的next()方法:主要是一些算法用来取得下一条要执行的Message对象。不打算在此处花费大量篇幅写。下一篇,会优先考虑写下这个算法。有兴趣的可以看看。
之前留下的还剩下一个dispatchMessage方法。这个方法在下面的Message中将提到。

Message对象:

从类的定义来看知道是一个序列化的对象,很明显是用来传输数据的。
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. public final class Message implements Parcelable   
关键字 what用得最多的:描述很清楚的写着每个handler都有自己的独立命名空间。不用担心命名冲突问题。
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. /**  
  2.      * User-defined message code so that the recipient can identify   
  3.      * what this message is about. Each {@link Handler} has its own name-space  
  4.      * for message codes, so you do not need to worry about yours conflicting  
  5.      * with other handlers.  
  6.      */  
  7.     public int what;  
看看构造方法:官方不推荐直接调用,推荐使用obtain获取handler 对象。性能问题。具体原因:也就是一个新建对象的开销问题。参考这篇文章:http://blog.csdn.net/h3c4lenovo/article/details/7914902
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).  
  2.     */  
  3.     public Message() {  
  4.     }  
其实讲到这,Message日常用到可能也就是what,这儿补充一点内容。关于message对象的target。
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. /*package*/ Handler target;  
这个target是个handler对象,主要目的是用来指向当前使用的handler。从obtain方法可见:
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. /**  
  2.      * Same as {@link #obtain()}, but copies the values of an existing  
  3.      * message (including its target) into the new one.  
  4.      * @param orig Original message to copy.  
  5.      * @return A Message object from the global pool.  
  6.      */  
  7.     public static Message obtain(Message orig) {  
  8.         Message m = obtain();  
  9.         m.what = orig.what;  
  10.         m.arg1 = orig.arg1;  
  11.         m.arg2 = orig.arg2;  
  12.         m.obj = orig.obj;  
  13.         m.replyTo = orig.replyTo;  
  14.         m.sendingUid = orig.sendingUid;  
  15.         if (orig.data != null) {  
  16.             m.data = new Bundle(orig.data);  
  17.         }  
  18.         m.target = orig.target;  
  19.         m.callback = orig.callback;  
  20.   
  21.         return m;  
  22.     }  
可能看到这儿大家都看到了m.target = orig.trget。

突然间可能有种懵的感觉?忘了orig这个参数?没关系,温习一下。handler实例的时候我们如何用target就知道了。
----------------此处为分割线------------------------------
讲了这么多总算到了handler机制,前面都是铺垫,是不是简直令人发指。
---此处是基础:-------
Handler有个Callback接口,i m p liments接口,实现方法handleMessage,用来处理子线程发的消息的响应。
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. /**  
  2.      * Callback interface you can use when instantiating a Handler to avoid  
  3.      * having to implement your own subclass of Handler.  
  4.      *  
  5.      * @param msg A {@link android.os.Message Message} object  
  6.      * @return True if no further handling is desired  
  7.      */  
  8.     public interface Callback {  
  9.         public boolean handleMessage(Message msg);  
  10.     }  
  11.       
  12.     /**  
  13.      * Subclasses must implement this to receive messages.  
  14.      */  
  15.     public void handleMessage(Message msg) {  
  16.     }  
new一个handler实例。直接上源码:
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. public Handler() {  
  2.         this(null, false);  
  3.     }  
指向多参数的handler构造方法
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. public Handler(Callback callback, boolean async) {  
  2.         if (FIND_POTENTIAL_LEAKS) {  
  3.             final Class<? extends Handler>  klass = getClass();  
  4.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  5.                     (klass.getModifiers() & Modifier.STATIC) == 0) {  
  6.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  7.                     klass.getCanonicalName());  
  8.             }  
  9.         }  
  10.   
  11.         mLooper = Looper.myLooper();  
  12.         if (mLooper == null) {  
  13.             throw new RuntimeException(  
  14.                 "Can't create handler inside thread that has not called Looper.prepare()");  
  15.         }  
  16.         mQueue = mLooper.mQueue;  
  17.         mCallback = callback;  
  18.         mAsynchronous = async;  
  19.     }  
通过Looper.myLooper()获取实例。myLooper()方法我前面讲没讲?自己翻一翻。
通过前面去获得Looper实例获取queue实例。
拿到所有可以用的东西了。至于Message,那是通过handler的方法obtainMessage方法获得的。
准备工作做好。发送消息====》handler.sendMessage()
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. public final boolean sendMessage(Message msg)  
  2.     {  
  3.         return sendMessageDelayed(msg, 0);  
  4.     }  
可见虽然我没有传入参数最终都是要进入===》sendMessageDelayed(Message msg, long delayMillis)
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. public final boolean sendMessageDelayed(Message msg, long delayMillis)  
  2.    {  
  3.        if (delayMillis < 0) {  
  4.            delayMillis = 0;  
  5.        }  
  6.        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  7.    }  
进入===》sendMessageAtTime(Message msg, long uptimeMillis)下面方法为发送消息的唯一方法。所有方法均调用它。
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
  2.         MessageQueue queue = mQueue;  
  3.         if (queue == null) {  
  4.             RuntimeException e = new RuntimeException(  
  5.                     this + " sendMessageAtTime() called with no mQueue");  
  6.             Log.w("Looper", e.getMessage(), e);  
  7.             return false;  
  8.         }  
  9.         return enqueueMessage(queue, msg, uptimeMillis);  
  10.     }  
调用handler自己的插入消息方法。其实这是一个伪方法。真正调用的Messagequeue的该同名方法。
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  2.         msg.target = this;  
  3.         if (mAsynchronous) {  
  4.             msg.setAsynchronous(true);  
  5.         }  
  6.         return queue.enqueueMessage(msg, uptimeMillis);  
  7.     }  
说到这,消息也就发送出去了。
当然光发出去还不行,还得由主UI线程或者当前handler接收回来。
当轮询器Looper实例的Looper.loop方法调用msg.target.dispatchMessage(之前我已经讲过,msg.target指向就是当前handler)来,看下handler源码中的该方法:
[html] view plain copy print在CODE上查看代码片 派生到我的代码片
  1. /**  
  2.      * Handle system messages here.  
  3.      */  
  4.     public void dispatchMessage(Message msg) {  
  5.         if (msg.callback != null) {  
  6.             handleCallback(msg);  
  7.         } else {  
  8.             if (mCallback != null) {  
  9.                 if (mCallback.handleMessage(msg)) {  
  10.                     return;  
  11.                 }  
  12.             }  
  13.             handleMessage(msg);  
  14.         }  
  15.     }  
好了,总算到了handleMessage(msg)方法了。此方法,我们实现接口的时候已然重写。那么只需调用该方法即可。
主UI响应,over。

PS:写到这,感谢大家看完。如有错误,欢迎指正。


原文链接:http://blog.csdn.net/haibo_bear/article/details/52003813

0 0
原创粉丝点击