美文网首页
Handler、Looper、MessageQueue详解---

Handler、Looper、MessageQueue详解---

作者: 慕北人 | 来源:发表于2020-03-28 00:37 被阅读0次

    Handler

    一、成员与构造函数

    Handler有一个静态成员值得注意:

    private static Handler MAIN_THREAD_HANDLER = null;   
    

    不出所料,有一个方法与其搭配:

    public static Handler getMain() {
        if (MAIN_THREAD_HANDLER == null) {
            MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
        }
        return MAIN_THREAD_HANDLER;
    }  
    

    这里直接将其Looper设置为了MainLooper

    构造函数

    Handler的构造函数之间有回调关系,最终都会分别调用两种构造函数:

    public Handler(Callback callback, boolean async) {
        ........
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }  
    
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }  
    

    所以如果Looper对象不传递的话,Handler会自己调用Looper.muLooper()获取Handler所在线程绑定的Looper对象

    二、Message的分发

    Handler通过dispatchMessage分发Message:

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

    可见,处理Message的优先级为:

    • Handler的handleCallback优先级最高
    • Handler的Callback回调第二优先级
    • Handler的handleMessage方法优先级最低

    三、Message的产生

    Handler本身有很多生成Message的方法:

    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }  
    
    public final Message obtainMessage(int what)
    {
        return Message.obtain(this, what);
    }  
    
    public final Message obtainMessage(int what, Object obj)
    {
        return Message.obtain(this, what, obj);
    }  
    
    public final Message obtainMessage(int what, int arg1, int arg2)
    {
        return Message.obtain(this, what, arg1, arg2);
    }  
    
    public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
    {
        return Message.obtain(this, what, arg1, arg2, obj);
    }  
    

    平时都说,推荐使用Message.obtain()和Handler对象的obtainMessage方法来创建Message对象,且二者等价,原因就在于handler对象的obtainMessage方法都是调用的Message.obtain()系列

    注意:在Message.obtain方法中会将其target属性初始化为Handler本身

    四、Message消息的发送

    Handler中有很多方法可以发送消息,主要分为两种:post和send

    1. Post系列方法

    这种方法都是直接post一个Runnable对象,所以首先来看看如何将一个Runnable对象封装成一个Message:

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
    
    private static Message getPostMessage(Runnable r, Object token) {
        Message m = Message.obtain();
        m.obj = token;
        m.callback = r;
        return m;
    }  
    

    很简单,就直接创建一个Message对象,然后将Runnable设置为Message的callback属性

    Q:post生成的Message没有将handler对象绑定到其target属性中?
    对于该情况下的target赋值发生在handler的enqueueMessage方法中

    现在我们来看post系列的方法:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    public final boolean postAtTime(Runnable r, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
    
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }    
    
    public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }    
    
    public final boolean postDelayed(Runnable r, Object token, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r, token), delayMillis);
    }  
    
    public final boolean postAtFrontOfQueue(Runnable r)
    {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }  
    

    可以看到,Post方法都是回调的sendMessage系列方法

    2. send系列方法

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }  
    
    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }  
    
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }  
    
    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }  
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }      
    

    可见,大部分send方法都是调用的sendMessageDelayed方法,而sendMessageDelayed方法又是调用的sendMessageAtTime方法实现的,所以我们可以说,Handler中所有发送Message的方法的实现都是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);
    }    
    

    而sendMessageAtTime的实现实际上就是回调了enqueueMessage

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

    最终我们搞清楚了,原来Handler中所有的回调,都最终归于queue.enqueueMessage方法

    而Handler中还有一个比较奇葩的方法:

    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
    }    
    

    这个看起来和sendMessageXX没啥不同啊,仔细发现,sendMessageDelayed在回调sendMessageAtTime的时候第二个参数做了手脚:

    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)  
    

    其在delayMillis基础上加了SystemClock.uptimeMillis(这个意义是手机从开机到当下的时间间隔),所以其永远都是一个大于0的数;而sendMessageAtFrontOfQueue则把第二个参数直接置为0,实际上其效果等同于:sendMessageAtTime(msg, 0)

    注意:1. MessageQueue在执行enqueue的时候会根据这个msg.when进行插入,所以该值越小越先被MessageQueue.next方法返回,从而越先被Handler处理。 2. 为什么选择使用SystemClock.uptimeMillis而不是System.currentMillis是因为当我们手动更改系统时间的时候会影响后者的值,导致MessageQueue在入队插入的时候逻辑混乱

    3. 小结

    粗略下来,感觉post和send实现都一样,为啥要有两种api发送Message,虽然实现机制都一样,但是在处理的时候就不一样了。还记得handler的dispatchMessage中处理消息的优先级吗?没错:post系列方法的getPostMessage中将post的参数Runnable赋值给了Message的callback,所以导致在处理Post的消息的时候是通过最高优先级的handleCallback方法处理的;而send系列方法,在通常情况下是通过handler的callback属性或者handleMessage方法处理消息。

    五、经典面试题

    Q1:子线程中创建Handler中报错是为什么?

    在Handler的构造器中有这么一段:

    mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }  
    

    因为没有执行Looper.prepare()导致Looper.mylooper返回为null

    Q2:如何在子线程创建Looper?

    Looper.prepare()

    Q3:为什么能通过Handler实现线程切换?

    纯属个人理解:Handler的MessageQueue来自于Looper中的MessageQueue,而在Message.obtain中,Message的target被赋值为了Handler对象;Looper在从MessageQueue中取出Message后通过message.target来处理消息,所以消息的处理是在Looper所绑定的线程,而Handler可以在任意线程往MessageQueue中发送消息,所以实现了线程的切换

    Q4:Handler处理消息发送在那个线程,是Handler所在的线程还是Looper?

    Looper所在的线程

    Q5:Looper和Handler一定要处于一个线程吗?子线程中可以用MainLooper去创建Handler吗?

    不一定位于一个线程;子线程可以通过MainLooper创建Handler:实现有两种:

    Handler h = new MyHandler(Looper.getMainLooper());  
    Handler h = Handler.getMain();  // Handler的api  
    

    Q6:Handler的post/send()的原理

    最终都是调用的sendMessageAtTime -> enqueueMessage -> mQueue.enqueue

    Q7:Handler的dispatchMessage()分发消息的处理流程?

    优先级为:

    • 如果msg.callback不为空,调用handleCallback
    • 如果mCallback属性不为空,则调用mCallback.handleMessage
    • 最后调用handleMessage方法

    Q8:Handler为什么要提供Callback构造方法?

    正常使用Handler要使用一个子类继承Handler,然后再子类中实现handleMessage方法,而使用Callback构造函数则可以简化这一流程,一步到位。

    Looper

    一、成员和构造器

    Looper的成员有三个需要关注:

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class    
    final Thread mThread;
    

    其中的sThreadLocal就是实现Looper和线程绑定的原因

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

    构造器中直接初始化了mThread和mQueue

    二、prepare()、myLooper()方法

    该方法大家都比较熟悉:

    public static void prepare() {
        prepare(true);
    }  
    
    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));
    }  
    

    就是在当前线程中初始化了Looper对象,并且把它保存到了ThreadLocal中

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }  
    

    myLooper()方法就是将Looper对象从ThreadLocal中取出来

    三、quit和quitSafely

    二者的区别就在于:mQueue.quit参数是否为true

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

    通过注释我们知道第一个方法可能导致还有没有接受到的Message,可能会产生意外情况,所以推荐我们使用第二个方法

    四、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);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...
    
            msg.recycleUnchecked();
        }
    }  
    

    这里只保留了关键的代码:

    • 要想loop结束只有msg为null,而在MessageQueue中只有调用了quit方法才会导致其next方法返回null;而Looper的quit和quirSafely方法内部就是回调了mQueue的quit方法,所以只有调用Looper的quit或者quitSafely方法才会终止loop()方法
    • msg.target.dispatchMessage(msg)中,msg的target即为handler对象,所以这里就实现了跨线程

    五、经典面试题

    Q1:如何开启消息循环?

    Looper.loop()

    Q2:Looper两个退出方法有何区别?

    quit会导致还未接收到的Message无法处理
    quitSafely不会

    Q3:如何终止消息循环?

    quitSafely

    Q4:Looper的loop流程

    • 获取Looper和MessageQueue
    • 无限for循环取出msg
    • 执行msg.target.dispatchMessage

    Q5:MessageQueue的next什么情况下返回null

    执行了quit或者quitSafely

    Q6:Android如何保证一个线程最多只能有一个Looper?

    通过ThreadLocal保证只有一个Looper

    MessageQueue

    这里需要提一下其Message mMessages代表链表的表头

    我们这里只讨论其enqueueMessage方法和next方法。

    1. enqueueMessage方法

    boolean enqueueMessage(Message msg, long when) {
        。。。
        synchronized (this) {
            
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                
                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;
            }
    
           is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }  
    

    可以发现,这里就是按照when的大小,将Message插入到链表中

    2. next方法

    Message next() {
       ...
        int nextPollTimeoutMillis = 0;
        for (;;) {
            ...
            //根据我们上面的分析,nextPollTimeMillis为0则不阻塞,也就是第一次循环不阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);
    
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                //这里判断msg不为空但是target为空,而我们enqueueMessage的时候特意设置了target的
                //所以这里的msg不是我们设置而是系统在初始化的时候设置的屏障,这里不再详解
                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());
                }
                //这里是正常情况下的msg
                if (msg != null) {
                    //未达到处理时间的,将会计算需要等待的时间,不超过整形的最大值
                    if (now < msg.when) {
                        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;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                 //这里是队列中没有消息
                } else {
                    nextPollTimeoutMillis = -1;
                }
                //上面分别对消息队列进行判断然后修改nextPollTimeoutMillis,而之前的分析可以看出这个值就是线程
                //需要阻塞的时长,有未达到处理时间的消息则阻塞对应时间,没有消息则一直阻塞直到被唤醒
            ...
            }
            ...
        }       
    }  
    

    next方法详细分析:MessageQueue的next方法详解

    相关文章

      网友评论

          本文标题:Handler、Looper、MessageQueue详解---

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