Android进程内线程之间的通信广泛使用到了Handler,handler也是Android独有的消息处理机制。最常见的莫过于使用handler更新ui了。现在就来分析Handler机制。

Android进程内线程之间的通信 如下图所示:

Android中当一个app运行之后,至少有一个主线程,也就是通常说的UI线程。如果还有其他线程要更新UI,那么试图更新UI的线程,就要给UI主线程发送消息,告诉UI线程如何更改UI等。

当然也可以向其他线程发送消息。Android 可以处理消息的线程都有且仅有一个消息队列,用来暂存发送给他的消息,处理线程的消息也有且仅有一个处理消息的对象,用来从消息对象中取出消息进行处理。

这里就要搞清楚,当有多个消息处理线程的时候,如何发送给某个具体的消息处理线程。消息处理线程又是如何处理消息的。

Android中与消息机制相关的类主要是 Looper,Handler,Message,MessageQueue.其中Looper充当消息处理的角色,MessageQueue充当消息队列,Message充当消息,Handler可以暂时理解为充当消息的发送者,实际上还Handler还定义了如何处理消息,只是处理消息是由消息处理线程完成的。

Handler机制即可用于异步通信,也可用于同步通信。大多数时候使用的是异步通信。

Looper

源码路径:

1
Android-6/frameworks/base/core/java/android/os/Looper.java

主要成员如下图所示:

Looper充当消息处理的角色,每个线程只能有一个looper对象,那么是如何做到的呢?

如何创建线程唯一的looper

创建looper对象只能使用其提供的静态方法prepare(),因为其构造方法是私有的。

123456789101112131415
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));}private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }

静态变量sThreadLocal的类型是 ThreadLocal,它通过将需要保存的对象和线程id关联在一起的方式实现了线程本地存储的功能。这样放入sThreadLocal对象中的Looper对象就和创建它的线程关联在一起了。

消息处理循环

创建好Looper对象之后,调用他的loop方法即可进入消息处理循环:

1234567891011121314151617
    public static void loop() {      // 拿到与当前线程关联的looper对象        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;..............................................        for (;;) {            Message msg = queue.next(); // might block        ...................................            msg.target.dispatchMessage(msg);..........................................            msg.recycleUnchecked();// 回收Message对象        }    }

loop()方法内部是一个for无线循环,所以肯定调用loop方法也是有讲究的:一般来说对其的调用是放在线程的run方法中的。

123456
class mtThread extends Thread{  public void run(){    Looper.prepare();    Looper.loop();  }}

loop()方法会循环从MessageQueue队列中取出消息,然后吧消息分发出去。消息的分发是通过Message对象的Target变量完成的。这变量的类型是Handler类型。一个Looper对象可以和多个Handler对象关联.

Message是消息的载体,发送者吧需要传递的消息放在Message对象中,Message创建的时候需要指定他的处理对象。Handler主要用来发送和处理消息。只不过处理消息这个过程发生在消息处理线程中。

消息的载体Messenger

1
Android-6/frameworks/base/core/java/android/os/Message.java

关键数据成员:

1234567891011121314151617181920212223242526272829
public final class Message implements Parcelable {    // 标识消息的类型,消息分发的时候需要使用    public int what;    // 此消息可以携带的一个int型数据    public int arg1;    // 此消息可以携带的第二个int型数据    public int arg2;    // 此消息可以携带的一个对象    public Object obj;    public Messenger replyTo;    /*package*/ static final int FLAG_IN_USE = 1 << 0;       /** If set message is asynchronous */       /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;       /** Flags to clear in the copyFrom method */       /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;       /*package*/ int flags;       /*package*/ long when;       /*package*/ Bundle data;       /*package*/ Handler target; // 此消息绑定的Handler,也就是该Handler发送和处理该消息       /*package*/ Runnable callback; // 处理消息的回调方法,执行是在消息处理线程

其中target表示哪个Handler处理该消息。要知道在哪里设置了该字段的值。

如何创建一个Message

创建Message对象,不建议直接new,而是调用Message提供的一个静态方法obtain():

推荐:

123456
public static Message obtain(Handler h) {    Message m = obtain();    m.target = h;    return m;}

Message设计时,实现了Recyle机制。

12345678910111213
public static Message obtain() {     synchronized (sPoolSync) {         if (sPool != null) {             Message m = sPool;             sPool = m.next;             m.next = null;             m.flags = 0; // clear in-use flag             sPoolSize--;             return m;         }     }     return new Message(); }

其他obtain方法:

12345
public static Message obtain(Handler h, int what);public static Message obtain(Handler h, Runnable callback);//同事指定了处理该消息的逻辑public static Message obtain(Handler h, int what, Object obj);public static Message obtain(Handler h, int what, int arg1, int arg2);public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj);

Recycle机制

Android源码中大量使用了一个设计机制:当一个对象不再使用时把它储藏起来,不让虚拟机回收,需要的时候再从仓库里拿出来重新使用,这就避免了对象被回收后再重分配的过程。对于在应用的生命周期内(或者在循环中)需要频繁创建的对象来说这个机制特别实用,可以显著减少对象创建的次数,从而减少 GC 的运行时间。运用得当便可改善应用的性能

如何实现?

首先,我们需要一个仓库用于存放暂时不用的对象;需要新对象的时候我们不能使用 new 来分配一个新对象,所以还需要一个方法 obtain 来从仓库里获取对象;最后,便是 recycle 方法了,用于回收不再使用的对象。

Message类中与Recycle机制相关的成员,要注意除了next外,其他都为static类型。

1234567891011121314151617
// 同步private static final Object sPoolSync = new Object();// 仓库中的某个Messageprivate static Message sPool;//  指向仓库中的下一个可用Message对象,当 next 为 null 时表示仓库为空Message next;// 仓库中的 Message 对象 数量private static int sPoolSize = 0;// 回收仓库中的Message对象数量最大为50个private static final int MAX_POOL_SIZE = 50;private static boolean gCheckRecycle = true;

创建对象的时候,不建议直接new,而是调用obtain()来尝试从仓库中获取。

第一次调用Obtain时,仓库为空,那么就调用构造方法创建Message对象。当Message不在使用的时候,调用recycle()方法可以把该对象放在仓库中。

12345678910111213141516171819202122232425262728293031323334
public void recycle() {        if (isInUse()) {            if (gCheckRecycle) {                throw new IllegalStateException("This message cannot be recycled because it "                        + "is still in use.");            }            return;        }        recycleUnchecked();    }    void recycleUnchecked() {        // Mark the message as in use while it remains in the recycled object pool.        // Clear out all other details.        flags = FLAG_IN_USE;        what = 0;        arg1 = 0;        arg2 = 0;        obj = null;        replyTo = null;        sendingUid = -1;        when = 0;        target = null;        callback = null;        data = null;        synchronized (sPoolSync) {            if (sPoolSize < MAX_POOL_SIZE) {                // 初次sPool为null                next = sPool;                sPool = this;                sPoolSize++;            }        }

放在仓库中,实际上就是为该对象增加了一个引用而已,这样虚拟机就不会回收它了。等下次使用在使用obtain方法尝试获取Message对象的时候,就可以省去分配对象的过程了,直接从仓库中获取。

可能会有疑问,平时在使用过程中,并没有手动调用过recycle()方法啊,其实Looper在处理消息时,会调用Message的recyle()方法!!!

handler

在创建Message对象时,需要关联一个Handler,那么现在看看这个Handler是什么。

源码位置:

1
Android-6/frameworks/base/core/java/android/os/Handler.java

Handler充当了消息发送者和处理者的角色(定义了如何处理消息,但是是由Looper调用的,执行发生在消息处理线程),与Handler关联的Message可以有多个,那么Handler就要提供一个分发的功能,因为不同的消息,处理肯定也不一样。在一个线程中,可以只使用一个Handler对象来处理所有消息,也可以有多个。

如何创建Handler

Handler的创建必须关联一个Looper,因为Handler负责发送消息,那么自然要指定谁来接收这个消息了。如果创建Handler没有关联Looper,那么默认关联当前线程中的Looper对象。

Handler也要负责定义消息的实际处理逻辑,所以创建Handler时,可以传递一个callback,负责这个Handler所有消息的处理。但这不是必须的,因为Message类中也有callback变量,而且可以使用下面的方法创建Message并指定消息处理逻辑:

1
public static Message obtain(Handler h, Runnable callback);

创建Handler的构造方法如下,构造Handler时会将与之关联的Looper中的消息队列也通过一个引用保存在Handler对象中,方便直接操作。

普通的消息的Handler:

1234567891011121314151617
// 关联当前线程的Looper// 也指定了处理该Handler中所有Message的回调方法public Handler(Callback callback) {    this(callback, false);}// 指定关联的Looperpublic Handler(Looper looper) {    this(looper, null, false);}// 指定关联的Looper// 也指定了处理该Handler中所有Message的回调方法public Handler(Looper looper, Callback callback) {    this(looper, callback, false);}

异步消息的Handler:

123456789101112131415
// 下面三个构造方法中有一个相同的参数:boolean类型的 async// 为true时,表明异步消息public Handler(boolean async) {        this(null, async);    }public Handler(Callback callback, boolean async);public Handler(Looper looper, Callback callback, boolean async){        mLooper = looper;        // 与这个Handler关联的Looper中的消息队列        mQueue = looper.mQueue;        mCallback = callback;        //在将该消息放入消息队列的enqueueMessage方法中,该变量为true时,会设置该消息为异步消息        mAsynchronous = async;}

普通消息和异步消息的区别在于是否调用Message的setAsynchronous将其设置为异步消息了。默认创建的消息的均为普通消息,需要显示调用setAsynchronous将其变为异步消息。

1234567
public void setAsynchronous(boolean async) {        if (async) {            flags |= FLAG_ASYNCHRONOUS;        } else {            flags &= ~FLAG_ASYNCHRONOUS;        }    }

根据创建Handler方式的不同,在Handler发送消息到消息队列的时候设置是否为异步消息。异步消息和普通消息的处理之后当消息队列中存在一个target为null的message的时候,才会不同。当消息队列中没有这样的message的时候,处理是一样的。

Handler如何发送Message

有两大类发送消息的方法。

  1. send类,该类方法发送的消息一般用于发送传统的带有消息id的消息,也可以携带一些其他信息。消息的处理由Handler设定callback方法处理。当然Message也是可以指定消息处理的callback的。
1234567891011121314
// 发送一个Message,希望马上处理public final boolean sendMessage(Message msg)// 创建一个Message,初始化what,然后发送,希望马上处理public final boolean sendEmptyMessage(int what)// 创建一个Message,初始化what,然后在uptimeMillis毫秒时 发送,希望在指定的时间处理public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)// 延时delayMillis毫秒,在发送Message,希望延时一段时间处理public final boolean sendMessageDelayed(Message msg, long delayMillis)// 在uptimeMillis毫秒时,发送Message,希望在指定的时间处理public boolean sendMessageAtTime(Message msg, long uptimeMillis)

上述send方法最终都会调用

123456789101112131415161718192021222324252627
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;//在这里设置了Message的target字段        // 如果是异步Handler的时候,mAsynchronous为true        // 此时就会把它发送的消息设置为异步消息,然后放入消息队列中        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

所谓的send消息,只是把消息插入到了消息队列中,同时指定消息处理的时间。MesssageQueue中的消息是按照时间排序的,后面在细说。

如果指定的时间为0,则表示需要立即处理,MesssageQueue会把这条消息放到队列的头部:

1234567891011
// 发送一个Message,放在消息队列前面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);}

该方法用于将本消息放在消息队列的最开始的位置,创建的消息一般是希望马上尽快处理,非常紧急。

  1. post类的发送方法中的一个必要参数是一个Runnable类的对象,然后在post方法内部调用getPostMessage(Runnable r),得到绑定该Runnable对象的Message对象。最后在发送。说白了,post类型的方法发送的message,会绑定一个用于消息处理的Runnable对象。

和send一样,post也有下列若干方法:

123456789101112131415161718192021
sendMessage{   return  sendMessageDelayed(getPostMessage(r), 0);}public final boolean postAtTime(Runnable r, long uptimeMillis){   return sendMessageAtTime(getPostMessage(r), uptimeMillis);}public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){   return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);}public final boolean postDelayed(Runnable r, long delayMillis){  return sendMessageDelayed(getPostMessage(r), delayMillis);}public final boolean postAtFrontOfQueue(Runnable r){    return sendMessageAtFrontOfQueue(getPostMessage(r));}

getPostMessage方法定义:

1234567891011121314
private static Message getPostMessage(Runnable r) {    Message m = Message.obtain();    // Message类中的callback为Runnable类型    m.callback = r;    return m;}// 消息可以携带一个对象private static Message getPostMessage(Runnable r, Object token) {    Message m = Message.obtain();    m.obj = token;    m.callback = r;    return m;}

handler如何处理消息

再来看看Looper中的loop方法中是如何处理消息的:

1234567891011121314151617
    public static void loop() {      // 拿到与当前线程关联的looper对象        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;..............................................        for (;;) {            Message msg = queue.next(); // might block        ...................................            msg.target.dispatchMessage(msg);//-----------------------调用Handler中的dispatchMessage对消息进行分发处理..........................................            msg.recycleUnchecked();// 回收Message对象        }    }

从中可以看出Handler是使用dispatchMessage来进行消息处理的

1234567891011121314151617181920
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 interface Callback {        public boolean handleMessage(Message msg);}

逻辑很简单清晰。如果Message中指定了消息处理callback,那么就调用这个callback处理消息。其中post类型的方法发送的message就符合这种情况。

如果Message中没有指定callback,那么再看创建Handler对象时,有没有绑定一个callback,有的话,就是用这个callback进行处理。

如果Handler对象没有绑定callback,那么就调用handleMessage()方法处理。这个方法通常由其子类实现。我们在使用Handler的时候经常使用的就是定义一个继承Handler的类,并实现其handleMessage()方法。

这里强调下,当消息处理结束后,调用了Message的recycleUnchecked(),对该Message对象进行了回收,放入了仓库中,下次使用obtain方法获取Message对象时,就可以直接使用了,不在需要重新new了。

消息队列MessageQueue

MessageQueue类是整个Android Handler消息处理机制的难点也是精华。

源码位置:

1
Android-6/frameworks/base/core/java/android/os/MessageQueue.java

MessageQueue类定义节选:

1234567891011121314151617181920212223242526
public final class MessageQueue {    private final boolean mQuitAllowed;    private long mPtr; // used by native code    Message mMessages;    private final ArrayList mIdleHandlers = new ArrayList();    private SparseArray mFileDescriptorRecords;    private IdleHandler[] mPendingIdleHandlers;    private boolean mQuitting;    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.    private boolean mBlocked;    // The next barrier token.    // Barriers are indicated by messages with a null target whose arg1 field carries the token.    private int mNextBarrierToken;    private native static long nativeInit();    private native static void nativeDestroy(long ptr);    private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/    private native static void nativeWake(long ptr);    private native static boolean nativeIsPolling(long ptr);    private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);    ..............

其中Message mMessages记录的就是一条消息链表。另外还有几个native函数,这就说明MessageQueue会通过JNI技术调用到底层代码。mMessages域记录着消息队列中所有Java层的实质消息。一定要注意了,mMessages记录的只是Java层的消息,不包括native层的。而mptr记录的则是native层的message.

MessageQueue的示意图如下:

Android系统在Native层也实现了一个用于native进程中的线程通信的looper消息处理机制。java层的Looepr和Native的Looper并没有什么直接的关系。MessageQueue虽然使用了Native的looper类,但也仅仅使用了它的等待/唤醒机制。其余的如消息队列的实现都是在java层。

关于MessageQueue的不得不知的内幕

再次看一下Looper中的loop方法:

1234567891011121314151617
    public static void loop() {      // 拿到与当前线程关联的looper对象        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;..............................................        // 无限死循环        for (;;) {            Message msg = queue.next(); // might block        ...................................            msg.target.dispatchMessage(msg);//-----------------------调用Handler中的dispatchMessage对消息进行分发处理..........................................            msg.recycleUnchecked();// 回收Message对象        }    }

本质上是一个无限for循环。纵然loop是运行在单独一个线程中的,一个死循环对性能的消耗也是很可观的。这是Android系统不允许的。正常的逻辑应该是这样的,消息队列中没有消息要处理的时候,线程应该被挂起。等有消息被传入消息队列的时候,在唤醒线程去处理消息。for循环中的三个方法有两个方法我们已经知道是做什么的了,其中没有涉及挂起线程的代码,那么只能是消息队列中的next方法在作怪了!

对于Looper而言,它主要关心的是从消息队列里取消息,而后分派消息。然而对消息队列而言,在取消息时还要考虑更多技术细节。它关心的细节有:

1)如果消息队列里目前没有合适的消息可以摘取,那么不能让它所属的线程“傻转”,而应该使之阻塞;

2)队列里的消息应该按其“到时”的顺序进行排列,最先到时的消息会放在队头,也就是mMessages域所指向的消息,其后的消息依次排开;

3)阻塞的时间最好能精确一点儿,所以如果暂时没有合适的消息节点可摘时,要考虑链表首个消息节点将在什么时候到时,所以这个消息节点距离当前时刻的时间差,就是我们要阻塞的时长。

4)有时候外界希望队列能在即将进入阻塞状态之前做一些动作,这些动作可以称为idle动作,我们需要兼顾处理这些idle动作。一个典型的例子是外界希望队列在进入阻塞之前做一次垃圾收集。

MessageQueue的创建

MessageQueue是和Looper绑定的,一个Looper只能有一个MessageQueue.在Looper的构造方法中会创建MessageQueue对象,然后关联到Looper中。

其构造方法

1234
MessageQueue(boolean quitAllowed) {        mQuitAllowed = quitAllowed;        mPtr = nativeInit();    }

jni方法nativeInit如下:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();    if (!nativeMessageQueue) {        jniThrowRuntimeException(env, "Unable to allocate native queue");        return 0;    }    nativeMessageQueue->incStrong(env);    return reinterpret_cast(nativeMessageQueue);}NativeMessageQueue::NativeMessageQueue() :        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {    mLooper = Looper::getForThread();    if (mLooper == NULL) {        mLooper = new Looper(false);        Looper::setForThread(mLooper);    }}Looper::Looper(bool allowNonCallbacks) :        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {    //创建一个eventfd,这是一个计数器相关的fd,计数器不为零是有可读事件发生,read清零计数器,write递增计数器;返回的fd可以进行如下操作:read、write、select(poll、epoll)、close    mWakeEventFd = eventfd(0, EFD_NONBLOCK);    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd.  errno=%d", errno);    AutoMutex _l(mLock);    rebuildEpollLocked();}void Looper::rebuildEpollLocked() {............................................    // 创建epoll    mEpollFd = epoll_create(EPOLL_SIZE_HINT);    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);    struct epoll_event eventItem;    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union    eventItem.events = EPOLLIN;    // 监听前面创建的eventfd    eventItem.data.fd = mWakeEventFd;    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance.  errno=%d",            errno);    for (size_t i = 0; i < mRequests.size(); i++) {        const Request& request = mRequests.valueAt(i);        struct epoll_event eventItem;        request.initEventItem(&eventItem);        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);        if (epollResult < 0) {            ALOGE("Error adding epoll events for fd %d while rebuilding epoll set, errno=%d",                    request.fd, errno);        }    }}

可以看到在构造Looper对象时,其内部创建事件对象,用来实现等待/通知(wait/notify) 机制。linux内核会为这个对象维护一个64位的计数器(uint64_t)。并且使用第一个参数(initval)初始化这个计数器。调用eventfd()这个函数就会返回一个新的文件描述符(event object)

创建这个事件对象后,可以对其做如下操作。

write 将缓冲区写入的8字节整形值加到内核计数器上。

read 读取8字节值, 并把计数器重设为0.如果调用read的时候计数器为0, 要是eventfd是阻塞的, read就一直阻塞在这里,否则就得到 一个EAGAIN错误。

除此之外Native的Looper还创建了一个epoll来监听事件对象的“读操作”。也就是说,是利用epoll机制来完成阻塞动作的。每当我们向消息队列发送事件时,最终会间接向这个事件对象写入uint64_t 类型的1.于是epoll立即就感知到了风吹草动,如果有读操作而阻塞的线程,那么就会被唤醒了。

如何将Message放入消息队列中

Handler的enqueueMessage方法将一个Message放入消息队列的时候,实际调用的就是MessageQueue的enqueueMessage方法:

1234567891011121314151617181920212223242526272829303132333435363738394041
boolean enqueueMessage(Message msg, long when) {.............................        msg.when = when;        // p 指向消息队列头        Message p = mMessages;        boolean needWake;        if (p == null || when == 0 || when < p.when) {            // 消息队列为null,或者消息需要插到消息队列的头部            // 这是如果线程阻塞了,就需要被唤醒            // mBlocked的值由next()方法来设置            msg.next = p;            mMessages = msg;            needWake = mBlocked;        } else {            // 此时,新消息会插入到链表的内部,一般情况下,这不需要尝试唤醒线程            // 但当消息是异步消息而且消息队列中存在target为null的消息的时候,就要尝试唤醒线程            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;        }        //是否唤醒        if (needWake) {            nativeWake(mPtr);        }    }    return true;}

enqueueMessage()方法主要是把消息放入消息队列中,消息队列的组织也很简单,利用Message类中的next”指针”形成一个从头指向尾的单向链表。 因为消息链表是按时间进行排序的,所以主要是在比对Message携带的when信息。

enqueueMessage()方法会近尽量避免唤醒线程,因为这不是他的主要职责。除非插入的消息是立即要处理的消息,比如Handler使用sendMessageAtFrontOfQueue()方法发送的消息,因为when为0,所以enqueueMessage()方法在处理的时候会尝试唤醒线程来处理。

还有一种情况就是消息队列中存在target为null的消息,而且此时放入消息队列中的消息是一个异步消息,那么也要尝试唤醒线程。

target为null的Message是一个”同步分割栏”,它就像一个卡子,卡在消息链表中的某个位置,当消息循环不断从消息链表中摘取消息并进行处理时,一旦遇到这种“同步分割栏”,那么即使在分割栏之后还有若干已经到时的普通Message,也不会摘取这些消息了。请注意,此时只是不会摘取“普通Message”了,如果队列中还设置有“异步Message”,那么还是会摘取已到时的“异步Message”的。

如何向消息队列中放入”同步分割栏”呢?肯定不能使用Handler提供的发送Message的方法了。

方法就是MessageQueue中的postSyncBarrier():

12345678910111213141516171819202122232425262728293031
public int postSyncBarrier() {      return postSyncBarrier(SystemClock.uptimeMillis());  }private int postSyncBarrier(long when) {    // Enqueue a new sync barrier token.    // We don't need to wake the queue because the purpose of a barrier is to stall it.    synchronized (this) {        final int token = mNextBarrierToken++;        final Message msg = Message.obtain();        msg.markInUse();        msg.when = when;        msg.arg1 = token;        Message prev = null;        Message p = mMessages;        if (when != 0) {            while (p != null && p.when <= when) {                prev = p;                p = p.next;            }        }        if (prev != null) { // invariant: p == prev.next            msg.next = p;            prev.next = msg;        } else {            msg.next = p;            mMessages = msg;        }        return token;    }}

“同步分割栏”这种卡子会一直卡在消息队列中,除非我们调用MessageQueue中的removeSyncBarrier()删除这个卡子。

12345678910111213141516171819202122232425262728293031
public void removeSyncBarrier(int token) {    // Remove a sync barrier token from the queue.    // If the queue is no longer stalled by a barrier then wake it.    synchronized (this) {        Message prev = null;        Message p = mMessages;        while (p != null && (p.target != null || p.arg1 != token)) {            prev = p;            p = p.next;        }        if (p == null) {            throw new IllegalStateException("The specified message queue synchronization "                    + " barrier token has not been posted or has already been removed.");        }        final boolean needWake;        if (prev != null) {            prev.next = p.next;            needWake = false;        } else {            mMessages = p.next;            needWake = mMessages == null || mMessages.target != null;        }        p.recycleUnchecked();        // If the loop is quitting then it is already awake.        // We can assume mPtr != 0 when mQuitting is false.        if (needWake && !mQuitting) {            nativeWake(mPtr);        }    }}

删除“同步分割栏”的时候,如果它的前面还有其他Message,则不需要唤醒线程。如果“同步分割栏”就是消息队列的第一个消息,而且如果“同步分割栏”后面还有其他非“同步分割栏”的Message的时候,就尝试唤醒线程。

现在再来看看这个nativeWake()方法:

1
Android-6/frameworks/base/core/jni/android_os_MessageQueue.cpp
1234567891011121314151617181920
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {    NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);    nativeMessageQueue->wake();}void NativeMessageQueue::wake() {    mLooper->wake();}void Looper::wake() {#if DEBUG_POLL_AND_WAKE    ALOGD("%p ~ wake", this);#endif    uint64_t inc = 1;    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));    if (nWrite != sizeof(uint64_t)) {        if (errno != EAGAIN) {            ALOGW("Could not write wake signal, errno=%d", errno);        }    }}

做的事情很简单,就是向一个管道中写入一个uint64_t类型的数据1.

MessageQueue的消息循环

这里指的就是MessageQueue的next()方法了

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
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();        }        //调用jni方法使其阻塞nextPollTimeoutMillis毫秒,当nextPollTimeoutMillis为-1时,要一直阻塞        nativePollOnce(ptr, nextPollTimeoutMillis);        // 使用this对象同步,只要next方法还没退出,在调用本对象的任何方法都将导致调用线程挂起。        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) {              //此时说明消息队列的头部是一个“同步分割栏”              // 则查找消息队列中的异步消息                do {                    prevMsg = msg;                    msg = msg.next;                } while (msg != null && !msg.isAsynchronous());            }            // 找到了一个异步消息            if (msg != null) {                if (now < msg.when) {                    //当前还没倒Message希望处理的时刻,计算需要等待的时长                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                } else {                    // 当前已经到了Message希望处理的时刻                    // 那么设置阻塞唤醒标志为false                    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();                    //将找到的消息冲消息队列中取出返回,此时该Message从消息队列中也被移除了                    return msg;                }            } else {                // No more messages.                // 表明消息队列中没有要处理的消息                nextPollTimeoutMillis = -1;            }            // Process the quit message now that all pending messages have been handled.            // 如果退出标志设置了,则销毁native对象,然后返回            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.            // 如果是第一次进入,idle会检查是否安装了idle handler,            // 实际上就是获取idle handler的数量            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;// 设置阻塞唤醒标志为true                continue;// 没有安装 idle handler 则继续for循环            }            // idle handler 方法数组mPendingIdleHandlers中            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.        // 处理所有的idle handler        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 {              // 如果idle handler 返回false,表示不在需要继续处理                keep = idler.queueIdle();            } catch (Throwable t) {                Log.wtf(TAG, "IdleHandler threw exception", t);            }            if (!keep) {              //返回false,那么移除该idle handler              // 否则一直被循环调用                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.        // 如果有 idle handler ,重置nextPollTimeoutMillis为0,让for循环继续,而不是阻塞线程        nextPollTimeoutMillis = 0;    }}
  1. 首先检查消息队列中的第一条消息是否是”同步分割栏”,如果是,寻找队列中第一条异步消息,找到后设为当前处理的消息;如果第一条消息不是”同步分割栏”,把第一条消息设置为当前处理的消息。

  2. 如果当前处理的消息不为null,检查该校的处理时间是否已经超时,如果没有,计算等待的时长。如果处理的时间到了,next()方法将返回该消息并退出。

  3. 如果当前处理的消息为null,表示队列中没有可以处理的消息,设置等待时间为-1

  4. 检查消息队列中的退出标志,如果设置了,那么销毁native层的对象,然后next()方法退出

  5. 检查是否安装了处理idle状态的回调方法,如果没有安装则回到for循环的最开始处重新执行,也就是执行nativePollOnce()方法挂起线程并等待新的消息到来。

  6. 如果安装了idle状态的回调方法,则调用所有的回调方法,同时把nextPollTimeoutMillis设置为0.这表明在安装了idle处理方法的情况下,消息队列的循环处理是不会被阻塞的,这样idle处理函数将会不停的被调用直到处理方法返回false。

实际上next这个方法里的for循环并不是起循环摘取消息节点的作用,而是为了连贯“当前时间点”和“处理下一条消息的时间点”。简单地说,当“定时机制”触发“摘取一条消息”的动作时,会判断事件队列的首条消息是否真的到时了,如果已经到时了,就直接返回这个msg,而如果尚未到时,则会努力计算一个较精确的等待时间(nextPollTimeoutMillis),计算完后,那个for循环会掉过头再次调用到nativePollOnce(mPtr, nextPollTimeoutMillis),进入阻塞状态,从而等待合适的时长。

当消息队列中没有消息需要马上处理时,会判断用户是否设置了Idle Handler,如果有的话,则会尝试处理mIdleHandlers中所记录的所有Idle Handler,此时会逐个调用这些Idle Handler的queueIdle()成员函数。

如果要彻底搞清楚next方法,那么就必须搞定nativePollOnce()到底做了什么事情。

nativePollOnce()起到了阻塞作用,保证消息循环不会在无消息处理时一直在那里“傻转”。那么,nativePollOnce()函数究竟是如何实现阻塞功能的呢?

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,        jlong ptr, jint timeoutMillis) {    NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);}void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {   ...................    mLooper->pollOnce(timeoutMillis);  .....................}inline int pollOnce(int timeoutMillis) {      return pollOnce(timeoutMillis, NULL, NULL, NULL);  }  int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {      int result = 0;      for (;;) {        .................          result = pollInner(timeoutMillis);      }  }  int Looper::pollInner(int timeoutMillis) {......................      // 阻塞,等待      int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);      .............      // 处理所有epoll事件      for (int i = 0; i < eventCount; i++) {          int fd = eventItems[i].data.fd;          uint32_t epollEvents = eventItems[i].events;          // 是我们创建的事件对象fd          if (fd == mWakeEventFd) {            // 有数据可读              if (epollEvents & EPOLLIN) {                  awoken();              } else {                  ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);              }          } else {              ssize_t requestIndex = mRequests.indexOfKey(fd);            .......................          }      }..........................      return result;  }  void Looper::awoken() {      uint64_t counter;      TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));  }

pollInner()调用epoll_wait()时传入的timeoutMillis参数,其实来自于前面所说的MessageQueue的next()函数里的nextPollTimeoutMillis,next()函数里在以下3种情况下,会给nextPollTimeoutMillis赋不同的值:

  1. 如果消息队列中的下一条消息还要等一段时间才到时的话,那么nextPollTimeoutMillis赋值为Math.min(msg.when - now, Integer.MAX_VALUE),即时间差;

  2. 如果消息队列已经是空队列了,那么nextPollTimeoutMillis赋值为-1;

  3. 不管前两种情况下是否已给nextPollTimeoutMillis赋过值了,只要队列中有Idle Handler需要处理,那么在处理完所有Idle Handler之后,会强制将nextPollTimeoutMillis赋值为0。这主要是考虑到在处理Idle Handler时,不知道会耗时多少,而在此期间消息队列的“到时情况”有可能已发生改变。

不管epoll_wait()的超时阀值被设置成什么,只要程序从epoll_wait()中返回,说明有事件可以被处理了,否则就一直阻塞在epoll_wait 方法中。

当从epoll_wait中返回了,调用awoken(),而这个方法也是很简单的读了一下数值而已。

Android java层的Handler机制还是很简单的。无非就是向Looper的消息队列中插入Message,而后再由Looper在消息循环里具体处理。因为消息队列本身不具有链表一变动就能马上感知的功能,所以它需要借助linux内核提供的等待/通知机制来监听变动。java层的Handler机制仅仅是利用native层的Looper中提供的阻塞唤醒机制而已。当消息处理线程调用next方法尝试获取message的时候,实际上就是在等待epoll_wait方法返回。epoll_wait方法在监听我们创建的事件对象的读事件。当没有内容可读的时候就会一直阻塞,而当我们使用发送Message的时候,就会向这个时间对象中写入uint64_t类型的数据1,此时有内容可读了epoll_wait就可以返回了。仅此而已。

更多相关文章

  1. Android将胜过Windows Mobile五大原因
  2. Android消息机制
  3. android 问题总结
  4. Handler与异步消息处理
  5. Android(安卓)开发艺术探索读书笔记 10 -- Android(安卓)的消息
  6. Android(安卓)Push Notification技术实现
  7. Android应用程序键盘(Keyboard)消息处理机制分析(一)
  8. Android大图片裁剪终极解决方案(下:拍照截图)
  9. Android消息机制全面解析

随机推荐

  1. android 把图变成灰色
  2. android studio 代理设置
  3. Android中一直走马灯效果的TextView
  4. APP启动时白屏或出现标题
  5. [Android] 如何取得版本号码
  6. android GridView 只显示一行,可以左右滑
  7. Android获得屏幕的宽和高
  8. android TelephonyManager
  9. android 收集
  10. 【Android】编译CM10遇到的错误解决方案