很容易理解的Android消息机制分析

最近经常使用Handler相关的一些东西。翻了下源码,总结一遍方便回顾。

Android消息机制就是Android的handler机制。

首先是为什么要使用handler?

子线程不能操作ui,因此需要将操作ui的消息传递到主线程,使主线程按照需求更新ui,避免操作不安全。

 

Handler的重要组成部分:Handler Looper MessageQueue ,其中Looper是Handler和messagequeue通信的桥梁。

Handler是消息处理器。MessageQueue是消息队列;Looper是消息循环器。

重要的方法:

Looper.prepare():为当前线程创建一个looper。在该方法内部,使用ThreadLocal进行实现。把new Looper()保存到ThreadLocal。在Looper的构造方法中创建了消息队列。并获取了对应的Thread。

Looper.loop();开启消息循环,在ActivityThread中,自动开启消息循环。

Looper.myLooper() :获取当前线程的looper

looper.mQueue:looper对应的消息队列。

在handler的构造方法中,先获取looper和messagequeue.实现了handler和looper和message的关联。如下:

public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        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;
    }

 

消息机制的理解可以分为以下三个部分

1、消息的插入

2、消息的循环

3、消息的处理

 

1 消息分发和入队

在了解消息消息机制前,先了解下消息获取。通常建议使用obtain进行消息的获取,而不是直接new Message,原始是使用obtain方法可以实现消息复用,里边使用了消息池。代码如下:

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
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();
}

接下来进入正题。我们通常使用sendMessage方法进行handler的消息发送(其他的如sendEmptyMessage、post等,大同小异,只是会对信息进行包装,最终调用的方法都是一样的)。这里先以sendMessage为例,后边顺带讲一下post的操作。

话不多说,上源码,注意里边添加的注释

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);
}

从这里可以看到消息发送最终就是调用了一个消息入队的方法,把message放到消息队列mQueue中,这个消息队列使用looper.mQueue取出,looper是Handler对应的消息循环器,它创建时会创建一个对应的消息队列。是不是很简单,发送消息就是让消息入队。那怎么入队呢?继续走。。。

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循环吧消息插入到消息队列中
            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;
}

省略了方法中我认为不重要的代码,主要关注重点内容。相关的注释已经在代码中添加,可以看到,消息入队很简单,主要是通过时间比对,按照消息需要执行的时间点插入到队列的相应位置。里边️一个需要关注的点,代码中出现了一个方法nativeWake,这个方法通过native调用唤起被阻塞的方法,代码中的mBlocked表示消息循环是否处于阻塞状态。在if分支中,消息插入到队首,此时如果是被阻塞的状态, 则标记needWake为mBloacked(true)。如果是else分支,插入到队列中间,需要使用needWake和p是否是异步决定是否标记needWake为true。如果前边有消息是异步消息,那本次插入则不需要进行唤醒操作,因为前边异步消息插入的时候已经进行了标记。最后根据needWake的值来确认要不要调用nativeWake方法,该方法的作用是进行唤醒操作,唤醒被阻塞的消息获取过程。

插入队列的消息谁来处理呢?答案是Looper,消息循环器。从队列中去消息进行处理,可以想像成一个传送带,MessageQueue就是传送带,handler往上边放消息,Looper取下来处理。Looper通过loop方法进行消息获取,核心源码如下:

/**
 * 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();
    ...
    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 (;;) {
        //next方法是获取消息的关键方法,是一个阻塞方法
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        
        ...
        try {
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
    }

在loop方法中,实现主要功能的代码块就是一个无限for循环操作,用于不停地从队列中获取消息。获取消息是通过MessageQueue的next方法实现的。该方法是一个阻塞方法,且该方法获取的message正常不会为null,如果为null,loop方法return。next方法的核心源码如下。

Message next() {
    // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
    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();
        }
        //该方法的作用是通过natvie调用进行阻塞,如果nextPollTimeoutMillis==1,则进行阻塞
        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方法稍微有点长,主要看其中的注释部分。这里有两个关键点,nextPollTimeoutMillis变量和nativePollOnce方法,nextPollTimeoutMillis变量是一个时间标记量,nativePollOnce的形参,该方法中,如果nextPollTimeoutMillis如果值为-1,会进行阻塞,如果不是-1,正常往下走。真正取消息的地方是synchronized代码块,在这里,首先通过一个while循环找到需要处理的异步消息。这里涉及到了同步屏障机制,简单理解,就是为了保证异步消息优先处理,会插入一个同步屏障,同步屏障消息的特点是targe为null,这样的话,在while循环中就会不停往后寻找,直到找到一个异步消息,拿出来。接下来有两种情况:

1、消息拿到了,需要对比一下当前时间和消息中的时间信息,如果当前时间还没有达到消息要执行的时间点,直接使用时间差赋值给变量nextPollTimeoutMillis。直到时间满足了消息执行的要求。在else分支中,从队列中取出消息。返回给Looper。

2、拿到的消息是null,说明队列中没有异步消息,这时候标记nextPollTimeoutMillis为-1,在下一次循环中通过nativePollOnce方法进行阻塞,阻塞过程中不消耗cpu资源。直到有消息入队,进行阻塞唤醒的操作,还记得前边的方法吗?在MessageQueue的enqueueMessage方法中,消息入队后有个nativeWake方法,便是和这个阻塞方法相对应。即,没有消息时使用nativePollOnce阻塞,消息入队后使用nativeWake唤醒。

再次回到Looper的loop方法,在该方法中,如果取到消息,会调用msg.target.despatchMessage方法进行消息的处理。走到这里基本已经接近消息处理的尾声,这个方法中执行的就是消息对应的真正逻辑。

public void dispatchMessage(Message msg) {
    //callback是通过post方法传过来的runnable,handleCallback方法中其实就是执行runnable的run方法。
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //这个mCallback其实就是构造handler的时候作为参数传进来的handler
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //这里是调用构建handler时实现的handleMessage方法。
        handleMessage(msg);
    }
}

通过上边的代码可以看到despatchMessage方法的执行主要分成三块:

1、执行调用message的callback执行逻辑;

2、调用mCallback执行处理逻辑;

3、调用handleMessage进行逻辑处理。

分别看下

(1)message的callback其实就是我们调用handler的post和postDelay方法中携带的runnable。代码如下:

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

通过代码可以看到,使用post进行消息分发,也是把runnable包装成了一个message进行分发和处理,包装完后执行的也是sendMessage操作,后续流程和sendMessage流程一模一样。postDelay同样。这里不尽兴赘述。

(2)mCallback又是个什么东西呢?

这个是handler构造时带过来的,举个栗子:

private Handler testHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });

构造参数中的callback就是对应的Handler中的mCallback。因此,如果我们不是使用post方法发送的事件,会优先判断该回调是否为空。

(3)如果上述两种情况都为空了,就会执行我们创建Handler时实现的handleMessage方法,这个方法是不是很熟悉。

总结一下,Android消息机制就是一个线程,使用指定线程的Handler把消息扔到消息队列,指定线程的Looper取出消息在对应线程进行处理。

你可能感兴趣的:(Android)