1.Android音讯机制概述

1.1. Android音讯机制是什么?

Android音讯机制首要是指Handler的运行机制以及Handler所顺便的MessageQueue和Looper的作业工程,这三者实践上是一个整体,只不过咱们在开发进程中比较多的接触到Handler罢了。

1.2. Handler的首要效果

Handler的首要效果是将一个任务切换到Handler地点的线程中履行。比如咱们常常碰到的一个场景:Android建议不要在主线程中进行耗时操作,否则会导致程序无法呼应(ANR),那么咱们会在子线程中进行耗时的网络请求和I/O操作。当耗时操作完结后需要更新UI,由于Android开发标准的约束,咱们不能在子线程中拜访UI,否则会程序反常,这个时分就需要经过Handler将任务切换到主线程进行UI更新操作。

对于拜访UI只能在主线程中进行,否则程序会抛出反常这个验证作业是由ViewRootImpl的checkThread办法来完结的,如下所示:

    void checkThread(){
        if (mThread != Thread.currentThread()){
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views."
            );
        }
    }
  • 这儿延伸一个问题,体系为什么不允许在子线程中拜访UI?

体系为什么不允许在子线程中拜访UI呢?这是由于Android饿UI控件不是线程安全的,假如在多线程中并非拜访可能会导致UI控件处于不行预期的状况,那为什么体系不对UI控件的拜访加上锁机制呢?缺陷有两个:首要加上锁机制会让UI拜访的逻辑变得复杂;其次锁机制会降低UI拜访的效率,由于锁机制会堵塞某些线程的履行。鉴于这两个缺陷,最简单高效的办法便是选用单线程模型来处理UI操作,对于开发者来说也不是很费事,只是需要经过Handler切换一下UI拜访的履行线程即可。

2. MeesageQueue的作业原理

MessageQueue首要包括两个操作:刺进和读取,分别对应MessageQueue的enqueueMessage和next办法。Handler经过send办法发送一条音讯后,终究会调用MessageQueue的enqueueMessage办法,enqueueMessage办法会将这条音讯刺进到音讯行列中。而next办法的效果是从音讯行列中取出一条音讯并将其从音讯行列中移除。

2.1. enqueueMessage办法剖析

咱们先来剖析下刺进操作的实现,也便是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) {
                // 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;
    }

从enqueueMessage的实现来看,它的首要操作是单链表的刺进操作。链表的刺进操作是依照when的大小进行排序,when值小的message排在前面:

if (p == null || when == 0 || when < p.when):

  • p == null:表明当时链表没有音讯

  • when == 0:表明新Message的when为0

  • when < p.when:表明新Message的when小于链表首结点Message的when

  • 假如是上述三种状况,则直接将新的Message刺进到链表首部。

  • 假如p == null || when == 0 || when < p.when为false,则会开端遍历链表,获取链表中的下一个Message与新Message的when值比较,假如满意p == null || when < p.when则完毕循环,将新Message刺进。

2.2. next办法剖析

接着咱们看下如何从音讯行列中取出音讯,也便是next办法的实现,源码如下:

    Message next() {
        //...省掉无关代码
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (; ; ) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            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;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    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.
                        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;
                }
                //...
            }
            //...
        }
    }

能够发现next办法是一个无限循环办法,假如音讯行列中没有音讯,那么next办法会一向堵塞在这儿。当有新音讯到来时,next办法会返回这条音讯并将其从单链表中移除。

3. Looper的作业原理

Looper在Android音讯机制中扮演着音讯循环的角色, 它会不停的从MessageQueue中查看是否有新音讯,假如有新音讯就会立刻处理,否则就一向堵塞在那里。对于Looper咱们需要剖析下它的创立进程以及它是如何轮询音讯的。

3.1. Looper的创立

Handler的作业需要Looper,假如当时线程没有Looper目标线程就会报错,那么如何为当时线程创立Looper呢?其实很简单,经过Looper.prepare()即可为当时线程创立一个Looper目标,接着经过Looper.loop()来敞开音讯循环,如下所示:

    new Thread("Thread#1"){
        @Override
        public void run() {
            super.run();
            //创立Looper目标
            Looper.prepare();
            Handler handler = new Handler();
            //敞开音讯轮询
            Looper.loop();
        }
    }.start();

咱们先来剖析Looper的prepare办法,看看Looper目标是怎样创立以及存取的,代码如下:

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    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));
    }
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

剖析上面的prepare办法能够知道,Looper目标是经过ThreadLocal实现在线程中存取的,换句话说便是Looper的效果域是线程,每个线程都有各自的Looper目标。并且当时线程创立Looper后,假如再次调用perpare办法,会抛出RuntimeException反常,所以每个线程都只要一个Looper目标。

下面咱们再看下Looper的构造办法,在构造办法中它会创立一个MessageQueue(音讯行列),然后将当时线程的目标保存起来,如下所示:

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

3.2. loop办法剖析

Looper最重要的一个办法是loop办法,只要调用了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.");
        }
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }
        me.mInLoop = true;
        // 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 = me.mQueue.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);
            }
            msg.target.getTraceName(msg);
            // 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();
        }
    }

Looper的loop办法是一个死循环,唯一跳出循环的办法是MessageQueue的next办法返回了null。当Looper的quit办法被调用时,Looper就会调用MessageQueue的quit或者quitSafely办法来告诉音讯行列退出,当音讯行列被标记为退出状况时,它的next办法就会返回null。也便是说,Looper必须退出,否则loop办法就会无限循环下去。loop办法会调用MessageQueue的next办法来获取新音讯,而next是一个堵塞操作,当没有音讯时,next办法会一向堵塞在那里,这也导致loop办法一向堵塞在那里。

假如MessageQueue的next办法返回了新音讯,Looper就会处理这条音讯:msg.target.dispatchMessage(msg),这儿的msg.target是发送这条音讯的Handler目标,这样Handler发送的音讯终究又交给它的dispatchMessage办法来处理了。可是这儿不同的是,Handler的dispatchMessage办法是在创立Handler时所使用的Looper中履行的,这样就功地将代码逻辑切换到指定线程中去履行了。

4. Handler的作业原理

Handler的首要作业是发送和接收音讯。 Handler能够经过post的一系列办法或send的一系列办法来发送音讯,当然终究都是经过sendMessageAtTime办法来发送音讯的,在sendMessageAtTime办法中,经过调用MessaageQueue的enqueueMessage将音讯刺进到音讯行列中。Handler接收音讯是在handleMessage办法中,Looper查询到新音讯后,终究会调用Handler的handleMessage办法,这样音讯终究又回调到了handleMessage放中。

4.1. Handler发送音讯

Handler提供了post的一系列办法或send的一系列办法来发送音讯,如下所示:

    public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
    public final boolean postDelayed(Runnable r, int what, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
    }
    public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }
    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

能够看到,Handler提供的post系列办法和send系列办法终究都是调用了sendMessageAtTime办法,而sendMessageAtTime办法终究调用了MessageQueue的enqueueMessage办法向音讯行列中刺进了一条音讯。

至此,Handler发送音讯进程就完结了。

4.2. Handler处理音讯

在剖析Looper的时分,咱们说到Looper的loop办法中,假如有新音讯会交给Handler处理,即Handler的dispatchMessage办法会被调用,dispatchMessage源码如下:

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

在dispatchMessage办法中,首要查看Message的callback是否为null,不为null就经过handleCallback来处理音讯。Message的callbakc是一个Runnable目标,实践便是Handler的post办法所传递的Runnable参数。

    //callback便是post办法所传递的Runnable目标
    //public final boolean post(@NonNull Runnable r) {
    //   return  sendMessageDelayed(getPostMessage(r), 0);
    //}
    private static void handleCallback(Message message) {
        message.callback.run();
    }
    ```
-   其次,查看mCallback是否为null,不为null就调用mCall的handleMessage办法来处理音讯,Callback是个接口,它的定义如下:
-   ```
    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }

经过Callback能够选用如下办法来创立Handler目标,Handler handler = new Handler(callback)。那么Callback的意义是什么呢?源码里面的注释现已做了说明:能够用来创立一个Handler的实例但并不需要派生Handler的子类。

最后,调用Handler的handleMessage办法来处理音讯。