Android 的异步消息处理机制

Android 的异步消息处理机制

线程是CPU独立运行和独立调度的基本单位,在 Android 中主线程,又叫UI线程,这是一个特殊的线程,任何耗时的操作最好不要在UI线程中执行,因为这有可能造成 ANR(详情请参考Android ANR 原因分析与检测)。so,耗时任务只能在其它线程搞事,完成后再通知 UI 线程更新界面。这就需要理解Android的异步消息处理。

关于 Android 的异步消息处理机制,一直以来,知其然而不知其所以然。带着好奇,搜了几篇博客,然后跟着鸿洋大神的这篇博客的思路,浏览了一下 Handler、Looper、MessageQueue、Message、ActivityThread 这几个类的源码,忽然间,豁然开朗。具体的原理,大神文中已经分析得比较清楚,这里对阅读过程中想到的几个问题做做笔记。

一、只有 UI 线程才有 Looper 对象吗?

答案是否定的,普通的 Thread 也可以有 Looper 对象。普通的线程也可以在创建的时候创建 Looper 对象,UI 线程之所以看起来天生自带 Looper 对象,其实是在其创建的时候创建了一个Looper对象:

// ActivityThread#main
public static void main(String[] args) {
    // ...
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    // ...
    Looper.loop();
    // ...
}

其实跟踪源码,在 Looper#prepareMainLooper()中并没有创建 Looper,查看其注释,发现这么一句:The main looper for your application is created by the Android environment。原来是在其它地方创建的,这里只是把这个Looper标记为Application的main 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();
        }
    }

二、如何创建一个 Looper Thread?

创建一个线程,在run中调用先后调用 Looper.prepare(),Looper.loop(),在prepare()中创建一个 Looper 对象,存放在sThreadLocal中,要成为一个 Looper Thread 还需要调用 loop(),在loop()中线程进入一个循环,在循环中处理消息,然后可以通过Handler向线程发送消息。HandlerThread 是一个 Looper Thread,它的 run()方法如下:

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

Looper#prepare()代码如下,首先检查 sThreadLocal 是否为null,如果不是,那么抛出一个RuntimeException,如果是,则创建一个Looper对象,并存在sThreadLocal 对象中。所以,一个线程,只能调用一次Looper#prepare()。

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

三、Thread和 Looper Thread有什么区别呢?

一段代码,如果不是使用了while(true)、for(;;) 等死循环的逻辑,按逻辑顺序跑完了就完了。没有Looper对象的 Thread,当 run 方法跑完的时候自动退出了, Looper Thread,在 Looper.loop() 时进入了一个死循环,该循环不断地从消息队列中读取和处理消息。UI 线程就是一个 Looper 线程。

   /**
     * 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();
        // ...
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            // ...
            msg.recycleUnchecked();
        }
    }

注意这段代码,第六行 if 中判断 msg 为null则跳出循环,看起来消息处理完就退出这个线程了。实际上在它的上一句中,queue.next(),这里如果没有消息则线程阻塞等待,然后等有新的消息进入队列才唤醒线程。

四、Handler发送的消息是如何回到 handleMessage() 中处理的?

首先需要知道:在哪个线程创建的Handler,应该说,Handler 对象持有的是哪个线程的 Looper 对象,消息就会发送的哪个线程处理。

4. 1 Handler 发送消息

Handler 发送消息的方法有:post()、postAtTime()、postDelayed()、sendMessage()、sendMessageDelayed()、sendMessageAtTime()、sendEmptyMessage()、sendEmptyMessageDelayed()、sendEmptyMessageAtTime()等等。实际上最后调用的是Handler#enqueueMessage(),将 Message 插入到 Looper 的消息队列中:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Handler#enqueueMessage() 中,调用的是MessageQueue的enqueueMessage():

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                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.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

在最后倒数第5行中,如果需要唤醒则调用了一个native方法,然后回到了loop的消息循环中的queue.next()

4.2 Looper 轮询 MessageQueue

在 Looper#loop() 中,线程进入了一个死循环,不断地从 MessageQueue 中读取 Message 然后交给 Message 的 target处理:

 public static void loop() {
        final Looper me = myLooper();
        // ...
        for (;;) {
            Message msg = queue.next(); // might block
            // ...
            msg.target.dispatchMessage(msg);
            // ...
        }
    }

Message#target 指向的就是发送这个Message 的Handler 对象,所以,消息从哪里来,最终又到哪里去,兜了一圈,然后回到了Handler#dispatchMessage() :

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

4.3 Handler 处理消息

在 Handler#dispatchMessage() 中,最后调用了我们重写的 handleMessage()

class MyActivity extends Activity{
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg){
            // 更新 UI
        }
    }
}

总结

Thread、Handler、Looper、Message、MessageQueue之间的关系:
1. Looper Thread中有且只有一个Looper对象
2. Looper对象有一个消息队列 MessageQueue 对象
3. Handler对象持有Thread的Looper对象,和 Looper 对象的 MessageQueue 对象

线程(如UI线程)在Looper#loop() 中进入消息循环,不断地从消息队列中取出消息进行处理,如果没有消息则阻塞,等待消息进入队列,然后唤醒。handler 对象 向 Looper Thread 发送 Message,最终调用的是Handler 的 enqueueMessage(),enqueueMessage()中将Message插入消息队列,Looper在循环处理消息的时候,将消息从消息队列中取出,通过Message的target属性,将消息交还给发送消息的 Handler 对象处理,最终,调用我们重写的 handleMessage() 方法将消息交给线程处理。

参考

  1. Handler.java、Looper.java、Message.java、MessageQueue.java、ActivityThread.java、HandlerThread.java
  2. Android ANR 原因分析与检测
  3. Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
原创文章 25 获赞 12 访问量 1万+

猜你喜欢

转载自blog.csdn.net/half_bottle/article/details/78513915