美文网首页
Handler、Looper、MessageQueue源码解析—

Handler、Looper、MessageQueue源码解析—

作者: windfall_ | 来源:发表于2017-05-13 23:20 被阅读0次


    Looper


    在Handler的构造函数中有这样一行代码:

    mLooper = Looper.myLooper();

    Looper是干什么的呢?

    Looper负责创建一个MessageQueue,然后开启消息循环,不断的从MessageQueue取出消息,再交给Handler来处理消息。

    Looper中两个最重要的方法是prepare()和loop(),我们分别来看看这两个方法的实现。

    当我们要创建一个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));
        }```
    
    sThreadLocal 是一个ThreadLocal对象,ThreadLocal以key-value的方式储存变量,并保证该变量线程间互不影响。prepare()方法在调用了set方法把一个new Looper对象存进去,所以可以看出prepare()方法只能调用一次,否则sThreadLocal不为空会抛出Exception。接下来进入到Looper的构造方法,一探究竟。
    
    ``` java
        static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
        private static Looper sMainLooper;  // guarded by Looper.class
        final MessageQueue mQueue;
        final Thread mThread;
       
        ...
       
        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    

    在构造方法中新建的一个MessageQueue,并获取当前Thread对象。

    在Handler构造方法中调用了Looper的myLooper()方法:

        public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    

    可以看出myLooper()方法通过ThreadLocal获取当前的线程的Looper对象,回到Handler的构造函数

        public Handler(Callback callback, boolean async) {
            ...
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
           ...
        }```
    
    如果myLooper得到的Looper对象为空,则会抛出异常,也就是说在新线程我们如果要创建一个Handler必须要调用Looper.prepare()。那么,为什么我们在主线程不需要调用prepare()方法呢?
    
    那是因为在ActivityThread中主线程的Looper已经开启:
    
    
    public static void main(String[] args) {
        ...
    
        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()同样会报错。
    
    再看Looper另外一个重要的方法,loop():
    
    ``` java
        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 (;;) {
                // 从MessageQueue中不断取出消息
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
              ...
                try {
                    //调用Handler的dispatchMessage (msg)方法
                    msg.target.dispatchMessage(msg);
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
    
                ...
    
                msg.recycleUnchecked();
            }
        }```
    
    可以看到,loop()方法是一个死循环,从MessageQueue中不断取出消息,然后调用handler的dispatchMessage(msg)方法,交给Handler来处理消息。直到消息队列为空才能结束循环,当然,我们也是可以手动结束消息循环的,Looper提供了两个方法:
    
    
    public void quit() {
        mQueue.quit(false);
    }
    
    public void quitSafely() {
        mQueue.quit(true);
    }```
    

    调用MessageQueue的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);
            }
        }```
    
    >当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。
    
    >当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
    
    具体实现我会在后面的MessageQueue中讲到。
    
    以上就是Looper的大部分源码解析,读到这我们应该能大致知道Handler、Looper、MessageQueue消息处理机制大致是一个怎样的流程了。在Looper的源码中,我们遇到了一个新的东西——ThreadLocal,接下来我会带来ThreadLocal的源码分析。

    相关文章

      网友评论

          本文标题:Handler、Looper、MessageQueue源码解析—

          本文链接:https://www.haomeiwen.com/subject/blyfxxtx.html