Handler机制源码分析之Looper

Looper作为轮询器,其作用就像送货员的货运车,用于为指定线程创建并维护一个消息队列,并从消息队列中取出消息,交给Handler处理。


1. Looper中的一些字段

private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
//ThreadLocal保存着各个线程的信息,但当前线程调用的时候又只会得到当前线程信息,这里是创建的保存Looper对象的ThreadLocal对象
static final ThreadLocal sThreadLocal = new ThreadLocal(); 
private static Looper sMainLooper;  // 主线程的Looper对象引用
final MessageQueue mQueue; //与该Looper绑定的MessageQueue字段
final Thread mThread;  //与该Looper绑定的线程
private Printer mLogging;  //打印Log的
private long mTraceTag; //追踪标记

2. 创建Looper对象,prepare()方法

我们在创建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));
    }

这个方法是私有的,首先使用ThreadLocal中的get()方法,这个方法可以得到当前线程的Looper对象,如果不为空则会抛出异常,为空则创建新的对象,所以这个方法明确了** 一个线程只能创建一个Looper. **

ThreadLocal为每个线程单独保存了一份信息,多线程下可以同时访问,但是又互不影响,有兴趣的可以去看看ThreadLocal源码,里面的代码不多.这里就不展开说明了.

接着我们来看看quitAllowed这个参数,意思很明确,是否允许退出,刚才传进来的参数为true,那么就表示允许退出,那退出什么呢?,可以看到它又将这个传到给了其构造方法,那我们接下来就来看看其构造方法.

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

这个方法也是私有的,所以我们不能用** new ** 创建Looper对象,这个方法就做了两件事.

  1. 将创建MessageQueue对象并将quitAllowed传给它.
  2. 得到当前对象,并赋给mThread字段.

这里简单来说就是在创建Looper对象的时候,绑定了一个消息队列和当前线程.
既然将quitAllowed传到了MessageQueue中,那我们来简单看看这个在MessageQueue中做了什么吧.


** MessageQueue.class **

MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed; 
        mPtr = nativeInit(); 
    }

可以看到这个构造方法也是就做了两件事.

  1. 先将其赋给了MessageQueue中的mQuitAllowed字段.
  2. 调用了native层中的nativeInit方法.

这个方法是用C++写的,有兴趣的可以去看看,这个方法主要就是在native层创建了一个MessageQueue,然后mPtr就是指向native层中MessageQueue中的指针.
实际上MessageQueue真正的创建也就是在native层中,使用C++来写的,采用的ePoll和Poll机制来进行消息驱动,毕竟这些操作还是用C++来完成比较有效率.


这里只是将quitAllowed赋给了mQuitAllowed ,没有看到其具体的作用,既然如此,那我们继续找找.
就是这了

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);
        }
    }

先找到mQuitAllowed,可以看到quit方法中的采参数就是mQuitAllowed,当其为true才可以继续下面的代码,否则就会抛出异常了,
接下来不看其他的,可以看到总共就只有3个方法.

  • removeAllFutureMessagesLocked();removeAllMessagesLocked();看名字很明显是用来移除消息的,和safe参数有关,所以将这两者放在一起,显而易见,safe为true就调用前者,为false就调用后者,那么这两者有什么区别呢?我们还是看名字,一个是feature,一个是all,那么我们可以猜测,一个是移除未来,也就是还没执行的消息,一个是不管其他,移除全部的消息.嘛实际这两个方法也就是这个作用...关于MessageQueue源码,下次再仔细说...
  • 最后还有个方法nativeWake(mPtr)看名字就知道是调用的native层的方法,那么这个方法是干嘛用的呢?在这之前先想想,在执行这个方法之前做了什么?对,就是移出消息,不管是调用的是移除未执行的还是移除全部的,** 都产生了一个结果,MessageQueue中的消息发生了变化 ** 那么显而易见的,这个方法的作用就是唤醒native中的MessageQueue来进行真正的移除操作.你看都不需要去看native层中C++的代码,就能明白其方法的具体作用了~!

那么言归正传,还记得么quit方法中mQuitAllowed如果为false,会怎样?对,抛出异常,那还记得抛出的是什么异常么?对,就是** Main thread not allowed to quit **主线程不允许退出,那么也就是主线程也会创建Looper对象,而且传进来的mQuitAllowed参数为false((这不是废话么~!)).

再回到** Looper **之前我们先去看看主线程到底在哪里创建了Looper对象.
大家是否还记得在java中我们的程序都是从main方法开始执行的,可是到了Android中并没有看到main方法,会不会曾经产生过这样的疑问,是不是Android中没有main方法?Android不也是java写的么,为什么差别会这么大呢?实际上不是的.让我们看看下面这个类
** ActivityThread.class **

public static void main(String[] args) {
        ......

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        ......
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

可以看到在ActivityThread里面有个main方法,这里就是java层的起始了,为什么说是java层呢?当然是还有个native层(这不废话么),让我们看看这个方法,这里面我只截取了部分代码,具体的可以去看看源码,

这有这么一句代码Looper.prepareMainLooper();还有这一句 Looper.loop();很明显,这两句代码的作用一个就是创建主线程的Looper对象,另一个就是进行轮询消息了.

在这两个方法间还有个sMainThreadHandler = thread.getHandler();得到一个Handler,既然Thread,Looper,MessageQueue都有了,那么Handler当然也不能少.

最后的最后,也就是在main()方法的最后,还抛出个异常,这是干嘛呢?还能干嘛,异常信息写的很清楚** 主线程循环意外退出了 **正常情况下是执行不到这一句代码的.
对了,ActivityThread并不是主线程,只是一直运行在主线程上而已,主线程是Zygote创建的.


那最后让我来看看main()方法调用的Looper.prepareMainLooper()吧,终于回到Looper中了~!

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

可以看到prepareMainLooper()这个方法其实和prepare()差不多,myLooper()其内部也只是return sThreadLocal.get()而已,所以就不在多说了.
只是一个Looper的创建就扯了这么久,(我还是真能水啊,呵呵),嘛,毕竟Looper牵涉的比较多,又是MessageQueue又是ActivityThread的,还聊了些之外的MessageQueue中quit和nativeWake.


3. Looper中quit方法

既然都已经提到了MessageQueue中quit方法,那么接下就说说Looper中的quit方法吧,大家估计猜都猜到里面肯定调用了MessageQueue中的quit方法,既然如此,那么就不用说了吧.嘛,实际上Looper这个quit方法里面还真就是只调用了MessageQueue中quit方法,发个源码看看就行了.

    public void quit() {
        mQueue.quit(false);
    }

    public void quitSafely() {
        mQueue.quit(true);
    }

就是一个是安全退出,一个是不安全退出,都调用的MessageQueue中的quit方法.
接下来就是重头戏了


4. Looper中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;

        .......
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            .......
            try {
                msg.target.dispatchMessage(msg);
            } 
            ......
            msg.recycleUnchecked();
        }
    }

这个方法不算太长,但也不短,所以我删减了一些不太重要的,身份验证,trace和一些Log,详情可以去看看源码.

首先这是myLooper();里的具体实现return sThreadLocal.get()返回的就是当前线程的Looper.
那么这个方法基本可以分为四部分:

  1. for(;;)之前,就是得到当前线程的Looper对象和与其绑定的MessageQueue对象.
  2. 调用MessageQueue中的Message msg = queue.next()方法,拿到消息.
  3. 调用Message中的msg.target.dispatchMessage(msg)方法,执行消息
  4. 执行msg.recycleUnchecked()回收消息.

在loop()中主要的其实就是执行消息和回收消息,至于拿到消息的具体实现在MessageQueue中,这个下次再说.

以上就是Looper中和消息处理机制有关,并且比较重要的方法了.至于其他的一下方法,可以去阅读阅读一下源码,多看看一些源码还是对提升挺有帮助的.

** 总结: **

  1. Looper里面有着MessageQueue和当前线程thread的字段,并且Looper对象是存放在ThreadLocal中的
  2. 创建Looper对象的时候会绑定当前线程和创建一个MessageQueue对象,并与之绑定.主线程的Looper对象在ActivityThead中的main()被创建,
  3. 主线程的quitAllowed为false,不可退出.其他线程的quitAllowed为true.可以退出.
  4. loop()是真正进行消息轮询的地方,里面是个死循环,主要做的事为,调用MessageQueue中的next()拿到消息,然后处理消息,最后将消息回收.

相关文章:
Handler机制源码分析之Handler
Handler机制源码分析之Message

参考文章:
Android消息处理机制(Handler、Looper、MessageQueue与Message)
Android消息机制2-Handler(Native层)

最后,大家五一快乐啊~!

你可能感兴趣的:(Handler机制源码分析之Looper)