Android消息机制(Handler,Looper,MessageQueue)-源码分析
2017-02-23 15:48
369 查看
Android-消息机制(Handler,Looper,MessageQueue)源码分析
说到消息机制,Android开发者应该都不陌生。在Android开发过程中,需要使用消息传递的场景到处可见,如在子线程更新UI之类常见的操作都离不开消息传递。本篇文章主要从Handler 、Looper以及MessageQueue的运行机制及其源码方面对Android的消息机制进行讲解。0
Android为了UI组件的访问效率考虑,将UI组件实现为非线程安全的,因此Android不允许在非UI线程中操纵UI组件。但是,当我们需要从网络拉取数据、执行IO操作或者执行一些耗时运算的时候,我们必须将这些任务放到子线程来完成,然而在子线程完成任务后无法操纵UI来与用户交互,给开发者带来了很多麻烦。因此,Android提供了消息机制,通过消息机制我们能很方便的切换任务的执行环境,也即很好的解决了这个问题。下面是Handler、Looper以及MessageQueue三者之间的关系。在消息传递过程中,首先Handler将消息放到MessageQueue中,然后Looper在死循环中依次从MessageQueue读取消息,并在Looper所在的线程环境中执行相应任务。
Handler
Handler是Android消息机制的上层接口,通过Handler我们能将Message发送给Looper去处理,也即将指定的任务切换到指定的线程环境中去执行。首先看一下Handler的两个主要构造方法:
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; } public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
Handler的两个主要构造方法中一个不需要Looper对象(使用线程默认Looper,下面会详解),另一个则需要一个Looper对象,而Handler发送的消息就在该Looper中进行处理。
因此,在一个Handler创建的时候,会获取与之关联的Looper,以及该Looper内的MessageQueue。
Handler的post方法(post发送对Runnable对象会被包装到Message的callback)以及send方法最终都是通过调用sendMessageAtTime(Message msg, long uptimeMills)方法实现的,该方法源码如下:
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); }
可以看到,该方法只是简单的调用enqueueMessage方法,该方法如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
而enqueueMessage方法里又简单的调用了MessageQueue的enqueueMessage方法,该方法将Message插队到MessageQueue的队列里(其实是单链表实现),下面会详细分析。
再看一下Handler的dispatchMessage方法,当Looper处理Message时,会将Message发送给Handler的该方法来处理:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
该方法的处理流程为:首先看该Message是不是一个Runnable(也即通过post方法发送的,Runnable被包装到callback),如果是,则在handlerCallback方法中执行该Runnable;如果不是则看该Handler的Callback是否被设置,设置了callback的话就交给callback处理;如果callback没有成功处理,也即返回了false,或者没有设置callback,那么该Message交给Handler的handlerMessage方法(也即一般在我们创建Handler时覆盖的方法)处理。
由上可见,Handler的主要工作就是发送Message到MessageQueue,那么,消息又是怎么被Looper处理的呢?
Looper
我们知道,UI线程在初始化的时候,会创建一个Looper,该Looper负责处理外部事件,因此,我们在主线程中使用Handler时不用在创建Looper。但是,在子线程中,我们必须先使用Looper.prepare()来创建一个Looper,然后调用Looper.loop()来开启消息循环,之后才能正常的使用Handler传递消息。Looper的构造方法是私有的,整个Looper类只有三百行,下面主要看一下prepare、myLooper以及loop方法: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)); }
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
首先,看Looper的prepare方法,该方法会创建一个Looper(创建Looper的时候会创建MessageQueue),并将其放入到ThreadLocal中(一种线程内数据的存储对象,后面会详解)。如果你在主线程调用Looper.prepare()将会得到RuntimeException:“Only one Looper may be created per thread”。
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
myLooper方法会返回当前线程的Looper对象,该对象在调用prepare方法时被放在ThreadLocal中,使用Handler类中无Looper的构造方法创建Handler时会使用此方法获取线程内的Looper。
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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } 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.recycleUnchecked(); } }
loop方法的代码比较长,但不难看出,loop方法中开启了一个死循环,在循环中依次读取Message,然后调用
msg.target.dispatchMessage(msg)方法,msg.target为发送该Message的Handler,也即调用了Handler的dispatchMessage方法。在介绍Handler时已经说过Handler处理Message的流程, 此处不在赘述。
值得一提的是,loop中的死循环跳出的唯一条件是queue.next()返回null,而queue.next()返回null表示该MessageQueue正在退出,也即该Looper正在退出。此外,如果MessageQueue中没有消息,则它的next方法会产生阻塞,直到有消息可读。
通过这样,不管Handler在哪个线程环境,Handler都能将Message发送给关联的Looper,存储到Looper中的MessageQueue中, 然后Looper在自己被创建的线程环境中,通过死循环方法loop来从MessageQueue中读取消息,然后使用Handler规定的处理方式来处理消息。
到这,大家应该理解了Handler的工作机制了。但是,在Handler与Looper之间,MessageQueue发挥着重要的作用,下面再看一下Message Queue。
MessageQueue
MessageQueue是Handler发送的消息的目的地,是Looper读取消息的来源。MessageQueue中的Message以单链表结构组织在一块(Message对象本身具有next数据域,正是通过此数据域来组织成单链表)。下面主要看一下enqueueMessage方法以及next方法。enqueueMessage方法将一个Message插入到单链表中,next方法返回并删除一个Message。boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
首先看一下enqueueMessage方法, 该方法比较长,但逻辑很简单。首先检查Message的完整性,然后判断MessageQueue是否正在退出,是的话就抛出异常。没问题的话,就将该Message插入到单链表中(按照when的顺序)。
然后看一下next方法:
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = m c2e5 IdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
该方法也比较长,但是主要逻辑很清楚,首先,使用一个native方法nativePollOnce来阻塞获取消息,然后判断消息是否是ready状态,也即
now < msg.when是否成立,然后返回这个Message,或者执行IdlerHandler(如果有的话,可以通过MessageQueue的addIdleHandler方法添加)。
通过上面MessageQueue的两个主要方法,我们知道了MessageQueue的工作机制(核心工作是在native方法中完成,此处不再分析),也即通过enqueueMessage将Message插入到链表中,然后使用next方法依次从链表中读取并删除消息,最后消息交给Looper来处理。
ThreadLocal
分析完Handler、Looper以及MessageQueue的工作机制,大家应该对Android的消息传递有了一些了解,但是不知道大家有没有注意到一个问题,那就是,当我们使用Handler的没有Looper参数的那个构造方法构造对象时,Handler与哪个Looper关联呢?对,是与当前线程的Looper对象关联, 也即通过Looper.myLooper()来获取到的当前线程的Looper对象。那么问题来了,myLooper是一个静态方法,也就是说在不同的线程环境里调用myLooper方法,myLooper方法内操纵的都是同一个(或同多个)静态数据域,那么是怎么做到在不同的线程环境里调用myLooper方法返回线程相关的Looper对象呢?答案是ThreadLocal。
首先看一下在Looper中出现过的get和set 方法(在Looper的prepare方法中会使用set方法,在myLooper中会使用get方法)。
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
这是set方法,首先获取当前的线程对象啊,然后使用getMap方法获取当前线程对象中的ThreadLocalMap对象,该对象是Thread对象的局部变量,也即任何一个线程中都会有ThreadLocalMap。然后使用ThreadLocalMap的set方法来存储value(下面详解)。
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }
然后再看一下get方法,和set方法类似,get 方法也先取得当前线程对象的ThreadLocalMap对象,然后使用map.getEntry方法来获取一个Entry,Entry的Value属性即为最终结果。
现在,已经可以很清晰的知道get、set方法是如何工作了:首先获取当前线程的ThreadLocalMap,然后通过该对象存取数据。ThreadLocalMap是ThreadLocal的内部类,现在看一下ThreadLocalMap的set方法以及getEntry方法:
private void set(ThreadLocal key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
Entry[]为ThreadLocalMap中存储数据的数组,Entry存储了ThreadLocal的弱引用以及与该ThreadLocal相对应的Object。set函数通过hash映射将该key对应的value放到Entry数组中。
private Entry getEntry(ThreadLocal key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); }
private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { ThreadLocal k = e.get(); if (k == key) return e; if (k == null) expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; }
getEntry函数同样通过hash映射来取得相应的Entry,遇到冲突则使用getEntryAfterMiss函数顺次查找。查找不到指定数据则返回null。
总的来说,ThreadLocal其实是将自身作为key,存储的对象作为value,然后将这个key-value(也即Entry)放到ThreadLocalMap中,而ThreadLocalMap内部通过对key进行hash映射来将value放到数组的指定位置。这样,由于每个Thread中都有一个自己的ThreadLocalMap,而ThreadLocal做为存储的key,因此同一个ThreadLocal对象在不同的Thread中能关联不同的value。
相关文章推荐
- Handler,Looper,MessageQueue,android中的消息机制以及源码分析(二)
- android消息处理机制学习(三)-Handler,Message,MessageQueue,Looper源码分析
- Android Handler消息机制源码分析——第一部分:Looper与MessageQueue
- Handler,Looper,MessageQueue,android中的消息机制以及源码分析(一)
- android的消息处理机制(图+源码分析)——Thread,Looper,MessageQueue,Message,Handler之间的关系
- 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