源码分析:Handler、Looper、Message、MessageQueue之间的关系

Handler是平时用的最多的线程通信机制,都知道一些原理。但是,对于一些细节还是有些不太清楚,下面就带着下面的那些问题来重新熟悉下源码。

  • Handler是怎么保证线程间的通信的?
  • Looper的死循环,是怎么保证的线程不卡顿的
  • Message的消息复用是怎么做的?

先看下怎么使用handler通信的。

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //处理消息
        }
    };
    private void updateMessage() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = mHandler.obtainMessage();
                ...
                mHandler.sendMessage(message);
            }
        }).start();
    }
}


下面我们就从Handler初始化,sendMessage来看下源码。

1,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()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    ...
 /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }


Handler在初始化的时候,做了2件事:

  • 1,从ThreadLocal里面获取当前线程的looper(mLooper = Looper.myLooper())
  • 2,从Looper里面获取MessageQueue (mQueue = mLooper.mQueue)

我们在初始化Handler的时候,最终会调用这个Handler(Callback callback, boolean async)的构造函数。这里面拿到了Looper对象,通过looper拿到了MessageQueue对象。记住在Handler初始化的线程拿到的Looper,跟MessageQueue(一般都在主线程初始化Handler)

这里写图片描述

2,handler.sendMessage()

Handler.java

public final boolean sendMessage(Message msg)
sendMessageDelayed(Message msg, long delayMillis)
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        //拿到queue
        MessageQueue queue = mQueue;
        ...
        //调用enqueueMessage
        return enqueueMessage(queue, msg, uptimeMillis);
    }
...
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {    
        //让Message的target对象指向自己
        msg.target = this;
        ...
        //调用MessageQueue的enqueueMessage。这个时间,如果没传就是0
        return queue.enqueueMessage(msg, uptimeMillis);
    }

这个里面做的事情有:

  • 1,拿到looper(构造器,Looper.myLooper())
  • 2,初始化MessageQueue(构造器,从Looper获取)
  • 3,让Message的target指向自己(Handler)

这里写图片描述

然后,就调用了MessageQueue的enqueueMessage方法。

3,MessageQueue.enqueueMessage()

boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {

            if (mQuitting) {
                //调用Message的recycle方法
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            //第一次的话,mMessages是null的(就是头指针)
            Message p = mMessages;
            boolean needWake;
            //没有消息,或者这个消息没有延迟,或者延迟小于头指针的话
            if (p == null || when == 0 || when < p.when) {
                //链表,就把这个新消息置为头指针。
                msg.next = p;
                mMessages = msg;
                //如果队列阻塞了,就把needWake置为true
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //这里我们遍历消息队列
                for (;;) {
                    prev = p;
                    p = p.next;
                    //找到比延迟消息时间还长的消息或者都比它延迟的短
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                //把这个消息,添加到链表中
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            //如果loop阻塞的话,就唤醒
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }


我们可以看到,这个MessageQueue的enqueueMessage方法里面做的事情主要是

  • 1,如果消息是没有延迟的,我们就把它添加到头指针
  • 2,如果有延迟,我们就按照延迟时间遍历消息队列,把他放到合适的位置。
  • 3,如果loop阻塞了,有立即执行的消息,我们就唤醒loop

这里写图片描述
到这里,我们就看完了handler.sendMessage()做的所有事情。

总结下:
* 首先,初始化Handler,获取Looper和Looper里面的MessageQueue对象。
* 然后,调用MessageQueue的enqueueMessage的方法,把Message放到MessageQueue里。
* 最后,如果消息可立即执行,并且Looper.loop()里面的循环阻塞了,就唤醒。



上面看完了我们自己触发(客户端调用)的代码。下面,我们再分析下系统是怎么获取到Message,并重新交给Handler来处理的。这里我们就需要看程序的入口ActivityThread的main方法是怎么执行了。

1,ActivityThread的main方法

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


在这里我们看到了main方法调用了两个方法:

  • 1,Looper.prepareMainLooper();
  • 2,Looper.loop();

我们先看看Looper.prepareMainLooper()里面都做了什么

Looper.prepareMainLooper()

 public static void prepareMainLooper() {
        //这里调用了prepare方法
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            //这里获取sMainLooper
            sMainLooper = myLooper();
        }
    }
···
private static void prepare(boolean quitAllowed) {
        //在prepare的时候,我们从ThreadLocal获取Looper,如果有的话,就报异常
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //这里在当前线程创建了一个looper放到ThreadLocal
        sThreadLocal.set(new Looper(quitAllowed));
    }
···
public static Looper myLooper() {
        //从当前线程获取looper对象
        return sThreadLocal.get();
    }


这里我们看到prepareMainLooper方法,做的事情有:

  • 1,创建了一个Looper(主线程),并放到ThreadLocal里面
  • 2,sMainLooper从当前线程里面获取一个Looper(sMainLooper就是主线程looper)
  • 3,从prepare方法,我们可以看到每个每个线程只有唯一的一个Looper对象。

这里我们需要知道ThreadLocal的作用。
ThreadLocal是线程局部变量,做线程隔离的,就是为每一个使用该变量的线程都提供一个变量值的副本。每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。这样就保证了* 线程的唯一对应关系,一个线程只有一个Looper *

这里写图片描述

下面我们来看看Looper.loop()方法

 public static void loop() {
        final Looper me = myLooper();

        //这里从looper里面获取到MessageQueue        
        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;
            }
            ...
            //Message的target(上面说了是Handler),这里是运行在主线程。
            msg.target.dispatchMessage(msg);
            //上面处理完Message,这里就会调用Message的recycleUnchecked
            msg.recycleUnchecked();
        }
    }


在这里,我们看到调用了handler的dispatchMessage方法。这里面做了有4件事情:

  • 1,从Looper里面获取MessageQueue。
  • 2,从MessageQueue的next方法里面不断的取出消息(有可能阻塞)
  • 3,调用Handler的dispatchMessage()方法来不断的处理消息。
  • 4,调用了Message的recycleUnchecked()方法。

* 这里强调下第一点,在上面我们看到了,每个线程的Looper都是唯一的,那么在Looper里面的MessageQueue也就是唯一的 *

这里我们再分别看下MessageQueue.next(),Handler.dispatchMessage(),Message.recycleUnchecked(),三个方法

1.MessageQueue的next()方法

 Message next() {
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //阻塞线程
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                //获取当前的开机时间
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                //如果Message的头指针不是空,但是,Message的target Handler为null
                //就是没有消费这个Message的话,就找下一个
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                //如果找到了Message,并且不会null
                if (msg != null) {
                    //如果当前时间小于Message该处理的时间
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        //说明这个消息还没有准备好,计算差值
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //否则,就把mBlocked阻塞置为false,并返回这个消息
                        // Got a message.
                        mBlocked = false;
                        //链表把这个消息去除,改变头指针
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    }
                } else {
                    //如果没有找到消息的话
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
                 if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    // 标识为空闲,可以处理其他消息
                    mBlocked = true;
                    continue;
                }
                ...
                // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
                // 标0代表不用再阻塞等待,可以继续执行
                nextPollTimeoutMillis = 0;
            }

            ...           
        }
    }


我们看了MessageQueue的next()方法。这里面做的事情有:

  • 1,如果没有消息,把nextPollTimeoutMillis = -1
  • 2,如果有消息,没有到时间,nextPollTimeoutMillis = 时间差
  • 3,如果有消息,可以执行,就mBlocked改为false

这个是死循环,所以,当没有Message或者有Message没到时间的话,就会修改nextPollTimeoutMillis的值,然后再次执行。就会执行到nativePollOnce方法,在native层面处理阻塞。

第二个,调用Handler的dispatchMessage()

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

这个方法就回到我们Handler里面的handleMessage()处理消息中。

第三,我们看下Message的recycleUnchecked()方法

void recycleUnchecked() {
        //把使用Message放到对象池(链表)
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

//先从对象池获取Message如果没有,在实例化一个
public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }


这里我们可以看到,只是把使用过的Message放到sPool的链表里。在下次实例化的时候,可以通过obtain()方法,再次使用。

到这里,我们整个Handler、Looper、Message、MessageQueue就都分析完了。这里我们梳理下

首先,是启动应用程序做的事情:

  • 1,我们启动应用的时候,会在主线程初始化唯一一个Looper,并且调用loop()方法。
  • 2,Looper.loop()不断的从MessageQueue.next()里面获取Message,并通过 msg.target(Handler).dispatchMessage()方法,交给Handler来处理。
  • 3,如果MessageQueue里面没有消息,或者消息没到到执行的时间,就会在native层阻塞线程。

* 然后,我们发送消息: *

  • 1,Handler拿到主线程的looper,并从looper里面拿到MessageQueue。
  • 2,给Message设置一个target,指向自己。
  • 3,调用MessageQueue的enqueueMessage方法。把消息放到消息队列里面,如果,有需要立即执行的消息,并且Looper.loop()在native层阻塞的话,在native层唤醒loop()

* 现在,我们再来看下,一开始的几个问题 *

1。handler是怎么保证线程间的通信的?

我们知道在应用初始化的时候会在主线程初始化一个Looper(也是唯一一个),并调用loop()方法。
因为Looper在每个线程是唯一的。所以,它内部的MessageQueue也就是唯一的。
loop()方法是在主线程运行的,loop()方法里面会不断的从自身的MessageQueue消息队列来获取并处理消息,所以处理消息的过程是在主线程完成的。

我们一般在主线程初始化Handler,所以,Handler内部获取的Looper跟通过Looper获取的MessageQueue,都是主线程的那一个。
我们只是通过子线程来发送了消息,获取消息跟处理消息的过程,都是在主线程完成的。


2.looper的死循环,是怎么保证的线程不卡顿的?


当没有消息的时候或者消息没有到执行时间时候,会在native处理阻塞,不会造成主线程的卡顿

3.Message的消息复用是怎么做的?

在消息被处理了以后,会调用Message的recycleUnchecked()方法,重置Message的属性,放到对象池里面(链表)。我们通过Message的obtain()方法,会首先从链表中获取,如果没有,才会重新实例化一个。这样就完成了Message的复用。

完整的调用图

这里写图片描述

猜你喜欢

转载自blog.csdn.net/ecliujianbo/article/details/79764671
今日推荐