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

本节来分析LooperLooper可以理解为一个消息循环器,不断从消息队列中取出消息,然后交给Handler处理。如果一个线程中有多个Handler,则会根据Messagetarget属性来将消息处理任务分发到特定的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对象,同时也可以从中取出各个线程的LoopersMainLooper表示主线程的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,通过sThreadLocalset()get()可以设置与获取到当前线程的Looper。再看下Looper的构造函数:

private Looper(boolean quitAllowed) {
    mQueue new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

其中布尔型的参数表示该Looper是否可以退出,此值会传到MessageQueue中,后面一节会讲到。上述代码实际上给Looper绑定了一个MessageQueueThread,既然一个线程只有一个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语句说明跳出循环的唯一条件是msgnull,接下来会提到的Looperquit()方法,其实质就是调用MessageQueuequit(),让next()返回null,这样Looper就会跳出循环了。如果Looper从消息队列中获取的消息不为null,则分发给此消息对应的Handler处理:

msg.target.dispatchMessage(msg);

HandlerdispatchMessage(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消息机制的最后一部分内容。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值