转载请注明:javascript:void(0)
近期在找实习。笔试的时候有一道关于android消息机制的题目。曾经也看过一些这方面的东西。可是没有能够得总结过。今天看了任玉刚的《android开发艺术探索》,趁热打铁,总结一下android消息机制。

Android消息机制的主要作用是任务切换,将某个任务切换到指定的线程中运行。

从应用的角度来说,主要是为了解决UI线程单线程机制的限制。系统角度(接触不多,感觉实用于AMS调度Activity的作用)。
Q:UI线程为什么设计为单线程?
A:UI控件的操作不是线程安全的,对于多线程并发訪问的时候。假设使用加锁机制会导致:
1、UI控件的操作变得非常复杂。
2、加锁的操作必然会导致效率下降。


所以android系统在UI操作上使用单线程机制。
切入正题
Android消息机制主要包含:message、handler、Looper。

Message

当中message比較简单。仅仅作为消息的载体,可是它当中的一些属性不容忽略。比如target、callback。

另一些其它属性。比如:arg1、arg2、when、what、data、obj。

arg1、arg2、what为int型。when为long型;data为bundle。obj为Object。

Handler

Handler为消息操作者。主要用于发送和处理的message。发送有两种方式:
1、通过直接发送message。即sendMessage();
2、通过发送Runnable接口,即Post(Runnable)这样的方式终于会调用sendMessageDelayed

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

而在sendMessageDelayed()中。第一个參数getPostMessage(r)。我们来看看这里发生了什么

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

在getPostMessage(r)方法中,首先生成了一个message对象。同一时候为message的callback属性设置为post(Runnable r)传入的runnable接口。

通过无论哪种发送方式终于要将调用到handler的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);
    }

再来看看 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);
    }

在这种方法中,为message的target设置了值,值为发送此消息的handler(这里为以下消息处理做好了一些准备工作,同一时候确保了消息由哪个handler发送,之后处理的时候也是那个handler处理)。再往下看queue.enqueueMessage(msg, uptimeMillis),顾名思义,就是将message发送到Looper维护的MessageQueue队列中(不是真实的队列实现,通过单链表实现)。

处理消息的过程在以下详细说明。

Looper

Looper是消息机制中不可缺少的成分。它内部维护着一个消息队列MessageQueue,在实例化Looper的时候就会创建消息队列,一个线程中仅仅能有一个Looper和一个消息队列的问题了(当然得知道一个线程中仅仅能创建一个Looper,回答这个问题须要看看Looper.prepare()这种方法。同一时候了解ThreadLocal这个类)
看一下源码

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

Looper.prepare()中调用内部私有的静态方法prepare()。当中sThreadLocal的类型是ThreadLocal。

ThreadLocal类型主要应用于当某些数据是以线程为作用域而且不同线程中有不同的数据副本的场合。这里的Looper恰巧适用于这样的场合,通过prepare()确保了Looper在某个线程中仅仅能实例化一个对象,而且保存在sThreadLocal中。

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

而实例化Looper时,实例化了MessageQueue,从这里解释了上面一个线程中仅仅能有一个Looper和一个消息队列的问题。

在消息机制中Looper担当着消息的储存与转发的工作。通过handler发送的消息终于都存储在Looper的队列中。
Q:handler是怎么和MessageQueue建立的联系呢?
A:handler的构造函数有非常多,例如以下:

android消息机制_ide

可是终于会调用的是以下两个:

public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
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());
            }
        }

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

无论调用哪一个,总而言之,基本的任务就是初始化值。为handler中的mLooper、mQueue 、mCallback 等赋初值。
通过实例化handler就将handler和MessageQueue、Looper建立了联系。
友情提醒:
1、假设在没有Looper的线程中创建Handler,就会报”Can’t create handler inside thread that has not called Looper.prepare()”的错误,解决方法就是在创建handler之前先调用Looper.prepare()方法。


2、创建handler的时候我们总是使用handler的派生类,事实上向handler中传入callBack接口也能够实现处理消息的功能。

public interface Callback {
        public boolean handleMessage(Message msg);
    }

上面说了一下handler通过sendMessageAtTime方法将消息插入到。Looper中维护的MessageQueue队列中。接下来主要看看Looper是怎样取出消息,并将消息转交给handler处理。
Looper中一个重要的方法就是loop()。仅仅有调用了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.");
        }
        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;
            }
            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            msg.target.dispatchMessage(msg);
            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();
        }
    }

我们挑重要的看,首先looper方法中开启了一个死循环,不断的从MessageQueue 中获取消息
Message msg = queue.next(); // might block
queue.next()这种方法是堵塞操作。
获取到了message之后loop方法中调用 msg.target.dispatchMessage(msg),进行了转发工作。从上文的
enqueueMessage方法中看出这里的 msg.target就是当初发送这个 msg的handler。

接下来看看handler是怎么通过dispatchMessage处理消息的。

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

这里有两个callback须要解释下
msg.callback:这个callback是handler通过post(Runnable r)方法发送消息时。间接通过getPostMessage(r)将r这个Runnable接口赋值给message的callback。
mCallback:这个callback从类型上不同于上一个。它是一个接口,详细例如以下:

 public interface Callback {
        public boolean handleMessage(Message msg);
    }

是实例化handler时,能够选择性传入的參数值。

再看看handleCallback内部发生了什么

private static void handleCallback(Message message) {
        message.callback.run();
    }

代码非常easy,就是运行Runnable的run方法。


经过分析结合handler的dispatchMessage方法。不难发现handler处理消息的流程例如以下:

android消息机制_消息机制_02

至此消息机制介绍的几乎相同了。


最后用一张图来说明Message、Handler、Looper之间的关系。


android消息机制_android_03

错误之处。请大家指正。