1. 简介

Android消息处理机制实际上就是Handler的运行机制,除了Handler之外,我们还需要一个消息队列MessageQueue,用于存放Handler发送过来的Message。然后,Android中的Looper类提供了两个方法:prepare()loop(),前者用来创建MessageQueue,后者用来循环遍历MessageQueue并取出Message交给Handler处理。
1) Message:消息实体。
2) MessageQueue:消息队列。
3) Looper:创建消息队列,循环遍历消息队列。
4) Handler:发送、处理消息实体。

2. 子线程消息处理机制

我们知道更新UI的操作都是在UI主线程完成的,UI主线程有自己的消息队列,它是在应用进程的入口方法里进行初始化的。如果我们想自己的线程(非UI线程)有消息队列,该怎么做呢?其实很简单,分为三步:创建属于自己的MessageQueue,Handler绑定到当前线程,开始消息循环。我们先看前两步的具体实现,消息循环在下面会讲到。

Handler mSubHandler;class MyRunnable implements Runnable{        @Override        public void run() {           Looper.prepare();            mSubHandler = new Handler(){                @Override                public void handleMessage(Message msg) {                    super.handleMessage(msg);                }            };            Looper.loop();        }    }

1)初始化消息队列Looper.prepare()

调用Looper.prepare()方法,接着会调用prepare(boolean quitAllowed)方法,我们来看看该方法主要干了什么工作。

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

从上面的代码可以看出prepare主要做了两件事:
(1)在给sThreadLocal设值时,先判断了该变量是否为null,如果不为null的话,会抛出一个运行异常,即每一个线程只能创建一个Looper对象,这说明prepare方法在一个线程中只能被调用一次
(2)new一个Looper对象然后赋给一个ThreadLocal(备注:ThreadLocal是线程局部变量,会为每一个使用该变量的线程提供一个副本)的静态变量sThreadLocal
接下来我们看看new Looper(quitAllowed)方法主要做了什么工作:

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

从上面的代码可以看出Looper同样做了两件事:
(1)初始化了一个消息队列mQueue,quitAllowed代表该消息队列可以终止。
(2)将当前线程赋给mThread。

2)创建一个Handler对象

这里我们是在子线程里初始化的Handler,因此Handler里的消息队列是和子线程的消息队列绑定的,下面我们来看看Handler的构造函数:

public Handler(Callback callback, boolean async) {        if (FIND_POTENTIAL_LEAKS) {            final Class<? extends Handler> 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());            }        }        //初始化Handler的Looper变量        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        //初始化Handler的MessageQueue变量        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

从上面的构造函数我们可以看出端倪,Handler的两个成员变量mLooper,mQueue,分别引用当前线程的Looper和MessageQueue,所以Handler其实是和子线程的消息队列绑定的。从上面的代码,我们也可以看出,在子线程创建Handler之前,一定要执行Looper.prepare()方法,否则会抛出异常。
那么问题来了,我们在Activity、Dialog等类里面是直接创建Handler对象,在此之前也没有调用Looper.prepare()方法,为什么创建Handler没有抛出异常,而且也可以用Handler发送消息呢?下面,我们就看看UI主线程的消息循环是怎么处理的。

3. UI主线程消息处理机制

App进程有一个程序入口,是在ActivityThread类里面的main方法,我们来看看该方法的源码(部分源码),看完之后,我们就会知晓上面的问题为什么没有出现。

public static void main(String[] args) {        ...        //1.  初始化UI主线程消息队列        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        //2.  创建一个UI主线程的Handler        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        // End of event ActivityThreadMain.        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);       //3.  UI主线程开始消息循环        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

原来系统帮我们把这些事情已经做了, Looper.prepareMainLooper()即初始化UI主线程的消息队列,Looper里有一个Looper类型的变量sMainLooper,是UI主线程的Looper,sMainLooper同样在sThreadLocal变量里保存有一个副本。
我们在Activity、Fragment创建Handler都是在UI主线程里面创建,而进程的程序入口里,系统帮我们执行了Looper.prepareMainLooper()方法(当然我们自己在UI主线程也不能调用Looper.prepareMainLooper()方法,prepare方法在一个线程中只能被调用一次),因此我们自己在创建Handler和用Handler发送消息都没有问题。

4. MessageQueue消息的接收

在Android开发中,我们经常会把一些耗时的操作,如网络请求,读取IO等操作放到一个子线程里面进行,如果更新UI需要用到这些耗时操作的结果,我们往往通过Handler(UI主线程的Handler)发送一个Message,然后在Handler里面更新UI(备注:Android规定只能在UI主线程里面更新UI,否则会抛出异常)。
View的post方法、postDelayed方法,runOnUiThread方法最终都是通过Handler来往UI主线程的消息队列里面添加消息的。
Handler有两个方法sendMessageDelayedsendMessageAtTime,这两个方法是主要用来向MessageQueue添加消息。
sendMessageDelayed:

public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }

sendMessageAtTime:

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

sendMessageDelayed方法最终也是去调用sendMessageAtTime方法,但是uptimeMillis参数传入的值是:SystemClock.uptimeMillis() + delayMillis,也就是额外地加上了SystemClock.uptimeMillis()这么一个时间,下面我们来看看SystemClock.uptimeMillis()的具体含义。

    /**     * Returns milliseconds since boot, not counting time spent in deep sleep.     *     * @return milliseconds of non-sleep uptime since boot.     */    native public static long uptimeMillis();

从uptimeMillis方法的注释我们知道,返回的是从开机到现在的时间,除去手机休眠的时间,即CPU休眠、屏幕休眠、设备等待外部输入的时间。下面我们主要看看Handler的enqueueMessage(queue, msg, uptimeMillis)方法和MessageQueue的enqueueMessage方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

msg.target = this,target是一个Handler对象,该语句是将Handler绑定到Message上,当Looper的消息循环最终调用到msg.target.dispatchMessage()时,会间接调用到handler的handleMessage()函数,从而对消息进行实际处理

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) {                // 有消息插入到链表头部,如果此时队列阻塞,则需要调整队列的唤醒时间                msg.next = p;                mMessages = msg;                needWake = mBlocked;            } else {                // 根据when的大小在链表消息队列里插入当前消息                // 如果对头是一个栅栏消息而且插入的msg是一个异步消息,则需要调整队列的唤醒时间                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()) {                     //如果msg是异步消息,但是它不是链表的第一个异步消息,则不唤醒                        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的大小在MessageQueue中找到合适的位置,插入Message节点而已,MessageQueue是一个链表,并且是按时间进行排序的,所以插入操作主要是在比Message的when值。
MessageQueue的对头节点对应着最先将被处理的消息,如果Message被插到链表的头部了,说明队列的唤醒时间需要被调整了,因此,needWake会被设为true。

栅栏概念

上面的代码中还有一个“栅栏”的概念这里需要解释一下,所谓“栅栏”,其实是一种特殊的Message,它的target为null,只能通过调用MessageQueue的postSyncBarrier()来加入消息队列。
“栅栏”就像一个卡子,卡在消息链表中的某个位置,当从消息队列中取到“栅栏”消息时,此时不会再往下取出同步Message,只会取出下一个异步Message,如果想一个Message变成异步Message,只需调用一下Message的setAsynchronous()即可。
在Android的消息机制里,同步Message和异步Message区别就在这,如果消息队列中没有设置“栅栏”的话,那么处理同步Message和异步Message就没什么区别了。
备注:postSyncBarrier()和removeSyncBarrier(int token)方法是成对出现的,removeSyncBarrier(int token)方法是移除“栅栏”

5. 消息循环

当我们调用Looper的loop()方法,该方法会调用MessageQueue的next方法,这样,消息循环就开始了,下面结合源码进行说明。

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(); // might block            if (msg == null) {                return;            }        ...//省略非关键代码            msg.target.dispatchMessage(msg);        ...//省略非关键代码            msg.recycleUnchecked();        }    }

1)Loop()方法很简单,就是一个死循环,然后不断地从消息队列里取出Message,通过Message绑定的target即Handler进行分发处理。
2)当调用了Looper.quit()方法后,需要等待消息队列里面所有的消息处理完毕后,此时queue.next()方法会返回null,loop()循环就直接return结束了。

next():

Message next() {        // 以下两种情况会return:        //1.  消息队列执行了quit方法,而且消息队列被处理过了。        //2. 消息队列执行了quit方法,如果再次调用loop()方法。        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);            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;                }                // Process the quit message now that all pending messages have been handled.                if (mQuitting) {                    dispose();                    return null;                }                // If first time idle, then get the number of idlers to run.                // Idle handles only run if the queue is empty or if the first message                // in the queue (possibly a barrier) is due to be handled in the future.                if (pendingIdleHandlerCount < 0                        && (mMessages == null || now < mMessages.when)) {                    pendingIdleHandlerCount = mIdleHandlers.size();                }                if (pendingIdleHandlerCount <= 0) {                    // No idle handlers to run.  Loop and wait some more.                    mBlocked = true;                    continue;                }                if (mPendingIdleHandlers == null) {                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];                }                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);            }            // Run the idle handlers.            // We only ever reach this code block during the first iteration.            for (int i = 0; i < pendingIdleHandlerCount; i++) {                final IdleHandler idler = mPendingIdleHandlers[i];                mPendingIdleHandlers[i] = null; // release the reference to the handler                boolean keep = false;                try {                    keep = idler.queueIdle();                } catch (Throwable t) {                    Log.wtf(TAG, "IdleHandler threw exception", t);                }                if (!keep) {                    synchronized (this) {                        mIdleHandlers.remove(idler);                    }                }            }            // Reset the idle handler count to 0 so we do not run them again.            pendingIdleHandlerCount = 0;            // While calling an idle handler, a new message could have been delivered            // so go back and look again for a pending message without waiting.            nextPollTimeoutMillis = 0;        }    }

1)next()函数的for循环并不是起循环地去取Message,而是为了合理地处理当前要处理Message需要等待的时间,其中会计算一个较精确的等待时间(nextPollTimeoutMillis),然后下一个循环时调用nativePollOnce(mPtr, nextPollTimeoutMillis),进入阻塞状态,直到等待的时间到了,就直接返回这个Message。
2)next()里另一个要说的是Idle Handler,当消息队列中没有消息需要马上处理时,会判断用户是否设置了Idle Handler,如果有的话,则调用Idle Handler的queueIdle()函数去处理mIdleHandlers中所记录的Idle Handler。
备注:Idle Handler的处理之后在第一次消息阻塞的时间间隙执行,因为执行一次后pendingIdleHandlerCount置成0了

6. quit、quitSafely方法

Looper中有两个方法,quit和quitSafely方法,最后都会调到MessageQueue的quit方法:

void quit(boolean safe) {        if (!mQuitAllowed) {            throw new IllegalStateException("Main thread not allowed to quit.");        }        synchronized (this) {            if (mQuitting) {                return;            }            mQuitting = true;            if (safe) {                removeAllFutureMessagesLocked();            } else {                removeAllMessagesLocked();            }            // We can assume mPtr != 0 because mQuitting was previously false.            nativeWake(mPtr);        }    }

quit方法:

mQueue.quit(false);说明:移除MessageQueue的所有消息,包括延迟消息。

quitSafely方法:

mQueue.quit(true);说明:只移除MessageQueue的延迟消息。

上面我们提到过调用Looper的quit()方法后,loop循环就直接结束了,这是因为在next()方法有几行代码是这样写的:

 if (mQuitting) {        dispose();        return null;  }

说明:
1)调用Looper的quit方法后,mQuitting被设置成true,然后直接返回null,queue.next()方法取到null然后就直接return了,loop循环就结束了

2)主线程的Looper的消息队列的mQuitAllowed被设成false(mQuitAllowed是一个final类型,在构造函数被初始化),因此该代码不会被执行

7. 消息的处理过程

msg.target.dispatchMessage(msg)的处理过程用下面的流程图进行说明:

Android-Message处理流程图.png

附:dispatchMessage方法源码

 /**     * Handle system messages here.     */    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

总结

以上是自己对于Android消息处理机制的一些理解,欢迎大家指正。

更多相关文章

  1. Android——使用 Broastcast 实现进程间通讯
  2. Android应用程序获取ROOT权限的方法
  3. Android(安卓)Framework AIDL的使用
  4. Android新线程中更新主线程UI中的View方法汇总
  5. Android(安卓)进程间通信 实例分析
  6. Android(安卓)ContentProvider和Uri详解
  7. Android(安卓)更新UI的两种方法——handler和runOnUiThread()
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. android 获取屏幕高度,宽度,状态栏高度
  2. Android(安卓)之 Looper、MessageQueue、
  3. (译)Android(安卓)性能优化总览
  4. Android仿QQ主界面-------完善篇
  5. Android(安卓)ROM 开发技能图谱
  6. android 学习八 android selector的使用
  7. Android(安卓)TextView文字横向自动滚动(
  8. Android自己动手实现下拉刷新控件(1)----典
  9. React Native Android(安卓)ScrollView
  10. Android(安卓)网络安全配置