美文网首页Android 进阶之旅
Android 进阶学习(一) 消息机制

Android 进阶学习(一) 消息机制

作者: Tsm_2020 | 来源:发表于2020-11-04 17:43 被阅读0次

    说到android 消息机制离不开Handler , 其他的还有Looper MessageQueue ,以及作为消息载体的Message
    下面我们先来说一下他们每一个的职责,我们通过源码从不同的角度来说一下这个消息机制,

    Message

    Message 作为消息的载体,他自身维护了了一个单链表的消息池,大家在创建Message 的时候并没有直接new ,

        public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }
    

    每次执行Message.obatin 方法的时候, 如果有sPool 则直接使用这个sPool 消息,并将消息池中的下一个消息作为sPool 存储起来,如果没有才重新创建消息

    平时使用的时候大家比较关注的属性是 what arg1 arg2 obj ,其实这些都是一般的属性,并不参与消息的分发和传递, 重要的属性如 target(所谓的Handler) 想必一个非常重要方法Handler.obtainMessage() 大家一点也不陌生,,这个方法创建的Message 就将Message 与Handler 绑定到了一起

    public class Handler{
        public final Message obtainMessage()
        {
            return Message.obtain(this);
        }
    }
    
    public class Message{
      public static Message obtain(Handler h) {
            Message m = obtain();
            m.target = h;
    
            return m;
        }
    }
    

    这样在MessageQueue 轮询消息的时候就可以通过这个target ,来分发消息

    
    pubblic class Looper{
      public static void loop(){
        for (;;) {
          ...
          Message msg = messageQueue.next()///从消息队列中获取消息
          msg.target.dispatchMessage(msg);///利用target 分发消息
          ...
        }
      }
    }
    
    public class Handler{
        public Handler(Callback callback) {
            this(callback, false);
        }
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    }
    

    Message的callbakc 属性的创建是通过Handler.post(Runnable)方法来创建的,那么上面的逻辑也就比较清晰了,也就是如果使用的是Handler.post()的方法时 ,则会优先调用,如果在创建Handler 的时候指定Callback ,那么在收到消息的时候他的优先级会比handleMessage的优先级高

    Looper

    Looper 不断的执行循环,从MessageQueue中将消息拿出,交给handler 处理,想要让一个Looper 开始工作,所需要执行的方法是looper.prepare(); -->looper.loop(); 我们在looper 的prepare()中 看到在looper 是与线程绑定的,而且在创建Looper 的过程中同时创建一个MessageQueue

        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));
        }
        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    

    默认的在主线程中是不需要我们来启动主线程的looper 的,那么他是在什么时候启动的呢,
    在Android 的ActivityThread 中会默认启动MainLooper ,所有我们在主线程中不需要手动开启looper 的循环

    public class ActivityThread {
      public void main(String[] arge){
        ...
        Looper.prepareMainLooper();
        ...
        Looper.loop();
        ...
      }
    }
    

    如果想要在子线程中是用Looper ,我们需要怎么做呢,

      class LooperThread extends Thread {
          public Handler mHandler;
    
          public void run() {
              Looper.prepare();
              mHandler = new Handler() {
                  public void handleMessage(Message msg) {
                      // process incoming messages here
                  }
              };
    
              Looper.loop();
          }
      }
    

    按照上面的方法就开启了子线程的looper循环
    我们在来看一下Looper 的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(); // 获取消息  ,可能阻塞
                if (msg == null) {//如果没有消息则退出循环
                    return;
                }
              ...
                try {
                    msg.target.dispatchMessage(msg);///利用消息的target 来分发消息
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } finally {
                }
                ....
                msg.recycleUnchecked();///如果未超过消息吃的最大个数,将消息回收到消息池,
            }
        }
    

    loop()方法中重要的地方我都标记出来了,
    其实很多人会问Looper.loop() 既然是一个死循环,那么在ActivityThread 中执行死循环 为什么不会导致ANR , 首先我们看一下ActivityThread ,其实他并不是一个Thread,他只是启动了主线程的消息循环

    public final class ActivityThread extends ClientTransactionHandler {
      public static void mian(String[] arge){
            ...
        Looper.prepareMainLooper();
        ...
        Looper.loop();
      }
    }
    

    从代码来看ActivityThread并不是一个线程,大家也可以看到ActivityThread 的main 方法中启动的looper 也不是类似线程中启动的方法,而是执行了Looper.prepareMainLooper(),他的意思就是指定初始化主线程的looper,那么为什么不会阻塞主线程呢,我们看到在loop 的方法中MessageQueue 的next() 方法中的nativePollOnce 让主线程休眠

    Handler

    Handler 作为消息机制的辅助和暴露类,连接了Looper 和 消息,将消息添加到MessageQueue中,并且将消息进行了分发

        public Handler(Callback callback, boolean async) {
            mLooper = Looper.myLooper();///获取当前线程的Looper 
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;///消息队列是这个Looper 的消息队列
            mCallback = callback;
            mAsynchronous = async;
        }
    
        public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    

    Handler 是如何将Message 添加到MessageQueue中的呢,可以看到Message的sendToTarget() 和Handler.post() 的方法最后都是调用到

        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            ///这个MessageQueue 就是创建Handler时获取的与当先线程绑定looper的MessageQueue 
            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);
        }
    

    至于消息分发还是我们在Message 中介绍的

    public class Handler{
        public Handler(Callback callback) {
            this(callback, false);
        }
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    }
    

    MessageQueue

    MessageQueue 是一个消息队列,说是队列,其实他的结构是一个单链表的结构,究其原因是单链表结构有利于数据的插入和删除,他的主要方法就是插入enqueueMessage 和 获取消息 next 这两个方法,我们来分别看一下这两个方法

    先来看一下插入消息的方法

      boolean enqueueMessage(Message msg, long when) {
            synchronized (this) {
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    msg.recycle();
                    return false;
                }
                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;//是否唤醒则根据当前looper是否休眠
                } 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;
                }
                // 唤醒在需要的时候
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    

    我们来分析一下这个插入消息的方法,如果新插入的消息没有延迟,或者执行时间小于消息头的执行时间,将这个消息插入到链表头,

    image.png

    如果新消息的执行时间大于消息头的执行时间,则按照消息的执行时间插入


    image.png

    下面我们再来看一下移出消息的方法

      Message next() {
            final long ptr = mPtr;
            if (ptr == 0) {
                return null;
            }
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            int nextPollTimeoutMillis = 0;///延迟时间
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
                nativePollOnce(ptr, nextPollTimeoutMillis);////设置休眠时间,如果是0则不休眠
                synchronized (this) {
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    //如果存在消息
                    if (msg != null) {
                        if (now < msg.when) {///如果当前时间小于消息头的执行时间,指定休眠时间,
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            ///不需要休眠
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;/// 消息头设置为下一个消息
                            }
                            msg.next = null;
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        ///无限期休眠,知道有新消息加入
                        nextPollTimeoutMillis = -1;
                    }
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
                    if (pendingIdleHandlerCount < 0
                            && (mMessages == null || now < mMessages.when)) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    ///如果没有消息则继续循环,并标记已进入休眠,在for循环下次执行的时候等待
                    if (pendingIdleHandlerCount <= 0) {
                        mBlocked = true;
                        continue;
                    }
                    if (mPendingIdleHandlers == null) {
                        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                    }
                    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
                }
                for (int i = 0; i < pendingIdleHandlerCount; i++) {
                    final IdleHandler idler = mPendingIdleHandlers[i];
                    mPendingIdleHandlers[i] = null; 
                    boolean keep = false;
                    try {
                        keep = idler.queueIdle();
                    } catch (Throwable t) {
                    }
                    if (!keep) {
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }
                pendingIdleHandlerCount = 0;
                nextPollTimeoutMillis = 0;
            }
        }
    

    在插入的时候已经拍好对了,在取出的时候就按照链表结构来取,

    我们再来看下面代码来分析一下消息机制

      class LooperThread extends Thread {
          public Handler mHandler;
    
          public void run() {
              Looper.prepare();
              mHandler = new Handler() {
                  public void handleMessage(Message msg) {
                      // process incoming messages here
                  }
              };
              Looper.loop();
              mHandler.obatinMessage(1).sendToTarget();
          }
      }
    

    Looper.prepare(); 会在当前线程创建一个Looper 和消息队列,
    new Handler 会通过Looper.mylooper(),获取当前线程的looper,通过持有Looper 来间接拥有MessageQueue,
    Looper.loop()会开启当前线程的无线循环,由于没有消息, 会在loop() 方法中的MessageQueue的next()方法通过nativePollOnce 让当前线程休眠,
    mHandler.obatinMessage(1).sendToTarget();应该拆分为2步,
    第一步是创建消息,也就是mHandler.obatinMessage(1) ,从消息池中取出消息并与mHandler 绑定,也就是为msg设置target,
    第二步是发送消息,也就是Message.sendToTarget(),利用msg.target.将消息加入创建Handler时looper所持有的MessageQueue中,由于这个消息是没有延迟的,所有msg.when=0,所以插入的位置是MessageQueue 的链表的头部,并执行nativeWake 唤醒MessageQueue的next 方法,
    MessageQueue 的next 方法会将这个消息从链表中移出,将下一个消息置位链表头,并返回给looper ,looper 通过msg.target 来分发消息,

    最后关于主线程不会因为Looper.loop()里的死循环卡死的问题还涉及到很多问题,还涉及到Process/Thread,Android Binder IPC,Linux pipe/epoll机制,我会在所有相关知识点讲完后再来总结一下

    相关文章

      网友评论

        本文标题:Android 进阶学习(一) 消息机制

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