现在的位置: 首页 > 综合 > 正文

Android’s Thread, Looper, Handler, Message, MessageQueue那些事儿

2018年03月31日 ⁄ 综合 ⁄ 共 6736字 ⁄ 字号 评论关闭

        做多了Android App开发,对这些经常使用,但突然被问起来原理,虽然知道个大概,但真要拿出证据说服,还是得看看Framework代码,才能知道真正的原理,所以,这个博文,我们就直接看代码,讲点实际的东东,也方便大家在日后的分析问题中,更快找的原因。

        标题也说的很清楚了,就讲这点东西,说白了,就是线程间的通信,如果做过Win32开发的朋友,应该知道,线程间的通信有几种,比如:内存共享,消息传递等,然而,在Java的世界中,是没有对内存直接操作这个的,看到Message, MessageQueue,大家就知道了,Android是通过消息传递来进行线程间通信的。

一、概要:

Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。

Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。

MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。

Thread:线程,负责调度整个消息循环,即消息循环的执行场所。

二、关系:

图中,反应了MessageQueue是依赖Looper的,而Handler可以使用当前线程的Looper和MessageQueue中的方法,如果有多个Handler在同一线程,那么,都可共用Looper和MessageQueue。

三、代码讲解:

         1. Handler.java (文件路径 ./android/os/Handler.java)

    public Handler() {
        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 = null;
    }

        创建一个Handler,获取当前线程的Looper,并且从Looper中得到MessageQueue的链表header指针。

        注: 通常我们都是在UI主线程中创建Handler,因此,获取的Looper为UI主线程的Looper,

               如果,我们在子线程中创建Handler,那么,我们一定要在创建子线程Handler之前,

               先要创建子线程的Looper,否则会crash,代码如下:

     class LooperThread extends Thread {
         public Handler mHandler;
   
         public void run() {
             Looper.prepare();
   
             mHandler = new Handler() {
                 public void handleMessage(Message msg) {
                     // process incoming messages here
                 }
             };
   
             Looper.loop();
         }
     }

        在Thread中,我们会调用handler.sendEmptyMessage / sendMessageDelayed / sendMessage / postDelayed / post 等,最终都会走到 sendMessageAtTime 这个

        接口,我们看看这个接口究竟做了什么事:

    /**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }

        我们看到,首先会去检查MessageQueue是否存在,然后Message将target设置为自己,最终调用MessageQueue.enqueueMessage方法,将Message插入到MessageQueue中,这样就完成了发送消息的过程。
 

         2. Looper.java (文件路径 ./android/os/Looper.java)

         初始化Looper类:

    private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

         调用Looper.prepare():

     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

          在调用Looper.loop()前,必需先调用prepare(),在结束后必需调用quit,释放内存。

          接下来,看看Looper的核心代码,loop()是怎么从MessageQueue中取Message,并发送给Handler来处理的:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        MessageQueue queue = me.mQueue;

        while (true) {
            Message msg = queue.next(); // might block
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                msg.target.dispatchMessage(msg);
                msg.recycle();
            }
        }
    }

         loop()中,有个while死循环,直到收到msg.target = null的消息,这个消息是谁发的呢?看注释,我们知道是退出消息,那么,当然就是quit()函数发给自己的消息,来退出循环。循环中,不断的从MessageQueue中取下一条Message,如果有且msg.target肯定存在,则调用msg.target 的dispatchMessage(Message)方法,将消息传给目标,而这个目标,正是之前调用enqueueMessage的Handler自己。

         我们在回到Handler.java中,看看dispatchMessage的实现: 

    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
    
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

         在dispatchMessage中,会去检查Message.callback是否存在,也就是说,我们在创建Message时,可以指定一个callback,而不需要调用Handler.handleMessage方法;或者我们实现Callback接口及接口中的handleMessage方法;如果以上两种都没有,那么将调用我们必需实现的Handler.handleMessage方法。

         绕了一大圈,最终回到了Handler中来处理!

         Message的发送,处理流程,我们已经清楚了,我们看看承载着消息的队列 MessageQueue到底做了什么?比如:我们post 或者 postDelay,MessageQueue是怎么处理这两种消息的优先级的?

         3. MessageQueue.java (文件路径 ./android/os/MessageQueue.java)

    final boolean enqueueMessage(Message msg, long when) {
        final boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }

            msg.when = when;
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

         MessageQueue是一个底层类,在初始化时,会去调用native代码,而native代码就是用C/C++来实现的内存,数据结构的管理,从代码中可以看到它实际上,是一个链表结构:如果当前要插入的 Message.when为0,或者小于第一条消息的when,即执行时间,则将当前 Message 置为 MessageQueue的 header;反之,从链表头开始向后搜索,直到找到一条 Message.when的时间是大于当前的时间或者直接插到尾部。

    final Message next() {
        for (;;) {
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        mBlocked = false;
                        mMessages = msg.next;
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }
            }
        }
    }

         MessageQueue.next()中,也有一个死循环,这个循环,用来不断的检查当前时间与消息队列中第一条Message.when的时间,如果当前时间大于等于消息的时间,则移动 MessageQueue 的头指针至下一条,然后将当前消息返回。

四、小结

       1. Handler的处理过程运行在创建Handler的线程里

       2. 一个Looper对应一个MessageQueue

       3. 一个线程对应一个Looper

       4. 一个Looper可以对应多个Handler

       5. 不确定当前线程时,更新UI时尽量调用post方法

抱歉!评论已关闭.