您的位置:首页 > 移动开发 > Android开发

Android消息机制源码解析(三)——消息循环器Looper

2015-12-14 20:23 459 查看
本节来分析Looper,Looper可以理解为一个消息循环器,不断从消息队列中取出消息,然后交给Handler处理。如果一个线程中有多个Handler,则会根据Message的target属性来将消息处理任务分发到特定的Handler。如果消息队列中没有消息了,那么Looper就会等待,直到有消息进来,下面看一下Looper的源码实现。

一、Looper类的定义如下

public final class Looper
可以看到Looper类被定义为final类型,则此类不可以被继承。

Looper主要属性字段:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
这里涉及到一个叫ThreadLocal的东西,ThreadLocal的作用简单说就是可以在多个线程中被共享,但是在ThreadLocal中却可以保存各个线程的值,在不同线程中访问时,返回的也是不同线程保存的值,其实现源码也比较简单,有兴趣的同学可以自己看一下。ThreadLocal很有用,在Looper中,sThreadLocal用来保存每个线程的Looper对象,同时也可以从中取出各个线程的Looper。sMainLooper表示主线程的Looper对象;mQueue表示消息队列;mThread表示当前线程。

二、接下来分析如何初始化一个Looper,答案是通过prepare()方法:

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));
}
注意上述代码中的异常提示“Only one Looper may be created per thread”,它告诉我们,每个线程只能有一个Looper,通过sThreadLocal的set()和get()可以设置与获取到当前线程的Looper。再看下Looper的构造函数:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
其中布尔型的参数表示该Looper是否可以退出,此值会传到MessageQueue中,后面一节会讲到。上述代码实际上给Looper绑定了一个MessageQueue和Thread,既然一个线程只有一个Looper,也就只有一个MessageQueue。

三、之前提到在主线程创建Handler时不需要手动创建Looper,那么主线程的Looper又是如何被创建的呢?先看如下的方法:

/**
 * 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();
    }
}

/** Returns the application's main looper, which lives in the main thread of the application.
 */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}
prepareMainLooper()就是为主线程创建Looper,此函数就是在主线程ActivityThread中被调用的;getMainLooper()则可以返回主线程的Looper对象。

四、Looper还提供了一些其他方法:

public static Looper myLooper() {
    return sThreadLocal.get();
}
上述方法返回当前线程的Looper。

public static MessageQueue myQueue() {
    return myLooper().mQueue;
}
上述方法返回当前线程的Looper对应的消息队列。

public boolean isCurrentThread() {
    return Thread.currentThread() == mThread;
}
上述方法用来判断当前线程是否为Looper所在的线程。

public boolean isIdling() {
    return mQueue.isIdling();
}
上述方法用来判断此Looper是否处于闲置状态,其本质是判断MessageQueue是否处于闲置状态。

五、通过以上源码分析,大家已经了解如何创建一个Looper了,那么Looper创建好之后,是怎么运转的呢,它是怎样从消息队列不断获取消息的呢?下面请出Looper最重要的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
        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.recycleUnchecked();
    }
}
首先拿到当前线程的Looper,再得到该Looper对应的MessageQueue;接下来Binder相关的两句是关于进程间通信的,不影响我们理解。最重要的逻辑在for循环里,这是一个无限循环,首先从消息队列取消息:

Message msg = queue.next(); // might block
注意后边的注释语句,next()的具体实现会在下一节MessageQueue中介绍,这个方法可能会阻塞,理解这一点非常重要,阻塞说明即会等待,当有消息时就会被唤醒,而等待时并不会消耗太多资源,所以并不会造成卡死。接下里的if语句说明跳出循环的唯一条件是msg为null,接下来会提到的Looper的quit()方法,其实质就是调用MessageQueue的quit(),让next()返回null,这样Looper就会跳出循环了。如果Looper从消息队列中获取的消息不为null,则分发给此消息对应的Handler处理:

msg.target.dispatchMessage(msg);
Handler的dispatchMessage(Message msg)就是在这个地方调用的。最后一行代码是Message对象的回收。需要提醒一下,创建Looper之后,必须调用其loop(),才能将其启动起来。

六、最后是Looper的退出方法,有两个:

public void quit() {
    mQueue.quit(false);
}
public void quitSafely() {
    mQueue.quit(true);
}
可以看到都是调用了MessageQueue的退出方法,它们的区别时,直接退出则loop()不再处理任何消息马上终止,而安全退出时loop()会执行完目前已经在处理的消息,之后才终止。

Looper的主要源码已经分析完了,需要明白的是一个线程只有一个Looper、一个MessageQueue,但可以有多个Handler。最后一节《Android消息机制源码解析——消息队列MessageQueue》将分析Android消息机制的最后一部分内容。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息