美文网首页
Android Handler 运行机制

Android Handler 运行机制

作者: Ruffian_痞子 | 来源:发表于2018-07-10 20:11 被阅读0次

    什么是 Handler?有什么用?

    在 android 应用开发中经常用到 Handler ,大概知道 Handler 常被用来处理线程间通信,比如:在子线程中处理某个逻辑后通过 Handler 发送消息的方式,切换回 UI 线程更新界面

    Handler 到底是什么呢?总能听到最傻瓜式的问法,那么就来简单回答一下:Handler 不是一个具体的对象,暂且说是一套解决方案吧,在 Android 中处理消息 (Message)的一套方案。
    我们通过研究 Handler 的运行机制来了解 android 的消息机制

    什么是 Handler 运行机制?

    这里需要引入几个概念和名词 Message / MessageQueue / Looper
    以下请允许我用不太恰当地方式简单解释一下这几个概念

    Message : 消息
    MessageQueue : 消息队列
    Looper : 无限循环地从队列中获取消息交给 Handler
    Handler : 发送消息,处理消息

    如果有人问你, Handler 运行机制 是怎么样的,你就告诉他:

    Handler 发送消息(Message) 到消息队列中(MessageQueue) ,Looper 通过无限循环的方式不断向消息队列中获取新添加的消息 然后交给 Handler ,最终消息回到 Handler 中被处理

    (平常开发中)由于 Handler 创建在主线程,处理消息的方法也运行在主线程,因此上述中消息被 Looper 交回给 Handler 的时候就实现了“线程切换”

    喔~这个6,这个6,这个思想非常棒,仔细想想在子线程中做了一堆操作,什么添加消息到队列,我们不管,只认准最后 Looper 把消息交回给了 Handler ,又因为 Handler 运行在主线程中,因此最后处理消息时就在主线程了(根本原因是创建Handler 的 Looper 默认使用主线程的 Looper )

    理论往往是抽象枯燥的,我们总希望通过熟悉的代码来慢慢体会,来看下日常开发中常用Handler使用场景,在子线程中处理耗时逻辑,然后再发送消息到主线程中更新UI

    在主线程中创建Handler处理类

        class MyHandler extends Handler {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 1:
                        Log.e("tag", "thread name:" + Thread.currentThread().getName());
                        //更新UI
                        break;
                }
            }
        }
        
        MyHandler myHandler = new MyHandler();
    

    创建线程,处理耗时逻辑,然后发送消息

            new Thread("t1") {
                @Override
                public void run() {
                    //耗时操作
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Message message = Message.obtain();
                    message.what = 1;
                    //发送消息
                    myHandler.sendMessage(message);
                }
            }.start();
    

    这里可以看到,我们在主线程(activity运行在主线程中)创建Handler,然后创建一个子线程 t1 ,在 t1 中处理耗时逻辑,再通过 sendMessage() 发送消息,就是这么一个简单的代码,能够实现"线程切换"的效果,到底是怎么实现的呢?

    开发中我们能接触到的只有 Handler ,对于 MessageQueue 和 Looper 这两个概念比较陌生,就上述的例子中也完全没有体现这些字眼,但是对于 Handler 的运行他们起到了不可缺少的重要作用。

    Handler 通过 sendMessage 发送消息后,将 Message 发送到了哪里呢? 又是怎么拿到这些 Message 的呢?

    MessageQueue 消息队列工作原理

    MessageQueue 翻译为消息队列。简单的说就是装载消息(Message) 的容器。容器?那不是很简单,只要负责 添加读取(移除) 内容就好了。

    当 Handler 通过 sendMessage 方法发送 Message 时, MessageQueue 就会调用 enqueueMessage 方法把这条消息添加到自身容器中。然后呢?然后就放着呗,等着别人来取,当有人来取 Message 的时候,通过 next 方法取出一条消息并将消息从自身容器中移除,这就是消息队列的工作职责

    这里注意,虽然叫做消息队列,但是内部实现并不是用队列,而是单链表的数据结构来维护消息列表

    MessageQueue : 添加消息到队列 enqueueMessage

    上面说 sendMessage 时 MessageQueue 就会调用自身的 enqueueMessage 将消息添加到自身容器,怎么实现的呢?Handler 和 MessageQueue 两者又是怎么关联起来的?这时候我们就需要通过阅读一下源码啦

    等一下,别走啊,这源码很简单的,不用一头扎入,看个大概思路就行

    我们知道 Handler 发送消息的方法有很多,先不管,需要知道的是最后调用的都是同一个 sendMessageAtTime()

        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                        this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
            return enqueueMessage(queue, msg, uptimeMillis);
        }
    

    这个就是 Handler 的 sendMessageAtTime 方法的源码啦,很简单,将一个 mQueue (特别注意这个 mQueue)赋值给 MessageQueueMessageQueue 如果为空,抛异常,不为空当做一个参数传递给 enqueueMessage (Handler 的方法),恩,这里问题不大,很普通的写法,接着看看 Handler 的 enqueueMessage 方法

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

    丢掉前面无关的代码,直接聚焦 queue.enqueueMessage(msg, uptimeMillis) 这行代表表示,最终调用了 MessageQueueenqueueMessage 方法将消息添加进队列

    到这里已经很清晰可以看到 MessageQueue 将消息加入队列的逻辑了,不用看都知道, enqueueMessage 具体实现肯定是操作链表添加内容

    怎么样?是不是很简单,很清晰的看到

    Handler 调用 sendMessage 方法时 MessageQueue 会通过 enqueueMessage 方法将 message 添加到自身容器中

    嗯,细心的同学或许就会问了,这个 MessageQueue 是哪里来的,你突然引入一个 MessageQueue 然后就开始添加 消息了,也没说他是哪里崩出来的呀

    回到刚才那个 sendMessageAtTime() 方法,第一行代码出现:MessageQueue queue = mQueue;
    原来 MessageQueue 是通过 mQueue 赋值的。

    在 Handler 类中搜索 mQueue ,找啊找啊,最后在 Handler 构造方法中找到它

        public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    没错就是它 mQueue ,它是通过 looper.mQueue 得到的

    而且我们通过其他的构造函数发现 Handler 的所有构造函数最终都是调用这个方法,也就是说,我们在创建 Handler 的时候就创建了这个 MessageQueue

    soga 原来 MessageQueue 是通过 Looper 得到的,那 Looper 又是什么?乘胜追击看看这个 Looper 是什么。

    Looper 工作原理

    Looper 在 android 消息机制中扮演着 循环从队列中获取消息的角色,就是说它会无限循环的查询 消息队列中是否有新的消息,如果有就获取到这个消息,然后交给Handler,如果没有就阻塞在那里

    先看看 Looper 的构造方法

        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    

    在 Looper 构造方法中,创建了一个 MessageQueue 即消息队列,然后获取当前线程保存起来。

    我们知道 Handler 工作需要 Looper 的支持,如果没有 Looper 的线程中使用 Handler 就会报错,那如何为一个线程创建一个 Looper 呢? 很简单, 通过 Looper.prepure() 就可以为一个线程创建 Looper ,接着调用 Looper.loop() 来开启消息循环。示例如下

           new Thread("t1") {
                @Override
                public void run() {
                    Looper.prepare();//创建Looper
                    Handler handler = new Handler();//Handler 使用
                    Looper.loop();//开启消息循环
                }
            }.start();
    

    Looper.prepare() 创建 Looper 很简单不说了,下面有个 Looper.loop() 开启消息循环,这个是 Looper 的重心啦,看看怎么实现的

    loop() 的源码比较多,不必每一行都去分析,我们通过剔除一些代码,让源码看起来简洁一些,这样就不会掉进源码的漩涡中不能自拔。

        public static void loop() {
            //剔除代码
            final MessageQueue queue = me.mQueue;
            //...
            for (;;) {
                Message msg = queue.next(); // 调用 MessageQueue 的 next 方法获取 Message
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    // msg == null 表示   MessageQueue 正在退出(调用了quit等方法)
                    return;
                }
                //剔除代码
                 try {
                    // msg.target 就是发送消息的Handler,因此这里将消息交回 Handler
                    msg.target.dispatchMessage(msg);
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                //剔除代码
            }
        }
    

    前面说了,Looper 通过无限循环的方式获取 消息队列 中的消息

    for (;;) 死循环从消息队列中获取 消息 ,当 msg == null 跳出循环。

    通过调用 Looper 的 quit() 或者 quitSafely() 让 Looper 退出循环

        // quit
        public void quit() {
            mQueue.quit(false);
        }
    
        // quitSafely()
        public void quitSafely() {
            mQueue.quit(true);
        }
    

    我们看到 Looper 的 quit() 或者 quitSafely() 方法调用的是 MessageQueuequit() 方法,使得 msg 返回 null 从而让 Looper 退出循环。这两个方法的区别是: quit 是直接退出 Looper; quitSafely 会设置一个退出标识,等到队列中的消息都处理完了,再安全退出。Looper 退出后 Handler 就无法发送消息了

    loop() 中 获取到消息通过 msg.target.dispatchMessage(msg) 将消息 message 交给 Handler 的 dispatchMessage 方法处理,这也就是为什么 能够实现线程切换的关键所在,因为 Handler 运行在主线程

    MessageQueue : 获取消息 next

    Looper.loop() 中我们看到通过 MessageQueuenext 方法获取队列中的消息

        Message next() {
            // 剔除代码
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            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;
                    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());
                    }
                    if (msg != null) {
                        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 {
                            //存在消息,获取消息返回 
                            // Got a message.
                            mBlocked = false;//表示目前没有阻塞
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;//将 msg 从 MessageQueue 链表中移除
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        // 没有消息处理 进入阻塞
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                    }
    
                    // MessageQueue 退出 返回 null
                    // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
                     //剔除代码
                }
                //剔除代码
            }
        }
    

    next 通过 for (;;) 死循环不断获取新增的消息,返回给 Looper,如果没有消息,就一直阻塞在这里,当有新消息到达,next 会返回这条消息,并且从链表中删除

    Handler 工作原理

    Handler 的工作原理就比较简单啦,主要负责 发送消息处理消息

    发送消息 主要通过一些列 postsend 方法来实现,具体可以查查接口文档,通过源码可以看到最终都是调用 sendMessageAtTime()

        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                        this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
            return enqueueMessage(queue, msg, uptimeMillis);
        }
        
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    sendMessageAtTime() 的源码很简单,调用 MessageQueue 的方法 enqueueMessage 将 Message 添加到消息队列,同时 msg.target = this;Handler 自身赋值给了 msg.target

    可以发现, Handler 发送的消息过程是向消息队列添加一条消息,这时 MessageQueue 的 next 方法就会将这条消息返回给 Looper,Looper 收到消息后开始处理,最终通过 msg.target.dispatchMessage(msg); 将消息交给 Handler ,即 Handler 的 dispatchMessage 方法会被调用,这时 Handler 就进入了消息处理阶段

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

    Handler 处理消息的过程:

    首先检查 Messagecallback 是否为 null。 不为 null 直接通过 handleCallback 来处理消息。Messsagecallback 是一个 Runnable 对象,也就是 Handler 的 post 系列方法所传递的 Runnable 参数。 handler.post(runnable);
    handleCallback

    private static void handleCallback(Message message) {
            message.callback.run();
        }
    

    然后检查 mCallback 是否 null。不为 null 直接通过 mCallbackhandleMessage 处理消息。mCallback 是一个接口,定义如下

        /**
         * Callback interface you can use when instantiating a Handler to avoid
         * having to implement your own subclass of Handler.
         * //不需要通过一个 Handler 的子类,就可以创建一个 Handler 的实例
         *
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public interface Callback {
            public boolean handleMessage(Message msg);
        }
    

    接口注释说明了,通过 Callback 可以不通过 Handler 的子类就能实现一个 Handler 的实例。
    不理解?没事,代码解释。开发日常我们用的比较多的是通过继承 Handler 实现一个 Handler 的子类来实现

    class MyHandler extends Handler {
            @Override
            public void handleMessage(Message msg) {
                //处理消息
            }
        }
    

    而使用 Callback 接口时

    Handler handler = new Handler(new Handler.Callback() {
                @Override
                public boolean handleMessage(Message msg) {
                    return false;
                }
            });
    

    当我们不想派生子类时,就可以通过 Callback 的方式实现

    OK,再回过头来看看 Handler 运行在主线程 这个问题。
    其实 Handler 运行在哪个线程,是由创建 Handler 的 Looper 决定的,简单看个例子(onCreate()方法中)

            new Thread("t1") {
                @Override
                public void run() {
                    Looper.prepare();
                    looper = Looper.myLooper();
                    Looper.loop();
                }
            }.start();
    
            SystemClock.sleep(100);//确保成功获取到 looper
    
            Handler handler = new Handler(looper, new Handler.Callback() {
                @Override
                public boolean handleMessage(Message msg) {
                    Log.e("Ruffian", "Thread Name:" + Thread.currentThread().getName());
                    return false;
                }
            });
            Message mess = Message.obtain();
            mess.what = 1;
            handler.sendMessage(mess);
    

    解释一下代码:创建一个子线程 t1 ,为这个线程创建 Looper (Looper.prepare();),然后获取 t1 线程的 Looper 用于创建 Handler

    在 Handler 的 handleMessage 中处理消息,打印 Handler 所在的线程,结果为: t1

    那为什么默认创建的 Handler 就是运行在主线程的?看下面的例子

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Message mess = Message.obtain();
            mess.what = 1;
            Handler handler = new Handler(new Handler.Callback() {
                @Override
                public boolean handleMessage(Message msg) {
                    Log.e("Ruffian", "Thread Name:" + Thread.currentThread().getName());
                    return false;
                }
            });
            handler.sendMessage(mess);
        }
    
    

    在 Handler 的 handleMessage 中处理消息,打印 Handler 所在的线程,此时,结果为: main

    看一下 Handler 构造方法的代码(在不指定Looper的时候)

    public Handler(Callback callback, boolean async) {
            //剔除代码
            mLooper = Looper.myLooper();//获取当前线程Looper
            //剔除代码
        }
    

    通过代码可以看出,在创建 Handler 不指定 Looper 时,Handler 的构造方法会获取当前线程的 Looper ,由于activity 的 onCreate() 方法运行在主线程,所以 Looper 也属于主线程,通过此 Looper 创建的 Handler 当然也运行在主线程啦~

    由此可以验证 Handler 运行在哪个线程由 创建 Handler 的 Looper 属于哪个线程决定

    讲完...

    相关文章

      网友评论

          本文标题:Android Handler 运行机制

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