一.Android的消息机制概述

前面提到,Android的消息机制主要是指Handler的运行机制以及所附带的MessageQueue和Looper的工作过程,这三者实际上是一个整体,只不过我们在开发的时候比较接触多的是Handler而已,Handler的主要作用是将一个任务切换到某个指定的线程中去执行,那么Android为什么要提供这种功能呢?这是因为android的UI规范不允许子线程更新UI,否则会抛出异常,ViewRootImpl对UI的操作做了验证,这个验证工作是由ViewRootImpl的checkThread来完成的。

    void checkThread() {        if (mThread != Thread.currentThread()) {            throw new CalledFromWrongThreadException(                    "Only the original thread that created a view hierarchy can touch its views.");        }    }

这个异常相信很多人见过,由于这一点,并且Android又不允许在主线程有耗时的操作,所以我们必须要在子线程中完成耗时后转回到主线程更新某些东西,这里就需要用到Handler了,如果没有Handler,我们的确没有办法切换到主线程,所以,系统提供Handler,主要的原因就是解决在子线程中无法访问UI的矛盾

这里又要说了,系统为什么不允许在子线程访问UI呢?这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那为什么系统不对UI控件访问加上锁机制呢?缺点有两个,首先加上锁后会让UI访问的逻辑变得复杂,其次是会降低UI的访问频率,所以最简单搞笑就是采用单线程模型来处理UI操作,对于开发者来说也不算太麻烦,只需要切换一下即可

Handler的使用方法这里我就不做介绍了,我们来说下他的工作原理,Handler的创建会采用当前线程的Lopper来构建内部的消息循环系统,如果没有,就会报错

如何解决这个问题,只需要为当前线程创建一个looper即可,或者在一个有Lopper的线程中创建Handler也行,后面会讲到

Handler创建完毕后这个时候内部的Lopper以及MeaasgeQueue也可以和Handler一起协同工作,然后通过Handler的post方法将一个Runnable投递到Handler内部的Lopper中去处理,也可以通过Handler的send方法发送一个消息,这个消息同样会在Lopper中去处理,其实post方法最终还是通过send方法来完成的,接下来我们来看下send方法的工作过程,当Handler的send被调用的时候,会他向MessageQueu的enqueueMessage方法将这个消息放入消息队列,然后Lopper发现新消息到来时,就会处理这个消息,最终消息的Runnable或者Handler的handlerMessage方法就被调用,注意Lopper是运行在创建Handler所在的线程中,这样Handler中的业务就会被切换到所在线程就执行了,如图

Android开发艺术探索——第十章:Android的消息机制_第1张图片

二.Android的消息机制分析

1.ThreadLocal的工作原理

ThreadLocal是一个线程内部的数据存储类,通过它可以在执行的线程中存储数据,数据存储后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据,在日常开发中用到ThreadLocal的地方很好,但是在某些特殊的场景下,通过ThreadLocal可以轻松的实现一些看起来很复杂的功能,这一点在android的源码中也有所体现,比如Lopper,ActivityThread以及AMS中都是用到了ThreadLocal,这个不好描述,一般来说,某一个数据是以线程为作用域并且不同线程具有不同的Lopper,这个时候通过ThreadLocal就可以轻松的实现Looper在线程中的存取,如果不采取ThreadLocal,那么系统就必须提供一个全局的哈希表来Handler查找指定线程的Lopper,这样一来就必须提供一个类似于LooperManager的类了,但是系统并没有这么做而是选择了ThreadLocal,这就是ThreadLocal的好处。

ThreadLocal的另一个使用场景是复杂逻辑下的对象传递,比如监听器的传承,有些时候一个线程中的任务过于复杂,这可能表现为函数调用栈比较深以及代码入口的多样性,在这种情况下,我们又需要监听器能够贯穿
整个线程的执行代码,这个时候可以怎么做呢?其实这个时候就采用ThreadLocal,采用ThreadLocal可以让监听器作为线程内的全局对象而存在,在线程内部只要通过get方法就可以获取监听器,如果不采用ThreadLocal,那么我们能想到的如下两个办法,第一个是讲监听器作为参数的形式在函数调用栈中进行传递,第二种方法就是将监听器作为静态变量供线程访问,上述的两种办法都是有局限性的,第一种办法的问题是当函数调用栈深的时候,通过函数参数来传递监听器对象这几乎是不可接受的,这会让程序设计看起来很糟糕,第二种是可以接受,但是这种状态是不具有可扩展性的,比如同时两个线程在执行,那么需要提供两个静态的监听器对象,如果10个就需要10个监听器?这显然是不可思议的,而采取ThreadLocal,每隔监听器对象都有自己的线程内部存储,根本就不存在这个方法2的问题

介绍了这么多ThreadLocal的知识,可能还是有点抽象。下面通过实际的例子来演示ThreadLocal的真正含义,首先定义一个ThreadLocal对象,这里选择Boolean类型的,如下

    private ThreadLocal<Boolean> mBooleanThread = new ThreadLocal<Boolean>();

然后分别在主线程,子线程1和2中访问

        mBooleanThread.set(true);        Log.i(TAG, "主线程:" + mBooleanThread.get());        new Thread("Thread #1") {            @Override            public void run() {                mBooleanThread.set(false);                Log.i(TAG, "Thread #1:" + mBooleanThread.get());            }        }.start();        new Thread("Thread #2") {            @Override            public void run() {                Log.i(TAG, "Thread #2:" + mBooleanThread.get());            }        }.start();

这段代码中,主线程设置了ThreadLocal为true。而在子线程1中设置了false然后分别获取他们的值。这个时候主线程为true,子线程1中应该为false,而子线程2中由于没有设置,所以应该是null

这里写图片描述

虽然在不同线程中访问的是同一个ThreadLocal对象,但是他们通过ThreadLocal获取到的值确实不一样的,这就是ThreadLocal的奇妙之处,结合这个例子然后再看一遍前面对ThreadLocal这个场景的使用和分析,我们应该就能比较好的理解ThreadLocal的使用方法了,ThreadLocal之所以有这么奇妙的效果,是因为不同线程访问同一个ThreadLocal的get,ThreadLocal内部会从各自的线程中取出一个数组,然后从数组中根据当前的ThreadLocal索引查出对应的calue值,很显然,不同线程中的数组是不同的,这就是为什么通过ThreadLocal可以在不同的线程中维护一套数据的副本并且互不干扰。

首先看ThreadLocal的set方法

    public void set(T value) {        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values == null) {            values = initializeValues(currentThread);        }        values.put(this, value);    }

在上面的set方法中会通过values方法来获取当前线程的ThreadLocal数组,其实获取的方法也很简单,在Thread内部有一个成员专门用于存储线程的ThreadLocal数据:ThreadLocal.Values localValues,因此获取当前线程的ThreadLocal数据就变成异常简单了,如果loaclValues内部有一个数组:private Object[]tavle,ThreadLocal的值就存在这个table数组中,下面看一下localValues是如何使用put方法将ThreadLocal的值存储到table数组中的:

       void put(ThreadLocal<?> key, Object value) {            cleanUp();            // Keep track of first tombstone. That's where we want to go back            // and add an entry if necessary.            int firstTombstone = -1;            for (int index = key.hash & mask;; index = next(index)) {                Object k = table[index];                if (k == key.reference) {                    // Replace existing entry.                    table[index + 1] = value;                    return;                }                if (k == null) {                    if (firstTombstone == -1) {                        // Fill in null slot.                        table[index] = key.reference;                        table[index + 1] = value;                        size++;                        return;                    }                    // Go back and replace first tombstone.                    table[firstTombstone] = key.reference;                    table[firstTombstone + 1] = value;                    tombstones--;                    size++;                    return;                }                // Remember first tombstone.                if (firstTombstone == -1 && k == TOMBSTONE) {                    firstTombstone = index;                }            }        }

上面的代码实现了一个数据的存储过程,这里不去分析具体算法,但是我们可以看出一个存储规则,那就是ThreadLocal的值在table数组中的存储位置总是为ThreadLocal的refeence字段所标识的对象的下一个位置,比如ThreadLocal的reference对象在table数组中的索引为index,那么ThreadLocal的值在table数组中的索引就是index + 1 ,最终ThreadLocal的值将会被存储在table数组中,table[index + 1] = vales

我们再来看下get方法

    public T get() {        // Optimized for the fast path.        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values != null) {            Object[] table = values.table;            int index = hash & values.mask;            if (this.reference == table[index]) {                return (T) table[index + 1];            }        } else {            values = initializeValues(currentThread);        }        return (T) values.getAfterMiss(this);    }

可以发现ThreadLocal的get方法逻辑还算是比较清晰,他同样是取当前线程的localValues对象,如果这个对象为null那么就返回初始值,初始值由ThreadLocal的initialValue方法来描述,默认情况下为null,当然也可以重写这个方法,他的默认实现是:

    protected T initialValue() {        return null;    }

如果localValues对象不为null,那就取出他的table数组并找出ThreadLocal的rederence对象在table数组中的位置,然后table数组的下一个位置所存储的数据就是ThreadLocal的值

从ThreadLocal的set和get方法可以看出,他们所操作的独享都是当前线程的localValues对象和table数组,因此在不同线程中访问同一个ThreadLocal的set和get方法,他们对ThreadLocal所做的读写操作仅限于各自内部,这就是为什么ThreadLocal可以在多个线程找那个互不干扰的存储和修改数据,理解了ThreadLocal我们就可以理解Lopper的工作原理了

2.消息队列的工作原理

消息队列在Android中指的是MessageQueue,MessageQueue主要包含两个操作,插入和读取,读取操作本身会伴随着删除操作,插入和读取对应的方法分别为enqueueMessage和next,其中enqueueMessage的作用是往消息队列中插入一条消息,而next的作用是从消息队列中取出一条消息并将其从消息队列中一处,尽管MessageQueue叫消息队列,但是他的内部实现并不是用的队列,实际上它是通过一个单链表的数据结构来维护消息队列,单链表的插入和删除上比较有优势,下面主要看一下他的enqueueMessage和next方法的实现:

   boolean enqueueMessage(Message msg, long when) {        if (msg.isInUse()) {            throw new AndroidRuntimeException(msg + " This message is already in use.");        }        if (msg.target == null) {            throw new AndroidRuntimeException("Message must have a target.");        }        synchronized (this) {            if (mQuitting) {                RuntimeException e = new RuntimeException(                        msg.target + " sending message to a Handler on a dead thread");                Log.w("MessageQueue", e.getMessage(), e);                return false;            }            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 (;;) {                    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;    }

从enqueueMessage的实现中可以看出,他的主要操作就是单链接的插入操作,这里就不一一再过多解释了,下面看一下next方法的实现,next的主要逻辑:

   Message next() {        int pendingIdleHandlerCount = -1; // -1 only during first iteration        int nextPollTimeoutMillis = 0;        for (;;) {            if (nextPollTimeoutMillis != 0) {                Binder.flushPendingCommands();            }            // We can assume mPtr != 0 because the loop is obviously still running.            // The looper will not call this method after the loop quits.            nativePollOnce(mPtr, 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 (false) Log.v("MessageQueue", "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("MessageQueue", "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;        }    }

可以发现next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里,当有新消息到来时,next方法会返回这条消息并将其从单链表中移除

3.Lopper的工作原理

Looper在消息机制中扮演着消息循环的角色,具体来说就是她会不停的从MessageQueue中查看是否有新消息,如果有新消息就会立即处理,否则就会一直阻塞在那里,我们先来看下他的构造方法,在构造方法里他会创建一个MessageQueue,然后将当前线程的对象保存起来:

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

我们都知道,Handler的工作需要Looper,没有Looper的线程就会报错,那么如何为一个线程创建Looper,其实很简单,就是通过Looper.prepare()就可以为他创建了,然后通过Looper.loop来开启循环

        new Thread("Thread #2") {            @Override            public void run() {                Looper.prepare();                Handler mHandler = new Handler();                Looper.loop();            }        }.start();

Looper除了prepare方法外,还提供了parpareMainLooper方法,这个方法主要是给主线程也就是ActivityThread创建Looper使用的,由于主线程的Looper比较特殊,所以Looper提供了一个getMainLooper方法,通过他可以在任何地方获取到主线程的Looper,Looper也是可以退出的,提供了quir和quitSafely来退出一个Looper,二者的区别是:quit会直接退出,但是quitSafely是退出一个Looper,然后把消息队列中已有消息处理完毕后才安全退出,Looper退出后,通过Handler发送的消息会失败,这个时候Handler的send方法会返回false,在子线程中,如果手动为其创建了Looper,那么所有的事情完成以后应该调用quit方法来终止循环,否则子线程会一直处于等待状态,而如归Looper退出以后,这个县城就会立刻终止,因此建议不需要的时候终止Looper

Looper最重要的一个方法是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.recycle();        }    }

Looper的loop方法在工作过程也比较好理解,loop方法是死循环,唯一跳出循环的方法是MessageQueue的next方法返回null,当Looper被quit方法调用时,Looper就会调用MessageQueue的quit和quitSafely方法来通知队列退出,当消息队列被标记为退出状态的时候,他的next方法就会返回null,也就是说,Looper必须退出,否则loop方法就会无限循环下去,loop方法会调用MessageQueue的nect来获取最新消息,而next是一个阻塞操作,当没有消息事,next就会阻塞,这也就导致loop方法一直阻塞在哪里,如果MessageQueue的next返回最新消息,Looper就会处理这条消息:msg.target.dispatchMessage(msg),这里的msg.target是发送这条消息的Handler对象,这样Handler的dispacthManage方法是在创建Handler时所使用的Lopper执行,这样就成功的将代码逻辑切换到指定的线程中去执行。

4.Handler的工作原理

Handler的工作主要是包含消息的发送和接收过程,消息的发送是通过post的一系列和send来实现的,我们来看下:

    /**     * Pushes a message onto the end of the message queue after all pending messages     * before the current time. It will be received in {@link #handleMessage},     * in the thread attached to this handler.     *       * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.     */    public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    }    /**     * Sends a Message containing only the what value.     *       * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.     */    public final boolean sendEmptyMessage(int what)    {        return sendEmptyMessageDelayed(what, 0);    }    /**     * Sends a Message containing only the what value, to be delivered     * after the specified amount of time elapses.     * @see #sendMessageDelayed(android.os.Message, long)      *      * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.     */    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {        Message msg = Message.obtain();        msg.what = what;        return sendMessageDelayed(msg, delayMillis);    }    /**     * Sends a Message containing only the what value, to be delivered      * at a specific time.     * @see #sendMessageAtTime(android.os.Message, long)     *       * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.     */    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {        Message msg = Message.obtain();        msg.what = what;        return sendMessageAtTime(msg, uptimeMillis);    }    /**     * Enqueue a message into the message queue after all pending messages     * before (current time + delayMillis). You will receive it in     * {@link #handleMessage}, in the thread attached to this handler.     *       * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.  Note that a     *         result of true does not mean the message will be processed -- if     *         the looper is quit before the delivery time of the message     *         occurs then the message will be dropped.     */    public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }    /**     * Enqueue a message into the message queue after all pending messages     * before the absolute time (in milliseconds) uptimeMillis.     * The time-base is {@link android.os.SystemClock#uptimeMillis}.     * You will receive it in {@link #handleMessage}, in the thread attached     * to this handler.     *      * @param uptimeMillis The absolute time at which the message should be     *         delivered, using the     *         {@link android.os.SystemClock#uptimeMillis} time-base.     *              * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.  Note that a     *         result of true does not mean the message will be processed -- if     *         the looper is quit before the delivery time of the message     *         occurs then the message will be dropped.     */    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);    }    /**     * Enqueue a message at the front of the message queue, to be processed on     * the next iteration of the message loop.  You will receive it in     * {@link #handleMessage}, in the thread attached to this handler.     * This method is only for use in very special circumstances -- it     * can easily starve the message queue, cause ordering problems, or have     * other unexpected side-effects.     *       * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.     */    public final boolean sendMessageAtFrontOfQueue(Message msg) {        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, 0);    }    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

可以发现,Handler发消息仅仅是向消息队列里插入一条消息,MessageQueue的next方法就会返回这条消息给Looper,最终Handler的dispatchMessage方法就会被调用,这个时候Handler就进入了处理消息的阶段,dispatchMessage的实现如下:

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

首先,他会检查Message的callback是否为null,不为null就通过handlerCallback来处理消息,Message的callback是一个Runnable对象,实际上就是Handler的post方法所传递Runnable参数,handlerCallback的逻辑也很简单。

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

其次是检查mCallback是否为null,不为null就调用mCallback的handlerMessage方法来处理消息,Callback是个接口

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

通过Callback可以采用如下的方式来创建Handler对象,Handler handler = new Handler(callback),那么callback的含义在什么呢?源码里面的注释已经说明,可以用来创建一个Handler的实例单并不需要派生Handler的子类,在日常开发中,创建handler最常见的方式就是派生一个handler子类并重写handlerMessage来处理具体的消息,而Callback给我们提供了另外一种使用Handler的方式,当我们不想派生子类的时候,就可以通过Callback来实现,最后通过调用Handler的handlerMessage方法来处理消息,Handler处理消息的过程

Android开发艺术探索——第十章:Android的消息机制_第2张图片

Handler有一个特殊的构造方法,那就是通过一个特定的Looper来构造Handler,他的实现如下:

    public Handler(Looper looper) {        this(looper, null, false);    }

下面再来看下Handler的默认构造方法public Handler,这个构造方法是调用下面的构造方法,很明显,如果当前线程没有Looper的话,就会抛出Cant create handler inside thread that has not called Looper.prepare 这个异常,这个解释在没有Looper的子线程创建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());            }        }        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;    }

三.主线程的消息循环

Android的主线程就是ActivityThread,主线程的入口为main,在main方法中系统通过Looper.prepareMainLooper来创建主线程的Looper和MessageQueue,并且通过loop来循环

   public static void main(String[] args) {        SamplingProfilerIntegration.start();        // CloseGuard defaults to true and can be quite spammy.  We        // disable it here, but selectively enable it later (via        // StrictMode) on debug builds, but using DropBox, not logs.        CloseGuard.setEnabled(false);        Environment.initForCurrentUser();        // Set the reporter for event logging in libcore        EventLogger.setReporter(new EventLoggingReporter());        Security.addProvider(new AndroidKeyStoreProvider());        Process.setArgV0("");        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        AsyncTask.init();        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }}

主线程开始循环之后,ActivityThread还需要一个Handler来和消息队列交互,也就是H

    private class H extends Handler {        public static final int LAUNCH_ACTIVITY         = 100;        public static final int PAUSE_ACTIVITY          = 101;        public static final int PAUSE_ACTIVITY_FINISHING= 102;        public static final int STOP_ACTIVITY_SHOW      = 103;        public static final int STOP_ACTIVITY_HIDE      = 104;        public static final int SHOW_WINDOW             = 105;        public static final int HIDE_WINDOW             = 106;        public static final int RESUME_ACTIVITY         = 107;        public static final int SEND_RESULT             = 108;        public static final int DESTROY_ACTIVITY        = 109;        public static final int BIND_APPLICATION        = 110;        public static final int EXIT_APPLICATION        = 111;        public static final int NEW_INTENT              = 112;        public static final int RECEIVER                = 113;        public static final int CREATE_SERVICE          = 114;        public static final int SERVICE_ARGS            = 115;        public static final int STOP_SERVICE            = 116;        public static final int REQUEST_THUMBNAIL       = 117;        public static final int CONFIGURATION_CHANGED   = 118;        public static final int CLEAN_UP_CONTEXT        = 119;        public static final int GC_WHEN_IDLE            = 120;        public static final int BIND_SERVICE            = 121;        public static final int UNBIND_SERVICE          = 122;        public static final int DUMP_SERVICE            = 123;        public static final int LOW_MEMORY              = 124;        public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;        public static final int RELAUNCH_ACTIVITY       = 126;        public static final int PROFILER_CONTROL        = 127;        public static final int CREATE_BACKUP_AGENT     = 128;        public static final int DESTROY_BACKUP_AGENT    = 129;        public static final int SUICIDE                 = 130;        public static final int REMOVE_PROVIDER         = 131;        public static final int ENABLE_JIT              = 132;        public static final int DISPATCH_PACKAGE_BROADCAST = 133;        public static final int SCHEDULE_CRASH          = 134;        public static final int DUMP_HEAP               = 135;        public static final int DUMP_ACTIVITY           = 136;        public static final int SLEEPING                = 137;        public static final int SET_CORE_SETTINGS       = 138;        public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;

ActivityThread通过ApplicationThread和AMS进程进程间通信,AMS以
进程间通信的方法完成ActivityThread的请求后回调后者的Binder方法,然后通过H发送消息,H收到消息后将ApplicationThread中的逻辑切换到ActivityThread去执行,这就是切换到主线程去执行,这个过程就是主线程的消息循环模型。

更多相关文章

  1. [Android]ScrollView和ListView套用冲突的解决方法
  2. Android页面去掉标题全屏的方法-第一次用安卓试试看
  3. android 中使文本(TextView 、button等可参考)在屏幕正中心显示的
  4. Android消息机制不完全解析(上)
  5. android屏幕自适应4方法案例整合
  6. 打开SDK Manager检查Android SDK下载和更新失败的解决方法
  7. android studio 3.6.0 绑定视图新特性的方法

随机推荐

  1. Android GPS获取当前位置信息
  2. Android下拉刷新实例
  3. android之listView缓存机制
  4. Android版本差异
  5. Android SDK and AVD Manage图标没找到
  6. android的消息队列机制
  7. Android 版本
  8. 【Android Studio】Migrating From Eclip
  9. Android Learning-Application Fundament
  10. vlc for android 瀹樼綉sample浣跨敤