引言

由于Android对消息机制的封装,开发者在平常的开发过程中,直接使用Handler对象就能满足大部分的应用场景,是否了解Android的消息机制对开发来说并没有太大的影响。但Android的消息机制对开发者来说还是有很大启发的,因为这里面有完整的异步消息处理机制以及Android的设计思路,有很大参考价值。
在最开始接触Android的时候就了解了Android的消息机制,但在使用过程中总是选择性的忽视一些问题:

  • Handler具体是如何实现跨线程通信的?
  • Handler中post的一系列方法与send的一系列方法有什么关系与不同?
  • Handler是如何与线程中的Looper进行关联的?
  • 在主线程不断循环的Looper,为什么不会引起ANR?
  • Looper对象的内部实现机制是怎样?

因此,围绕以上问题,查阅《Android开发艺术探索》及源码,经过学习研究之后在这里进行总结。

Android的消息机制

Android的消息机制主要由MessageQueueLooperHandler三者支撑,三者的关系可以概括为:
Looper 中维护着一个MessageQueueHandler发送的消息会进入到MessageQueue也就是消息队列中,同时Looper会不断的轮询MessageQueue中是否有消息,如果存在消息,Looper将消息从MessageQueue中取出,交给Handler处理(下文会进行具体分析)。

MessageQueue

MessageQueue中主要进行两个操作,消息的插入读取,分别由enqueueMessagenext两个方法实现
enqueueMessage源码如下

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) {                // 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中通过链表的数据结构来维护消息列表,把从外部传递进来的消息(参数msg)插入到消息列表中。

next源码如下

Message next() {        // Return here if the message loop has already quit and been disposed.        // This can happen if the application tries to restart a looper after quit        // which is not supported.        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;        }    }

从以上代码可以了解到next方法中启动了一个无限循环,结束该循环只有两种情况

  • 当消息列表中有新消息插入,next方法会返回这条消息并把这条消息从消息列表中移除
  • mQuitting为truenext方法会返回null

Looper

要了解Looper的机制可以从Looper中的两个方法入手——prepare()loop()
首先是prepare(),用来在当前线程中创建Looper

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

首先可以看到一个sThreadLocal对象,在Looper中它的定义如下

static final ThreadLocal sThreadLocal = new ThreadLocal();

sThreadLocal对象是用于存放当前线程启动的Looper对象,从以上代码可以了解到两点

  • 在某线程中可以通过Looper.prepare()来创建Looper
  • 某线程中最多只能创建一个Looper,否则会抛出异常(Only one Looper may be created per thread)

然后是loop()loop()的作用主要是启动对MessageQueue的轮询,一般由线程直接调用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循环中内容,可以看到loop()方法执行后就通过for(;;)启动了无限循环,对Looper中的MessageQueue轮询,也就是不断的通过MessageQueuenext方法取出消息,当取出的消息不为空时则执行msg.target.dispatchMessage(msg)触发Handler处理消息;由上文对MessageQueue的介绍中可以知道,如果消息列表中没有消息,next方法则会阻塞,因此loop()方法当消息列表中没有消息时是处于阻塞状态。
那么什么时候触发

if (msg == null) {    // No message indicates that the message queue is quitting.    return;}

退出循环呢?由上文对MessageQueue的介绍中可以知道当mQuitting为true时,next方法会返回null

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

这时Looper中quit的相关方法就派上用场了

public void quit() {        mQueue.quit(false);    }public void quitSafely() {        mQueue.quit(true);    }

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

可以看到经过一系列的调用,mQuitting 被赋值为true,因此在处理完所有事件,需要终止Looper的无限轮询时,要调用Looperquit的相关方法终止loop方法中的无限循环,否则Looper所在线程就会一直处于等待状态。

Handler

为了实现子线程进行IO操作,然后在主线程更新UI,避免ANR的应用场景,通常会使用Handler实现跨线程通信
在主线程创建Handler对象并重写handleMessage方法,在该方法中接收并处理消息,如下

Handler mHandler = new Handler(){            @Override            public void handleMessage(Message msg) {                // TODO Auto-generated method stub                super.handleMessage(msg);            }        };

在子线程通过mHandler发送消息

mHandler.sendMessage(msg);

那么这两段代码背后的运行机制是怎样的?
先从Handler的构造方法入手,如下

 public Handler() {        this(null, false);    } 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() 最终调用的是Handler(Callback callback, boolean async),根据Handler(Callback callback, boolean async)可以看到,Handler去获取了当前线程的Looper对象,通过以上代码可以得出以下结论:

  • 创建Handler的线程必须维护者一个Looper对象,否则会抛出异常,所以在普通Thread中通过Handler()创建Handler对象前没有调用Looper.prepare()是会导致异常的
  • 通过 Looper.myLooper() 获得的Looper对象应该是运行在创建Handler的线程中的,否则无法管理跨线程通信
  • 通过获取到的Looper对象获取该Looper对象中的消息队列即代码中的**mQueue **

Looper.myLooper()代码如下

public static @Nullable Looper myLooper() {        return sThreadLocal.get();    }

可以看到Looper 对象是通过sThreadLocal来获取的,sThreadLocal中的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));    }

那么问题来了,Looper中的静态变量sThreadLocal

static final ThreadLocal sThreadLocal = new ThreadLocal();

在多个线程分别都调用了Looper的prepare方法之后,是如何维护不同线程中的Looper对象的呢,也就是说Looper的myLooper方法是如何获取到当前线程中的Looper的 Looper对象呢?
分别看下ThreadLocal的setget方法

 public void set(T value) {        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values == null) {            values = initializeValues(currentThread);        }        values.put(this, value);    }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的setget方法并不是简单的参数赋值与获取,而是将要存取的对象与当前线程Thread.currentThread()产生关联,以实现在不同线程中的同一个ThreadLocal对象获取到不同的目标对象。
综上所述,当前线程有调用Looper.prepare()的情况下在调用new Handler()之后,Handler对象就能获取到当前线程中的Looper对象及Looper持有的MessageQueue对象
初始化之后就是发送消息了,接下来看一下Handler的消息发送机制

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);    }private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

从以上代码可以知道,在外部调用了sendMessage(Message msg)之后最终执行的是enqueueMessage方法(send与post的一系列方法最终调用的都是enqueueMessage),所以我们主要关注enqueueMessage方法,在该方法中让要发送的Message对象持有当前Handler的引用(msg.target = this),最后将Message对象插入消息列表(queue.enqueueMessage(msg, uptimeMillis))

综上所述,可以总结如下:

  • 1.在需要处理消息的线程中调用Looper.prepare()创建该线程的Looper对象,调用Looper.loop()启动消息轮询(主线程ActivityThread已默认调用Looper.prepareMainLooper()与Looper.loop(),因此在Activity等在主线程运行的组件中可以直接调用new Handler()而不会抛出异常)
  • 2.通过new Handler()创建Handler对象,经过一系列调用会将Handler与当前的线程的Looper与MessageQueue进行绑定
  • 3.Handler通过sendMessage发送消息,其实本质上就是调用MessageQueue的enqueueMessage方法将消息对象插入消息列表中
  • 4.当MessageQueue的消息列表中插入消息时,MessageQueue的next结束阻塞返回Message对象,Looper在loop方法的循环中获取到Message对象,通过msg.target.dispatchMessage(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);        }    }private static void handleCallback(Message message) {        message.callback.run();    }public void handleMessage(Message msg) {    }

可以看到在dispatchMessage中优先处理Message中的callback,这个callback其实就是在post一系列方法中传递过来的Runnable

public final boolean post(Runnable r)    {       return  sendMessageDelayed(getPostMessage(r), 0);    }private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;    }

mCallback 是一个接口,是在Handler的构造方法中传递进来的,可以看到当mCallback 中的handleMessage方法返回值为true时Handler将不会执行Handler中handleMessage方法。

总结

结合上文对MessageQueueLooperHandler三者的分析,再回头看开头提到的几个问题

Handler具体是如何实现跨线程通信的?
以子线程进行IO操作,然后在主线程更新UI,避免ANR的应用场景为例:

  • 1.系统在主线程ActivityThread中已经调用Looper.prepareMainLooper()创建主线程的Looper,并调用Looper.loop(),启动轮询,不断地通过MessageQueue的next方法从消息列表中取出消息,当没有消息是,next处于阻塞状态,Looper.loop()也处于阻塞状态
  • 2.当我们在Activity等运行在主线程的组件中创建Handler时,Handler通过Looper.myLooper()获取与当前线程也就是主线程关联的Looper对象,同时Handler也持有了Looper中的MessageQueue
  • 3.子线程持有主线程创建的Handler对象,在子线程中通过Handler的send或post的系列方法发送消息,send与post的系列方法最终都是通过Handler持有的MessageQueue对象调用enqueueMessage方法将消息插入队列,触发在主线程主线程中轮询的Looper用过loop()取出消息,并在loop()中调用Handler的dispatchMessage方法,将消息交由Handler处理,最终达到了子线程进行IO操作后发送消息,主线程处理消息并刷新UI的目的

Handler中post的一系列方法与send的一系列方法有什么关系与不同?

  • 从上文对Handler的介绍中可以知道,Handler中post的一系列方法与send的一系列方法本质上最终都是通过MessageQueue对象调用enqueueMessage进行消息插入操作,只是在调用优先级上存在一点差别,具体从Handler的dispatchMessage方法可以看出
public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

Handler是如何与线程中的Looper进行关联的?
通过上文可以知道,Looper是在线程中通过Looper.preare()(主线程为preareMainLooper)创建的,preare的内部通过ThreadLocal将Looper保存并与当前线程相关联,Handler中通过Looper.myLooper获取到Looper,myLooper的内部则也是通过ThreadLocal来获取Looper,从而完成了Handler与当前线程中的Looper的关联

Looper对象的内部实现机制是怎样?
具体参考上文对Looper的分析,这里就不重复展开

那么还有一个问题
在主线程不断循环的Looper,为什么不会引起ANR?

主线程中的Looper.loop()一直无限循环为什么不会造成ANR?http://www.jianshu.com/p/cfe50b8b0a41
这篇文章已经回答了这个问题

更多相关文章

  1. Android Button的背景图片拉伸变形解决方法
  2. Android 线程归纳
  3. Android异步消息框架
  4. android设置textview限制字数以省略号显示的方法
  5. Android子控件超出父控件方法
  6. Android Studio下载及离线升级方法
  7. Delphi XE5 Android 运行黑屏卡死的解决方法

随机推荐

  1. Android 手势(Gesture)——手势检测
  2. Android移动应用界面的模板化设计【自定
  3. Android 适配问题分享和总结
  4. 硬核干货!系统盘点Android开发者必须掌握
  5. QtAndroid详解(5):JNI调用Android系统功能
  6. Android中结合OrmLite for android组件对
  7. Android 启动过程框架
  8. Android异步加载全解析之Bitmap
  9. Android菜鸟实训的第一天
  10. 分享几点Android 开发中的小技巧