android的消息处理机制(图+源码分析)——Thread,Looper,MessageQueue,Message,Handler之间的关系
2014-02-28 20:17
676 查看
本文主要参考:http://www.cnblogs.com/codingmyworld/archive/2011/09/12/2174255.html
先贴一张,我理解的它们五者的关系图:
当这个Looper线程启动会依次会prepare()和looper这两个方法。
我们会以这两个方法为主线,浏览源码逐步分析他们之间的关系。
先看prepare().
听起来有一点绕是吧,为什么不直接保存一下Looper对象呢?而ThreadLocal<Looper>这个类又是做什么的?Looper与Thread又是什么关系?
看来要把这几个问题说清楚,还是要从sThreadLocal.set(new Looper(quitAllowed)); 这个方法说起啊。
从上面的代码分析可以知道sThreadLocal.set(new Looper(quitAllowed)); 实质是将Thread类的localValues的成员变量设置一个新的Looper,所以也就是说一个线程只能有一个Looper。
这里有五个关键的步骤
(1)final Looper me = myLooper(); 根据当前的线程获取Looper对象。
(2)final MessageQueue queue = me.mQueue;根据当前的Looper获取它MessageQueue类型的成员变量
(3)Message msg = queue.next();出队例。
(4)msg.target.dispatchMessage(msg); 将msg传递给目标Handler,再出所在的Handler进行处理。
(5)msg.recycle(); 消息回收
再来仔细看一下Handler是怎样处理消息的?
(1) 先看Message的Runnable类型的callback成员变量是否为空,如果不为空。 则直接调用callback的run方法。
请注意这里调用的是run方法哦,而不是我们经常用的start方法。所有说这里虽然是使用了Runnable,但是我们没有把它作为另外一个线程来处理。run方法里的执行仍然和Handler处在同一个线程。
(2)先是尝试通过Callback回调的方法处理消息。如果处理成功(即Callback的handleMessage方法返回true),则直接返回。如果处理失败可在由重写Handler的handleMessage方法来处理(亲,虽然Callback和Handler处理消息的方法都叫handleMessage,但是Handler里那个没有返回布尔值哦)。
以上讲的是消息MessageQueue出取出,在由Handler处理的过程。
(1)先是obtain一下
通过以上两步,消息就成功的进入到对应的MessageQueue里啦。
可以看到通过以上三个方法,Message就进入到了MessageQueue里了。
哦,所以我们现在重点就是MessageQueue从哪里来的?
我们在Handler的构造方法找到了如下代码:
(1)根据Handler得到当前运行的Thread线程
(2)通过Thread里面的localValues成员变量找到线程里面的Looper
(3)再拿到Looper里面MessqgeQueue的引用就可以了。
前言说明
先看一段非常经典的代码,我们从会这里入手,慢慢分析源码去找这五个类的相互关系,以及消息封装,消息入队,消息出队,消息处理整个过程。public class LooperThread extends Thread { private Handler handler1; private Handler handler2; @Override public void run() { // 将当前线程初始化为Looper线程 Looper.prepare(); // 实例化两个handler handler1 = new Handler(); handler2 = new Handler(); // 开始循环处理消息队列 Looper.loop(); } }
先贴一张,我理解的它们五者的关系图:
当这个Looper线程启动会依次会prepare()和looper这两个方法。
我们会以这两个方法为主线,浏览源码逐步分析他们之间的关系。
先看prepare().
private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); /** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ public static void prepare() { prepare(true); } 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)); }从上面代码可以看到,执行prepare方法的实质,就是将Looper的成员变量sThreadLocal设置一个新的Looper对像。
听起来有一点绕是吧,为什么不直接保存一下Looper对象呢?而ThreadLocal<Looper>这个类又是做什么的?Looper与Thread又是什么关系?
看来要把这几个问题说清楚,还是要从sThreadLocal.set(new Looper(quitAllowed)); 这个方法说起啊。
/** * Sets the value of this variable for the current thread. If set to * {@code null}, the value will be set to null and the underlying entry will * still be present. * * @param value the new value of the variable for the caller thread. */ public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); } /** * Gets Values instance for this thread and variable type. */ Values values(Thread current) { return current.localValues; } /** * Creates Values instance for this thread and variable type. */ Values initializeValues(Thread current) { return current.localValues = new Values(); } /** * Sets entry for given ThreadLocal to given value, creating an * entry if necessary. */ void put(ThreadLocal<?> key, Object value) { cleanUp(); // Keep track of first tombstone. That's where we want to go back // and add an entry if necessary. int firstTombstone = -1; for (int index = key.hash & mask;; index = next(index)) { Object k = table[index]; if (k == key.reference) { // Replace existing entry. table[index + 1] = value; return; } if (k == null) { if (firstTombstone == -1) { // Fill in null slot. table[index] = key.reference; table[index + 1] = value; size++; return; } // Go back and replace first tombstone. table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++; return; } // Remember first tombstone. if (firstTombstone == -1 && k == TOMBSTONE) { firstTombstone = index; } } }
从上面的代码分析可以知道sThreadLocal.set(new Looper(quitAllowed)); 实质是将Thread类的localValues的成员变量设置一个新的Looper,所以也就是说一个线程只能有一个Looper。
消息出队和消息处理
再来看一下Looper的looper()方法:/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } 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); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); }
这里有五个关键的步骤
(1)final Looper me = myLooper(); 根据当前的线程获取Looper对象。
(2)final MessageQueue queue = me.mQueue;根据当前的Looper获取它MessageQueue类型的成员变量
(3)Message msg = queue.next();出队例。
(4)msg.target.dispatchMessage(msg); 将msg传递给目标Handler,再出所在的Handler进行处理。
(5)msg.recycle(); 消息回收
再来仔细看一下Handler是怎样处理消息的?
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } private static void handleCallback(Message message) { message.callback.run(); } /** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ public interface Callback { public boolean handleMessage(Message msg); } /** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }看来在Handler眼中,消息有两种处理方式。
(1) 先看Message的Runnable类型的callback成员变量是否为空,如果不为空。 则直接调用callback的run方法。
请注意这里调用的是run方法哦,而不是我们经常用的start方法。所有说这里虽然是使用了Runnable,但是我们没有把它作为另外一个线程来处理。run方法里的执行仍然和Handler处在同一个线程。
(2)先是尝试通过Callback回调的方法处理消息。如果处理成功(即Callback的handleMessage方法返回true),则直接返回。如果处理失败可在由重写Handler的handleMessage方法来处理(亲,虽然Callback和Handler处理消息的方法都叫handleMessage,但是Handler里那个没有返回布尔值哦)。
以上讲的是消息MessageQueue出取出,在由Handler处理的过程。
消息封装和消息入队
下面要说的是怎样把消息放入MessageQueue里。我们经常会这样写:(1)先是obtain一下
/** * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned. * @param h Handler to assign to the returned Message object's <em>target</em> member. * @return A Message object from the global pool. */ public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m;(2)再sendToTarget一下
/** * Sends this Message to the Handler specified by {@link #getTarget}. * Throws a null pointer exception if this field has not been set. */ public void sendToTarget() { target.sendMessage(this); }
通过以上两步,消息就成功的进入到对应的MessageQueue里啦。
/** * Pushes a message onto the end of the message queue after all pending messages * before the current time. It will be received in {@link #handleMessage}, * in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }/** * Enqueue a message into the message queue after all pending messages * before (current time + delayMillis). You will receive it in * {@link #handleMessage}, in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }/** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); }
可以看到通过以上三个方法,Message就进入到了MessageQueue里了。
哦,所以我们现在重点就是MessageQueue从哪里来的?
我们在Handler的构造方法找到了如下代码:
/** * Use the {@link Looper} for the current thread with the specified callback interface * and set whether the handler should be asynchronous. * * Handlers are synchronous by default unless this constructor is used to make * one that is strictly asynchronous. * * Asynchronous messages represent interrupts or events that do not require global ordering * with represent to synchronous messages. Asynchronous messages are not subject to * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. * * @param callback The callback interface in which to handle messages, or null. * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. * * @hide */ public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); 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; }所以mQueue是这样得到:
(1)根据Handler得到当前运行的Thread线程
(2)通过Thread里面的localValues成员变量找到线程里面的Looper
(3)再拿到Looper里面MessqgeQueue的引用就可以了。
相关文章推荐
- Android中的Looper , Handler , Message的关系,异步消息处理的机制,根据源码分析
- android消息处理机制学习(三)-Handler,Message,MessageQueue,Looper源码分析
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android消息处理机制——Looper、Handler、Message 源码分析
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message
- Android的消息处理机制(图+源码分析)——Looper,Handler,Message
- android的消息处理机制(图+源码分析)——Looper,Handler,Message