美文网首页Android
(Android面试必知必会系列)Handler消息机制

(Android面试必知必会系列)Handler消息机制

作者: 蓝师傅_Android | 来源:发表于2019-02-01 16:58 被阅读0次

    这篇文章应该是除夕之前的最后一篇文章,写文章的一个很深的体会,就是一个知识点虽然自己能理解,可以说出来,但是在写的时候要花很多时间,因为要让读者可以很好理解,不然写文章就没有意义了。

    进入正题,Android消息机制基本是面试必问的知识点,今天结合源码和面试中常问的点,进行一个分析和总结。

    开始源码分析

    Handler的使用

    我们一般使用Handler是这样

            Handler handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == 1){
                        //
                    }
                }
            };
    
            Message message = Message.obtain();
            message.what = 1;
            
            //1
            handler.sendMessage(message);
            //或者2
            handler.sendMessageDelayed(message,1000);
            //或者3
            handler.post(new Runnable() {
                @Override
                public void run() {
                    
                }
            });
    

    接下来开始分析,首先看下 Handler构造方法

    Handler 构造方法

        public Handler() {
            this(null, false);
        }
        
        public Handler(Callback callback, boolean async) {
            ...
            mLooper = Looper.myLooper();
            //1
            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;
        }
    

    注释1:主线程默认有一个Looper,而子线程需要手动调用 Looper.prepare() 去创建一个Looper,不然会报错。

    看下主线程Looper在哪初始化的,应用的入口是 ActivityThread 的main方法

    ActivityThread#main

        public static void main(String[] args) {
            ...
            //1
            Looper.prepareMainLooper();
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            Trace.traceEnd(64L);
            //2
            Looper.loop();
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    

    注释1:Looper.prepareMainLooper(),
    注释2:Looper开启消息循环
    接下来分析下

    Looper.prepareMainLooper()

        public static void prepareMainLooper() {
            //1
            prepare(false);
            Class var0 = Looper.class;
            synchronized(Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                } else {
                    sMainLooper = myLooper();
                }
            }
        }
    

    可以看到调用prepare(false),参数false表示不允许退出。然后是给sMainLooper赋值,看下prepare方法

    Looper.prepare()

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal();
    ...
        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");
            } else {
                //1
                sThreadLocal.set(new Looper(quitAllowed));
            }
        }
    

    可以看到 prepare 是创建一个Looper,并放到 ThreadLocal里。

    准备工作做好了,看下Looper.loop()

    Looper.loop()

    
        public static void loop() {
            //1 从ThreadLocal 获取Looper
            Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            } else {
                //2 Looper里面有一个消息队列
                MessageQueue queue = me.mQueue;
                Binder.clearCallingIdentity();
                long ident = Binder.clearCallingIdentity();
    
                while(true) {
                    //3 死循环获取消息
                    Message msg = queue.next();
                    if (msg == null) {
                        return;
                    }
                    ...
                    long end;
                    try {
                        // 4,获取到消息,处理
                        msg.target.dispatchMessage(msg);
                        end = slowDispatchThresholdMs == 0L ? 0L : SystemClock.uptimeMillis();
                    } finally {
                        if (traceTag != 0L) {
                            Trace.traceEnd(traceTag);
                        }
    
                    }
                    ...
                    // 5.消息回收,可复用
                    msg.recycleUnchecked();
                }
            }
        }
        
        public static Looper myLooper() {
            return (Looper)sThreadLocal.get();
        }
    

    注释1: 从ThreadLocal 获取Looper,子线程需要调用Looper.prepare,不然会报错,上面说过。
    注释2:获取消息队列
    注释3:queue.next(),获取一个消息
    注释4:处理消息

    接下来分成几个小点分析一下

    1.mQueue 是什么

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

    mQueue是一个消息队列,在创建 Looper 的时候创建的。

    2.MessageQueue#next

        Message next() {
            ...
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
                // 1 native方法,应该是唤醒线程的
                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;
                            //2.取出下一条消息
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        // 3.延时的处理,计算定时唤醒时间
                        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.
                            //4.获取到一条消息,因为是消息队列是链表结构,所以需要调整一下链表
                            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 {
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                    }
    
                    // Process the quit message now that all pending messages have been handled.
    
                    ...
                    //5.这里提到 IdleHandler,是个什么东西?下面会分析
                    for (int i = 0; i < pendingIdleHandlerCount; i++) {
                    final IdleHandler idler = mPendingIdleHandlers[i];
                    mPendingIdleHandlers[i] = null; // release the reference to the handler
    
                    boolean keep = false;
                    try {
                        keep = idler.queueIdle();
                    } catch (Throwable t) {
                        Log.wtf(TAG, "IdleHandler threw exception", t);
                    }
    
                    if (!keep) {
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }
    
            }
        }
    

    next方法,取出一条消息,如果有设置延时,计算一下多久后执行,然后在下一次循环,注释1处,调用native方法进行一个类似闹钟的设置,时间到的话会唤醒next方法。消息队列是单链表的数据结构,所以取出一条消息之后链表需要移动,注释4处。

    消息取出来之后就要处理了,即
    msg.target.dispatchMessage(msg);
    msg.target是一个Handler

    注释5是扩展分析,面试映客直播时候被问到Handler中的Idle是什么,这里就写一下
    在获取不到message的时候才会走注释5的代码,也就可以理解为 IdleHandler是消息队列空闲(或者主线程空闲)时候才会执行的Handler。
    IdleHandler 是一个接口,只有一个方法 queueIdle() ,调用addIdleHandler(IdleHandler handler) 这个方法的时候会将handler添加到list中

    public void addIdleHandler(@NonNull IdleHandler handler) {
            synchronized (this) {
                mIdleHandlers.add(handler);
            }
        }
    

    然后在消息队列空闲的时候会遍历这个list,执行里面IdleHandler的queueIdle方法。

    有什么应用场景呢?
    leakcanary 中使用到这个

    // 类:AndroidWatchExecutor
    private void waitForIdle(final Retryable retryable, final int failedAttempts) {
        // This needs to be called from the main thread.
        Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
          @Override public boolean queueIdle() {
            postToBackgroundWithDelay(retryable, failedAttempts);
            return false;
          }
        });
      }
    

    leakcanary中检测内存泄漏的耗时任务会等到主线程空闲才执行

    3.Handler#dispatchMessage

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

    一般走的是注释3,也就是我们重写的 handleMessage方法,
    注释1 :调用 Message.obtain(Handler h, Runnable callback) 传的callback。
    注释2:创建Handler的时候使用这个构造Handler(Callback callback)

    Looper开启循环,从MessageQueue取消息并处理的流程分析完了,还差一个消息的入队

    Handler#sendMessage

        public final boolean sendMessage(Message msg)
        {
            return sendMessageDelayed(msg, 0);
        }
       
        public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    
        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);
        }
    

    sendMessage 调用了sendMessageDelayed,最终调用了enqueueMessage,进行入队

    Handler#enqueueMessage

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

    最终调用了 MessageQueue 的 enqueueMessage

    MessageQueue#enqueueMessage

        boolean enqueueMessage(Message msg, long when) {
            ...
    
            synchronized (this) {
                ...
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                // 1.满足3个条件则插到链表头部
                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 {
                    // 2.否则插到链表中间,需要移动链表
                    // 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) {
                    //如果需要,调用native方法唤醒
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    

    注释1:p是队列头部,满足3个条件则把消息放到队列头部

    1.队列中没有消息,p==null
    2.入队的消息没有延时
    3.入队的消息的延时比队列头部的消息延时短

    注释2:消息插入到链表中,需要移动链表,对比消息的延时,插入到合适的位置

    好了,消息机制的分析大概就是这些了,接下来结合面试中的问题再回顾一下

    面试中相关的问题

    1.说一下Handler机制

    1.在应用启动的时候,也就是ActivityThread的main方法里面,创建了Looper和MessageQueue,然后调用Looper.loop 开启消息循环
    2.消息循环是这样的,调用MessageQueue的next方法,循环从消息队列中取出一条消息,然后交给Handler去处理,一般是回调handleMessage方法,取不到消息就阻塞,直到下一个消息入队或者其它延时消息到时间了就唤醒消息队列。
    3.消息入队,通过调用handler的sendMessage方法,内部是调用MessageQueue的enqueueMessage方法,进行消息入队,入队的规制是:队列没有消息,或者要入队的消息没有设置delay,或者delay时间比队列头的消息delay时间短,则将要入队的消息放到队列头,否则就插到队列中间,需要移动链表。

    2.发送延时消息是怎么处理的

    这个通过上面的分析应该很容易答出来了

    根据消息队列入队规制,如果队列中没消息,那么不管要入队的消息有没有延时,都放到队列头。如果队列不空,那么要跟队列头的消息比较一下延时,如果要入队的消息延时短,则放队列头,否则,放到队列中去,需要移动链表。

    入队的规制的好处是,延时越长的消息在队列越后面,所以next方法取到一个延时消息时,如果判断时间没有到,就进行阻塞,不用管后面的消息,因为队列后面的消息延迟时间更长。

    ok,关于Handler消息机制的分析到此结束,有问题欢迎留言交流。

    相关文章

      网友评论

        本文标题:(Android面试必知必会系列)Handler消息机制

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