您的位置:首页 > 产品设计 > UI/UE

Android 消息机制之 handler、messageQueue、looper深入剖析

2016-06-22 17:24 645 查看
        大家好,我是听者,耳听心受的听,孙行者的者,感谢大家阅读我的文章。今天继续为大家带来Android消息机制剖析。相信每一个开始接触Android开发的兄弟姐妹们在接触到线程之间通信时对于handler、looper、message、messageQueue都可能会懵逼,至少我是这样的,当拿到毕业证的第二天去某大型企业面试,面试官的第一个问题就是这个,背会了某度查询的结果,果不其然还是挂了,废话不说,直接进入主题。

       首先先谈谈handler,handler是怎么处理消息的呢?内部是怎样实现的呢?handler包含两个重要的成员变量mLooper和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();//给mLooper赋值

        if (mLooper == null) {

            throw new RuntimeException(

                "Can't create handler inside thread that has not called Looper.prepare()");

        }

        mQueue = mLooper.mQueue;//mQueue赋值

        mCallback = callback;

        mAsynchronous = async;

    }

从红色注释的代码中显然可以看出来mLooper和mQueue都是在handler的构造方法中赋值的并且是从looper中得到的,至于Looper.myLooper()是怎么实现的,咱们在这里留一个疑问(疑问一),下来我们再看一段代码,

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

        msg.target = this;//1

        if (mAsynchronous) {

            msg.setAsynchronous(true);

        }

        return queue.enqueueMessage(msg, uptimeMillis);//2

    }

在handler中不管是调用handler.sendmessage()还是调用handler.post()方法其最终是将runnable,看红色代码一是将当前的handler赋值到message的target上带走,红色2是将该message交给messageQueue处理(这里的messageQueue就是handler的成员变量)。这样handler结束了,对,handler就这么简单。

接下来我们看一下messageQueue实现了什么和enqueueMessage()中做了什么。messageQueue从字面意思看是一个消息队列,但是在messageQueue中只包含一个Message类型的mMessage成员变量,那这又是为什么呢?不要着急,我们看一段代码,这是enqueueMessage()中的一段核心代码:

           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 {

                

                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;

                prev.next = msg;

            }

通过红色代码可以看出message的实现是一个队列的实现方式,其包含一个message类型的成员变量next,该next指向列表的下一个节点,而messageQueue的成员变量mMessage是该列表的头节点。再看一下蓝色代码,通过循环遍历改mMessage消息队列,跳出循环的条件是下一个节点为空或者 when < p.when,至于这个when是什么线留个疑问,很明显,当跳出循环以后,p指向的下一个节点,prev指向的是前一个节点,将当前的msg插入二者中间(蓝色代码),没有看懂这个循环大家可以在纸上划拉一下即可。现在我们看一下when到底是什么?第一段代码是将enqueueMessage()传入的when赋给了当前message的when,在看一段代码:

sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)

红色代码是当前系统时间+延迟时间,那么这个when就是指在哪个时间点执行这个message,如果没明白这里留个疑问,接下来我们再看messageQueue的next()方法做了些什么操作。由此可见messageQueue是按照时间的执行先后排序的(结合 when < p.when)。代码:

            final long now = SystemClock.uptimeMillis();

            Message prevMsg = null;

            Message msg = mMessages;

            if (now < msg.when) {

                        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;

                    }

              if (mQuitting) {//当退出的时候返回空,后面用

                    dispose();

                    return null;

                }


这就是messageQueue中next()方法的核心代码,前面我们知道了messageQueue是按照执行时间先后排序的,这里就是先用当前的时间now和第一message的when比较如果还不到其执行时间等待nextPollTimeoutMillis
时间再来执行,next()方法是通过两个native方法阻塞的,至于怎么实现需要看native的代码,而遍历还是通过循环的,全部代码可以看一下源码,当到了该message执行的时间,将其从列表头节点取出来,返回出去。至于返回出去做了什么我们接下来看一下looper。
在介绍Looper之前我觉得有必要和大家分享另一个类threadLocal,ThreadLocal类的诞生给解决多线程并发的一个新思路。下面看一些核心代码:

threadLocal的set方法:

public void set(T value) {//threadLocal的set方法

        Thread currentThread = Thread.currentThread();

        Values values = values(currentThread);

        if (values == null) {

            values = initializeValues(currentThread);

        }

        values.put(this, value);

    }

threadLocal的values()方法:

Values values(Thread current) {

        return current.localValues;

    }

返回一个Thread的成员变量。

initializeValues方法

Values initializeValues(Thread current) {

        return current.localValues = new Values();

    }

从上述代码中可以看出来,threadLocal创建一个values对象,将其赋值到Thread的成员变量localValues中,再讲value值加入到该values中,具体怎么实现不深究,只要了解当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

知道这个概念以后咱们接下来再研究Looper,当为指定线程创建一个looper时大家都知道先调用Looper.prepare(),看一下其实现:

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

    }

new一个Looper对象,并将其添加到sThreadLocal中,再看一下初始化Looper对象时做了哪些操作:

 private Looper(boolean quitAllowed) {

        mQueue = new MessageQueue(quitAllowed);

        mThread = Thread.currentThread();

    }

创建了一个messageQueue对象,得到当前Looper的线程,还记前面handler中的messageQueue吗?就是这里创建的,在这里我们得出一个结论,没有Looper的Thread当创建Handler时会crash。接下来就应该调用Looper.loop(),在loop()方法中又做了哪些事情呢?接下来我们看一下loop()的核心代码:

 for (;;) {

            Message msg = queue.next();

            if (msg == null) {

                return;

            }

            msg.target.dispatchMessage(msg);

           

        }

根据代码可以看出当next返回空的时候looper结束,不为空的时候调用handler.dispatchMessage(msg)方法(还记得msg.target=this,不记得回handler看看)。这样一个消息传递机制就完成了,那什么时候返回空呢?还记得当时在messageQueue的时候那段加粗代码,当退出messageQueue的时候返回null,所以这里要得出一个结论,当我们再子线程中创建了Looper,在确定线程结束的时候要调用looper的quit()。

总结一下Android的消息机制:

step1:在线程中创建一个Looper对象,调用其loop方法形成死循环,读取messageQueue中的消息。

step2:创建一个handler对象。

step3 :在其他线程中调用handler.sendMessage()等方法,将message加入到Looper的messageQueue中,因为loop()是在创建Looper的线程中执行,所以handler的dispatchMessage回到了创建handler对象的线程中执行。完美完成了线程间通信。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: