Android 中的消息机制

作者: a57ecf3aaaf2 | 来源:发表于2019-04-21 14:24 被阅读15次

    Android 消息机制

    为什么 Android 中的默认操作都在主线程,但是却没有导致阻塞、停止,应用程序仍然能够流畅地运行?

    这就涉及到了 Android 中的运行机制,其实质是基于消息队列的形式运行在特定的线程中的。这个特定线程就是我们所说的主线程或 UI 线程(ActivityThread)。

    Android 消息机制图解

    一个主线程拥有一个 Looper,一个 Looper 拥有一个 MessageQueue,而 MessageQueue 中包含多个 Handler 投递过来的 Message,并通过 Handler 循环地处理 Message。

    Looper

    Looper,暂且称其为“循环装置”吧。这个循环装置怎么来的呢?我们从 UI 线程的入口 ActivityThread 一探究竟。

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

    首先,Looper 通过 prepare 方法创建一个 UI 线程唯一的 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();
            }
        }
    

    Looper 对象通过 ThreadLocal 保证一个线程同时只存在一个该对象的实例:

    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));
        }
    
    public final class Looper {
        /*
         * API Implementation Note:
         *
         * This class contains the code required to set up and manage an event loop
         * based on MessageQueue.  APIs that affect the state of the queue should be
         * defined on MessageQueue or Handler rather than on Looper itself.  For example,
         * idle handlers and sync barriers are defined on the queue whereas preparing the
         * thread, looping, and quitting are defined on the looper.
         */
    
        // sThreadLocal.get() will return null unless you've called prepare().
        static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
        private static Looper sMainLooper;  // guarded by Looper.class
    }
    

    Looper 创建完成后就开始了它辛勤的工作,Looper.loop() 方法就是在不断地、重复的从消息队列中取得消息,并完成消息的处理:

        /**
         * 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();
            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);
                    ...
                } finally {
                   ...
                }
            }
            ...
        }
    

    myLooper() 方法的返回值就是刚刚通过 prepareMainLooper() 方法创建的 UI 线程中唯一的“循环装置” Looper:

    /**
         * Return the Looper object associated with the current thread.  Returns
         * null if the calling thread is not associated with a Looper.
         */
        public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    

    MessageQueue

    上面的源码中,有一行关于 MessageQueue 的代码:

    public static void loop() {
            ...
            final MessageQueue queue = me.mQueue;
            ...
    }
    

    可见,MessageQueue 是从 Looper 中得到的,那么 MessageQueue 何时被创建的呢?

    其实在 prepare Looper 的时候就已经创建了该 Looper 的唯一 MessageQueue 实例:

    private static void prepare(boolean quitAllowed) {
            ...
            sThreadLocal.set(new Looper(quitAllowed));
    }
    
    private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            ...
    }
    

    Message

    回到之前的 loop() 方法中的相关代码,在 for 循环中有这样一行源码:

    public static void loop() {
        ...
        Message msg = queue.next(); // might block
        ...
    }
    

    可见,Looper 在不断地从消息队列中获取消息,获取消息成功后去处理消息:

    public static void loop {
        ...
        msg.target.dispatchMessage(msg);
        ...
    }
    

    Handler

    每一条消息都有一个 target,而这个 target 就是 Handler 对象。我们通过以下代码发送的消息最终的 target 会被指定为当前 new 出来的 Handler,同时被该 Handler 处理:

    new Handler().sendMessage(msg)
    

    那么,当我们在 new 一个 Handler 的时候,Android 都帮我们做了什么呢?

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

    通过以上代码我们了解到,Handler 将成员变量 mLooper 赋值了,而这个值就是当前创建 Handler 线程中的唯一的 Looper,如果是主线程创建的 Handler,那自然就是通过 prepareMainLooper() 方法创建的主线程 Looper。

    如果想在子线程中创建 Handler,在该线程启动时必须初始化 Looper 对象,否则会抛出上述代码中的 RuntimeException 异常。在子线程中创建 Looper 的代码示例如下:

    @Override
    public void run() {
        Looper.prepare();
        synchronized (this) {
             mLooper = Looper.myLooper();
             notifyAll();
        }
        Looper.loop();
        ...
    }  
    

    此时就可以在子线程中创建 Handler 了,当然也可以指定 Handler 的 Looper 对象:

    new Handler(Looper.getMainLooper())
    

    总结

    通过上面的分析,再来看下文章开头提出的问题:

    为什么 Android 中的默认操作都在主线程,但是却没有导致阻塞、停止,应用程序仍然能够流畅地运行?

    其实,主线程在一开始就创建了一个 Looper 对象去做了一个死循环操作,一般情况下,程序永远也无法停止。其次,基于消息队列的机制使得程序可以流畅地运行,而不会出现阻塞的情况发生。

    但是,为了保证 UI 的流畅性,一些耗时操作仍然需要在子线程中完成,UI 线程的串行循环只能处理短时间的响应事件,一旦出现耗时操作,必然会引发 ANR 等问题。

    本文由 Fynn_ 原创,未经许可,不得转载!

    相关文章

      网友评论

        本文标题:Android 中的消息机制

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