在Android系统中,主线程用于处理Window及各种窗口上的输入事件,当然应用程序中的Activity、Broadcast Receiver等也是由主线程来处理。除了主线程之外,往往还有单独运行的线程,通常由开发者自己创建,用来做一些额外的工作,尤其是那种耗时的工作,这就需要Thread的子类来实现。
接口java.lang.Runnable代表着一小段执行的任务,由一个线程去调用执行。Runnable接口只有一个run(),子类将需要完成的任务放在该函数中。问题来了,通常在java.lang.Thread线程中没有消息循环,也就是说,在run函数中,通常是执行完处理之后,意味着流程结束,也就是线程结束。如果需要让线程长期存在去处理某种任务,需要自己写个while(){}循环,在循环中去执行需要完成的任务。在执行的过程中,通常会因为条件不满足而阻塞,条件满足后被唤醒执行。显然这整个过程需要开发者去判断,稍有不慎,将造成严重的后果。
1.简要介绍
Android提供了一种消息处理的机制,调用者将需要处理的任务发送到消息队列后返回,线程不断的从消息队列中读取消息,进行处理。这种消息驱动的方式,让系统中的任务处理变得灵活。因此,不难想象,这整个过程如下图所示,不断的向消息队列发送消息,从消息队列中取消息,并发送来执行。
因此需要如下几个要素:
Ÿ 消息的表示:Message
Ÿ 消息队列:MessageQueue
Ÿ 消息循环:Looper
Ÿ 消息处理:Handler
在介绍Android的消息处理机制之前,我们首先通过一个简单的例子,了解如果一个线程想要实现消息循环应该怎么做,如下是源码中给我们提供的一个例子,如此该线程便成为了一个Looper线程,不断的从消息队列中取消息,并调用mHandler的handleMessage处理消息。
class LooperThread extends Thread { public Handler mHandler;
public void run() { Looper.prepare();
mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } };
Looper.loop();//开始循环,从消息队列中取消息,利用Handler去处理 } }
LooperThread.mHandler.sendMessage(msg);//消息发送 |
大家现在肯定会有如下疑惑,带着如下问题,我们去看Looper、Handler的源码。
1) MessageQueue在哪儿?
2) LooperThread是如何从MessageQueue取消息的?
3) 取到消息之后,是如何发送给mHandler的?
4) 他们之间的对应关系又是怎么样的?
2.Looper
在java层 native层均有Looper的实现,但其作用不一样。
2.1 Java层的Looper
首先来看Java层,其主要作用是将一个线程初始化为一个Looper线程,从MessageQueue中取消息发送给Handler执行。
/** 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[1]) { if (sThreadLocal.get() != null[2]) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
private Looper(boolean quitAllowed) { mQueue[3] = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } |
代码很简单,但是需要解释三点:
1) quitAllowed:是否允许消息队列退出
2) prepare()会先检查该线程是否已经有了自己的Looper,若有则会抛出异常,即一个线程最多只能有一个Looper。
3) Looper的构造函数中,新建了一个自己的MessageQueue对象,保存在成员mQueue,在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()[1]; // might block if (msg == null[2]) { // 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) Looper.loop()调用MessageQueue.next()从自己的消息队列中取出下一个消息,然后调用Handler的dispatchMessage(msg)将消息进行分发处理。其中next()可能会阻塞,例如消息队列为空,或者消息尚未准备好执行等等。
2) 若调用MessageQueue.next()返回空,不意味着消息队列为空,而是意味着此时调用了Looper.quit()。因此,当希望线程退出消息循环时,需要调用Looper.quit()函数。
/**********Looper.java***********/ public void quit() { mQueue.quit(false); } /***********MessageQueue.java************/ void quit(boolean safe) { …… mQuitting = true; …… } Message next() { …… // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } …… } |
Looper.java还有两个重要的函数,用来实现消息同步的,先不赘述,在MessageQueue中详细介绍
public int postSyncBarrier() { return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis()); } public void removeSyncBarrier(int token) { mQueue.removeSyncBarrier(token); } |