Android 异步消息处理机制

作者: zhangxiao | 来源:发表于2017-07-27 23:14 被阅读0次

    概述

    Handler无疑是Android的异步消息处理机制的核心,这遍文章将通过分析Handler的源码来系统的解析Android异步消息机制。

    Handler的使用

    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "MainActivity";
        private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
    
                switch (msg.arg1){
                    case 1:
                        if (BuildConfig.DEBUG) Log.d(TAG, msg.getData().getString("firstName"));
                        break;
                    case 2:
                        if (BuildConfig.DEBUG) Log.d(TAG, msg.getData().getString("lastName"));
                        break;
                    default:
                        break;
                }
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Message msg1 = new Message();
                    msg1.arg1 = 1;
                    Bundle bundle1 = new Bundle();
                    bundle1.putString("firstName", "xiao");
                    msg1.setData(bundle1);
                    mHandler.sendMessage(msg1);
    
                    Message msg2 = new Message();
                    msg2.arg1 = 2;
                    Bundle bundle2 = new Bundle();
                    bundle2.putString("lastName", "zhang");
                    msg2.setData(bundle2);
                    mHandler.sendMessage(msg2);
                }
            }).start();
        }
    }
    
    

    打印结果:

    image.png

    问题来了:

    1.消息是怎样传递到handleMessage()方法里面的。
    2.多个消息是如何保证正确的顺序。

    首先看一下Handler()
        public Handler() {
            this(null, false);
        }
    
        public Handler(Callback callback, boolean async) {
           //省略部分代码
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
    跟一下Looper.myLooper()
        /**   
         * 返回当前线程绑定的Looper,如果没有绑定,则返回null
         */
        public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    
    继续跟一下Looper.prepare()
         /** 为当前线程初始化一个Looper
          *在调用prepare()之后,可以创建一个绑定了该Looper的Handler
          * 通过loop()方法,开启循环
          * 通过quit()方法,结束循环
          */
        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));
        }
    

    1."Can't create handler inside thread that has not called Looper.prepare()",表示在初始化Handler之前必须先调用Looper .prepare()。但是通常在使用Handler的时候并没有去调用Looper.prepare(),这是因为在ActivityThread(app启动的时候会初始化)中已经调用了,如果想看一下,可以看ActivityThread类的main()方法,这里点到为止,只要记住一点:在非主线程中初始化Handler,必须先调用Looper.prepare()。
    2."Only one Looper may be created per thread",表示Looper和线程是一对一的。

    接下来我们看一下Handler的sendMessageAtTime(Message msg, long uptimeMillis),sendMessage最终调用的都是该方法
        /**
         * Enqueue a message into the message queue after all pending messages
         * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
         * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
         * Time spent in deep sleep will add an additional delay to execution.
         * You will receive it in {@link #handleMessage}, in the thread attached
         * to this handler.
         * 
         * @param uptimeMillis The absolute time at which the message should be
         *         delivered, using the
         *         {@link android.os.SystemClock#uptimeMillis} time-base.
         *         
         * @return Returns true if the message was successfully placed in to the 
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.  Note that a
         *         result of true does not mean the message will be processed -- if
         *         the looper is quit before the delivery time of the message
         *         occurs then the message will be dropped.
         */
        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);
        }
    

    通过sendMessage将消息存入queue中,并且通过一个时间参数uptimeMillis来表示延时。

    跟一下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;
        }
    

    两个“重点关注”中间的代码表示通过时间参数when(也就是之前传入的uptimeMillis),将msg按照时间顺序插入到queue中。
    接下来,我们通过Looper.loop()看一下msg是怎么被取出的。

        /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the 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;
    
            // 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 (;;) {//--------------------------->(1)
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
                // This must be in a local variable, in case a UI event sets the logger
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                final long traceTag = me.mTraceTag;
                if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
                try {
                    msg.target.dispatchMessage(msg);//---------------------------------->(2)
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
    
                if (logging != null) {
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                }
    
                // Make sure that during the course of dispatching the
                // identity of the thread wasn't corrupted.
                final long newIdent = Binder.clearCallingIdentity();
                if (ident != newIdent) {
                    Log.wtf(TAG, "Thread identity changed from 0x"
                            + Long.toHexString(ident) + " to 0x"
                            + Long.toHexString(newIdent) + " while dispatching to "
                            + msg.target.getClass().getName() + " "
                            + msg.callback + " what=" + msg.what);
                }
    
                msg.recycleUnchecked();
            }
        }
    

    (1) 表示死循环,queue.next()是一个阻塞的方法,一直等待消息。
    (2) msg.target表示对应的Handler。

    跟一下Handler.dispatchMessage(Message msg)
        /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {//msg.callback对应public static Message obtain(Handler h, Runnable callback){...}
                handleCallback(msg);
            } else {
                if (mCallback != null) {//mCallback对应public Handler(Looper looper, Callback callback) {...}
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    如果Msg有初始化一个callback则执行Msg.callback.handleCallback(Message msg),否则进行下面的判断
    如果Handler有初始化一个callback则执行Handler.callback.handleCallback(Message msg),否则执行Handler.handleMessage(Message msg)。

    以上就是从Handler.sendMessage(Message msg)到Handlerd.handleMessage(Message msg)的全部过程。流程图如下
    Handler (1).png

    发送异步消息的其它方式:

    1. Handler的post(Runnable r)方法
        public final boolean post(Runnable r)
        {
           return  sendMessageDelayed(getPostMessage(r), 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);
        }
    
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    最终调用的MessageQueue.enqueueMessage(msg, uptimeMillis)。

    2. View的post()方法
        public boolean post(Runnable action) {
            final AttachInfo attachInfo = mAttachInfo;
            if (attachInfo != null) {
                return attachInfo.mHandler.post(action);
            }
    
            // Postpone the runnable until we know on which thread it needs to run.
            // Assume that the runnable will be successfully placed after attach.
            getRunQueue().post(action);
            return true;
        }
    

    最终是调用上面的Handler.post(runnable r)。

    3. Activity的runOnUiThread()方法
        public final void runOnUiThread(Runnable action) {
            if (Thread.currentThread() != mUiThread) {
                mHandler.post(action);
            } else {
                action.run();
            }
        }
    

    如果当前线程是UI线程,直接运行。如果当前线程不是UI线程,则通过Handler.post(Runnable r)发送到UI线程。

    至此,Handler的解析全部完成:)

    相关文章

      网友评论

        本文标题:Android 异步消息处理机制

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