美文网首页
安卓Handler消息机制---总流程解析

安卓Handler消息机制---总流程解析

作者: qpan | 来源:发表于2018-03-25 13:33 被阅读44次

    包括以下内容:

    • Handler:发送及处理消息
    • Looper:分发消息
    • MessageQueue :存储消息
    • Message:消息实体 戳这里看详情
      扩展应用:
    • HandlerThread: 封装好消息机制的线程 戳这里看详情
    • IntentService:开启子线程处理事务的service
    • ThreadLocal:线程本地变量的管理类
    • LocalBroadcastReceiver:内部用handler来管理 戳这里看详情

    一. 背景
    1.1 使用场景
    跨线程发送消息,一般是子线程向主线程发送消息
    1.2 为什么要使用
    安卓的线程机制:只有主线程才可以更改ui
    二.关系图


    uml

    三. 流程解析
    用一个简单的使用handler发送消息的流程来解析下:
    我们使用handler其实主要分三步:

    • 新建一个handler
    • 发送消息
    • 处理消息
      下面从这三部分来解析
      1.初始化 handler
    // 构造函数入参: handler需要同一个looper绑定,默认为当前线程所在looper
                              callback: 回调,处理消息时会用到
    private Handler mhandler = new  Handler(looper,callback){
                   
                    @Override
                    public void handleMessage(Message msg) {
                            
                        }
                };
    
    

    在这里,需要关心的是参数 Looper; 用户可指定一个looper,或者不填;
    不填的情况下是怎么处理的呢:

    1. 获取Looper
      在Handler源码中,当构造函数中未指定looper对象时,会调用:

    mLooper = Looper.myLooper();

    继续查看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();
        }
    

    意思是: 会返回当前线程一一绑定的looper,如果未曾绑定过则返回null;
    那么 怎么绑定呢,在什么时机去绑定呢?
    这得分2种情况:主线程的looper和子线程的looper

    1. 主线程的looper
      这个无需我们过多考虑(想想,我们要在主线程新建一个对应的handler,都是使用无参的构造函数对吧),其实这是因为应用启动时系统帮我们做好了:
    public static void main(String[] args) {
                Looper.prepareMainLooper(); 
      .....
                ActivityThread thread = new ActivityThread(); 
      .....
                Looper.loop(); 
            }
    
    
    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    

    4.子线程的looper
    子线程的需要我们来 自己绑定,答案就在Looper的类解释文案上:

    * <p>This is a typical example of the implementation of a Looper thread,
      * using the separation of {@link #prepare} and {@link #loop} to create an
      * initial Handler to communicate with the Looper.
      *
      * <pre>
      *  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.prepare()

        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的构造函数

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

    可见,在Looper.prepare()里做了两件事:
    给当前线程新建并绑定了一个looper
    生成一个消息队列

    那么looper.loop()做了什么呢?

        /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the loop.
         */
        public static void loop() {
    //这里告诉我们获取某个线程的looper接口: myLooper(); 这个很有用
            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;
    
            // Make sure the identity of this thread is that of the local process,
            // and keep track of what that identity token actually is.
            Binder.clearCallingIdentity();
            final long ident = Binder.clearCallingIdentity();
    
          //从消息队列中取消息,会堵塞
            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 {
                   
                }
                ............
            }
        }
    

    初始化的流程暂告一段落,现在我们明白了 初始化handler时需要同一个looper绑定,并开启looper的循环;

    1. handler 发送一个消息
      可以通过sendMessage() 或者post()来发送消息,这二者其实都最后调用了:
    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);
        }
    

    继续看enqueueMessage()

        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
           //设置消息的处理对象,后续分发时有用
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
          // 加入消息队列中
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    然后再看看 消息队列是怎么入列一个消息的:

     boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
    
            synchronized (this) {
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w(TAG, e.getMessage(), e);
                    msg.recycle();
                    return false;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
    
              //这里是关键!!!
                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 {
                    // 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) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    

    有点长,大概可以概括为: 以单链表的形式插入消息;

    那么接下来系统做什么了呢?消息去哪了? 别急,想想之前的Looper.loop()
    我们把消息扔到某个消息队列,而消息队列是looper里创建的,一一对应,而handler又是和某个looper绑定的,这里的关键就是~~~Looper
    想想Looper.looper()里的for循环吧:

    //从消息队列中取消息,会堵塞
    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 {
    }
    }

    这里会取消息队列里的消息并分发
    其中: msg.target 就是消息的处理器,即某个handler,那么分发就得看

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

    细节就不说了,终于看到熟悉的handleMessage了,也就是说,我们发送的消息终于绕了一圈,又回到了我们的handleMessage了;

    好,收工!(如果感觉很多细节都很模糊,都没涉及到,这是正常滴,因为我都没讲~~~; )

    下面补充下总的流程图:


    流程图.png

    补充下 looper 和 handler,MessageQueue的对应图:
    一个线程只可有一个looper,一个looper可同时绑定多个handler,但一个handler只可绑定一个looper;
    一个looper里初始化一个消息队列,每个消息队列有多个消息,每个消息根据targer可找到对应的handler。


    对应图

    相关文章

      网友评论

          本文标题:安卓Handler消息机制---总流程解析

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