原创源码角度分析Android的消息机制系列——Looper的工作原理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原创源码角度分析Android的消息机制系列——Looper的工作原理相关的知识,希望对你有一定的参考价值。
ι 版权声明:本文为博主原创文章,未经博主允许不得转载。
Looper在android的消息机制中就是用来进行消息循环的。它会不停地循环,去MessageQueue中查看是否有新消息,如果有消息就立刻处理该消息,否则就一直等待。
Looper中有一个属性:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
这也就解释了,前面我们所说的我们可以通过ThreadLocal实现Looper在线程中的存取。
除此之外,还有两个属性需要注意:
final MessageQueue mQueue; final Thread mThread;
下面我们先看下Looper的构造函数:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
在构造函数中,创建了一个MessageQueue消息队列,并且将当前线程的对象保存了起来。
接下来看loop方法,只有调用了loop方法后,消息循环系统才真正地起到了作用。
/** * 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 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方法中首先调用了myLooper方法:
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
myLooper方法会返回与当前线程相关联的Looper对象。如果当前线程没有关联任何Looper对象的话,该方法则返回null。
查看loop方法的源码,可以知道,当当前线程没有关联任何Looper对象时,loop方法会抛出运行时异常,提示当前线程中没有Looper。若想解决该问题,可以在loop方法被调用前,先执行Looper.prepare()方法,创建一个looper对象。继续看loop方法的源码,可以看到该方法是一个死循环,唯一可以跳出该循环的方法就是queue.next()返回的对象为null。在上面的文章中,我们分析过,queue.next()即读取MessageQueue中的消息,next()方法返回null,说明MessageQueue中没有Message,即该MessgaeQueue调用了quit方法。那么何时MessageQueue会调用quit方法呢?来看下Looper的quit方法:
public void quit() { mQueue.quit(false); }
以及Looper的quitSafely方法:
public void quitSafely() { mQueue.quit(true); }
Looper的quit方法和quitSafely方法都会导致MessageQueue调用quit方法,所以当不需要Looper的时候,建议调用Looper的quit()方法或quitSafely()方法,以避免loop方法无限循环下去。
要想知道Looper的quit方法和quitSafely方法的区别,我们看下MessgaeQueue的quit方法:
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } }
安全退出,则调用removeAllFutureMessagesLocked()方法,该方法会设定一个标记,当消息队列中的已有消息全部处理完毕后才会安全退出;quit则会调用removeAllMessagesLocked(),直接退出。
下面接着看loop方法,重点看这一句:
msg.target.dispatchMessage(msg);
在Android的消息机制概述中,我们已经说过,target是Message的一个属性,其类型为Handler,msg.target也就是发送这条消息的对象。由此一来,Handler发送的Message最终又交给了它自己来调用dispatchMessage方法来处理,但是dispatchMessage方法是在Looper的loop方法中被调用的,那么Looper的loop方法是在哪里执行的呢?在创建Handler时所在的线程中执行的。
ActivityThread(主线程)在创建时,会初始化Looper,所以我们可以在主线程中直接使用Handler,当需要更新UI时,可以通过Handler发送消息,最后就可以回到主线程去更新UI啦,啦啦啦。
除此之外,Looper还提供了一些其他的方法,例如prepareMainLooper方法:
/** * Initialize the current thread as a looper, marking it as an * application‘s main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ 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啦。Android系统会为我们创建主线程的looper,我们也不需要自己手动去调用该方法了。该方法的实质还是通过prepare方法实现的。
再如getMainLooper方法:
/** * Returns the application‘s main looper, which lives in the main thread of the application. */ public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }
该方法使得我们可以在任何地方获取到主线程的Looper了。
以上是关于原创源码角度分析Android的消息机制系列——Looper的工作原理的主要内容,如果未能解决你的问题,请参考以下文章
原创源码角度分析Android的消息机制系列——Handler的工作原理
原创源码角度分析Android的消息机制系列——ThreadLocal的工作原理
原创源码角度分析Android的消息机制系列——MessageQueue的工作原理
原创源码角度分析Android的消息机制系列——ThreadLocal的工作过程