深入理解Android 消息机制(一)——综述

在日常开发和学习中,我们肯定都会接触到Android消息机制。我们知道,在非UI线程中不能直接更新UI,一般我们都是在非UI线程中通过Handler发送一条消息来更新UI。通过Handler可以将任务切换到Handler所在的线程中。Android消息机制主要是指Handler的运行机制,Handler的运行需要和MessageQueue和Looper配合才能完成。可能有人会问:我在Activity新建一个Handler对象,并没有看到MessageQueue和Looper,也照样可以使用Android消息机制啊。其实在启动应用的时候,在主线程中已创建Looper对象,并开始轮询MessageQueue,判断MessageQueue中是否有消息,具体的方式我们后面会讲解。在了解Android消息机制的细节前,我们最好还是先了解Android消息机制的总体框架,心中有个总体框架图,然后再去深入的了解其内部的具体实现。

一,总体框架

上面提到,Android消息机制中有三个角色,分别是:Handler、Looper、MessageQueue(虽然我们叫消息队列,实际上它的实现是链表),其中MessageQueue是存在于Looper中的,而且每个线程只能有一个Loop而对象,但是可以有多个Handler对象,这也是为什么我们可以在主线程创建多个Handler对象。Handler扮演的是消息的传递者以及消息的处理者,Looper负责消息的轮询,MessageQueue负责存储Handler传递过来的消息。以主线程为例,子线程通过主线程中的Handler对象,传递一条消息,这条消息被传递到Looper中的MessageQueue中,然而,Looper中有个方法会一直轮询,判断MessageQueue中有没有消息,当发现有消息的时候,会根据这条消息的target(Handler对象)属性来判断这条消息交给哪个Handler来处理。整个一个流程就是这样的,给出个流程图帮助大家理解:
这里写图片描述

二,分步解析

我们将上面的流程图分成两步:

1,生成一条消息,通过Handler将消息存入Looper中的MessageQueue中。

通常,我们通过Handler创建一条消息并发送的形式是这样的:myHandler.obtainMessage(UPDATE_TEXTVIEW,msg).sendToTarget();这条语句包含两个操作:创建一条消息:发送这条消息,将消息加入MessageQueue中。当然也可以将连个操作分开写,比如写成这样:

Message ms = myHandler.obtainMessage(UPDATE_TEXTVIEW,msg);
ms.sendToTarget();

两种方式看个人习惯,喜欢哪种就写成哪种吧。我们现在通过源码来看下具体的实现是怎样的。首先是Handler的obtainMessage(int what,Object obj)方法,其他的obtainMessage()都差不多,有兴趣的可以去看下,这里我们只看obtainMessage(int what,Object obj)方法。


public final Message obtainMessage(int what, Object obj)
{
    return Message.obtain(this, what, obj);
}

//obtainMessage(int what,Object obj)会调用Message
//的obtain(Handler h, int what, Object obj)方法,我们看
//下obtain()方法的具体实现
public static Message obtain(Handler h, int what, Object obj) {
    Message m = obtain();
    m.target = h;
    m.what = what;
    m.obj = obj;

    return m;
}

//在obtain(Handler h, int what, Object obj)方法中会创建一
//个消息,然后初始化消息的target、what等属性。这里提下,创
//建Message对象m时并没有直接new一个Message对象,而是通
//过Message.obtain()方法来获得Message对象,这是因
//为Message.obtain()方法会先判断消息池里面有没有消息,这样提高
//消息的复用,避免每次都创建Message对象。

到这,我么可以发现创建消息还有另一种方法,比如:

Message m = Message.obtain(myHandler,UPDATE_TEXTVIEW,msg);
m.sendToTarget();

了解消息的创建后,我们再看下消息是怎样被添加到MessageQueue中的。我们看Message的sendToTarget()方法。

public void sendToTarget() {
    target.sendMessage(this);
}

//在前面我们知道target就是Handler对象,这个对象也是
//在Message.obtain(Handler h,int what, Object obj)方法中
//初始化的,我们看下Handler的sendMessage(Message msg)方法

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);
}
//sendMessage(Message msg)最终会调
//用sendMessageAtTime(Message msg, long uptimeMills)
//方法,在此方法中的mQueue就是我们最终存放消息的地方.我们看
//下mQueue是在哪初始化的,当我们初始化Handler对象,没有传
//入Looper对象时,mQueue是这样被初始化的:

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

/**我们可以看到:mQueue指向的是Looper中的mQueue对象,这也是上面框架图中为什么将MessageQueue画在Looper中。至于具体的mLooper对象是
怎样得到,下一遍文章我会具体讲解,这次我们主要是理清整体的一个框架。好了,接着上面继续,在sendMessageAtTime(Message msg,
long uptimeMills)中会调用enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis),在这个方法中通过queue.enqueueMessage(msg, uptimeMillis)最终才会将消息存入MessageQueue。
**/

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

2,从MessageQueue中取出消息,然后将消息交给特定的Handler对象处理。

在Looper 的loop()方法中,有个for循环语句,将MessageQueue中的表头Message取出,然后将叫给该Message的Handler对象处理。我们看下Looper.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();
    }
}

我们主要关注for()语句中的前半段,”Message msg = queue.next()”从消息队列中取出消息,”msg.target.dispatchMessage(msg)”通过Message的target对象处理这条消息。然后我们看下Handler.dispatchMessage(Message msg)方法。

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

//这里就是分发消息该怎么处理了,如果之前是通
//过Handler.post(Runnable r)的方式创建消息时,比如这样

myHandler.post(new Runnable() {
    @Override
    public void run() {
        //处理消息
    }
});

/**这个时候在dispatchMessage(Message msg)中,“msg.callback != null” 条件就成立,看下handCallback(Message msg)方法你会发现,最终就会调用你创建消息时传递的Runnable的run()方法。而mCallBack变量是通过Handler(Callback callback)、Handler(Callback callback, boolean async)、Handler(Looper looper, Callback callback, boolean async)、Handler(Looper looper, Callback callback)构造方法创建Handler对象时传递进的Callback对象。
**/

//如果 “ msg.callback == null” 和 “ Callback == null” 
//那么久会调用handleMessage(msg)方法,这个方法在Handler中是
//个空方法,所以一般我们会重写这个方法来处理相应的操作(比如在主
//线程中更新UI)。

整个一个流程就是这样了,可能还有几个疑问,Android主线程中的Looper是什么时候被关键,Looper.loop()方法又是什么时候被调用的呢。要回答这个,我们就要看ActivityThread的main()方法,在main()方法中有“Looper.prepareMainLooper();”这条语句就为主线程创建了Looper对象,而“Looper.loop();”就开始轮询MessageQueue了。

三,总结

Android消息机制的整个一个流程就是这样了,大家可以对照上面的图,然后参照源码学习下。在Android消息机制中还有很多细节没有提到,比如message是怎么插入MessageQueue的,其中的算法是怎样的?Looper对象是如何存储的?这些在这都没有深入,以后会单独写篇Looper相关的博文,大家一起交流学习。

猜你喜欢

转载自blog.csdn.net/sd_zhuzhipeng/article/details/51760736