Android应用程序的消息处理机制(MessageQueue,Looper,Handler)

Android应用程序是通过消息驱动的。应用程序的每个线程启动时,会在内部创建一个消息队列,然后再进入到一个无限循环中。如果有新的消息需要处理,那么线程就会将它从消息队列中取出来处理;否则,线程进入睡眠状态,直到有新的消息需要处理为止。

Android系统主要通过MessageQueue,Looper和Handler三个类来实现Android应用程序的消息处理。MessageQueue类用来描述消息队列,Looper类用来创建消息队列,进入消息循环;Handler类用来发送消息和处理消息。

1 创建线程消息队列 Looper

MessageQueue对象可以通过Looper类的静态成员函数prepareMainLooper或prepare来创建,其中,prepareMainLooper用来为应用程序的主线程创建消息队列,prepare用来为应用程序的子线程创建消息队列。

类图如下:

Java层中的MessageQueue对象的成员变量mPtr,保存了C++层中NativeMessageQueue对象的地址,这样Java层MessageQueue对象和C++层NativeMessageQueue对象就联系起来了。

Looper[Native]对象的成员变量mWakeReadPipedFd和mWakeWritePipedFd,分别表示一个管道的读端文件描述符和写端文件描述符。当一个线程的消息队列没有消息需要处理时,它就会在这个管道的读端文件描述符上进行睡眠等待,直到其他线程通过这个管道的写端文件描述符来唤醒它为止。

顺序图如下:

分析线程消息队列的创建过程,即分析Looper类的静态成员函数prepareMainLooper和prepare的实现。

frameworks/base/core/java/android/os/Looper.java

public class Looper {
    ......
    private static final ThreadLocal sThreadLocal = new ThreadLocal();
    final MessageQueue mQueue;
    private static Looper mMainLooper = null;
    ......

    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeExcepition("Only one looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

    public static final void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        ......
    }
    
    private synchronized static void setMainLooper(Looper looper) {
        mMainLooper = looper;
    }
    
    public synchronized static final Looper getMainLooper() {
        return mMainLooper;
    }    

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

Looper类的静态成员变量sThreadLocal,可以理解为一个线程局部变量,关联一个Looper对象。

Looper类的静态成员函数prepareMainLooper只能在Android应用程序主线程(UI线程)中调用。将主线程Looper对象保存在一个独立的静态成员变量中,是为了让其他线程可以通过Looper类的静态成员函数getMainLooper()来访问它,从而可以向它的消息队列中发送一些与UI操作相关的消息。

Looper对象的创建过程中,会在内部创建一个MessageQueue对象。

frameworks/base/core/java/android/os/Looper.java

public class Looper {
    private Looper() {
        mQueue = new MessageQueue();
        ......
    }
}

一个MessageQueue对象在创建过程中,又会在C++层创建NativeMessageQueue对象。

frameworks/base/core/java/android/os/MessageQueue.java

public class MessageQueue {
    private int mPtr;
    
    private native void nativeInit();
    
    MessageQueue() {
        nativeInit();
    }
}

frameworks/base/core/jni/android_os_MessageQueue.cpp

static struct {
    jclass clazz;
    jfieldID mPtr;
} gMessageQueueClassInfo;

static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    ......
    android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}

static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj, NativeMessageQueue* nativeMessageQueue) {
    env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr, reinterpret_cast<jint>(nativeMessageQueue));
}

NativeMessageQueue::NativeMessageQueue() {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

android_os_MessageQueue_setNativeMessageQueue将nativeMessageQueu与Java层的MessageQueue对象关联起来。

gMessageQueueClassInfo的clazz指向Java层的MessageQueue类,mPtr指向MessageQueue类的mPtr成员。

一个NativeMessageQueue对象在创建时又会创建一个C++层的Looper对象。getForThread用来检查是否已经为当前线程船舰过一个C++层的Looper对象。setForThread用来将C++层Looper对象与线程关联起来。

一个C++层的Looper对象在创建的过程中,又会在内部创建一个管道。

frameworks/base/libs/utils/Looper.cpp

Looper::Looper(bool allowNonCallbacks) :
    mAllowNonCallbacks(allowNonCallbacks),
    mResponseIndex(0) {
    int wakeFds[2];
    int result = pipe(wakeFds);
    ......
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
    ......
    // Allocate the epoll instance and register
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    ......
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(epoll_event));
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    ......
}

当一个线程没有新消息需要处理时,它就会睡眠在这个管道的读端文件描述符上,直到新的消息到来为止;当其他线程像这个线程的消息队列发送了一个消息后,其他线程就会通过这个管道的写端文件描述符向这个管道写入一个数据,从而将这个线程唤醒。

IO多路复用epoll机制适用于监听了大量的文件描述符,但某一时刻只有少数文件描述符是活跃的情况。这里只监听了一个文件描述符,值得使用epoll机制么?实际上,我们还可以调用Looper类的成员函数addFd向这个epoll实例中注册更多的文件描述符,以便可以监听它们的IO读写事件。如Android应用程序的键盘消息处理机制等。

2 线程消息循环过程 Looper

顺序图如下:

(1)Looper.loop

frameworks/base/core/java/android/os/Looper.java

public  class Looper {
    public static final void loop() {
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
        ......
        while (true) {
            // might block
            Message msg = queue.next();
            ......
            if (msg != null) {
                ......
            }
        }
    }
}

(2)MessageQueue.next

frameworks/base/core/java/android/os/MessageQueue.java

public class MessageQueue {
    ......
    final Message next() {
        int pendingIdleHandleCount = -1;
        int nextPollTimeoutMillis = 0;
    
        for(;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);
            
            synchronized (this) {
                // Try to retrieve the next message. Return if found
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        ......
                        mMessages = msg.next;
                        msg.next = null;
                        ......
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }
                ......
                // Get the idle handlers
                ......
            }
            // Run the idle handles
            ......
            // While calling an idle handle, a new message could have been delivered
            // so go back and look again for a pending message without waiting
            nextPollTimeoutMillis = 0;
        }
    }
}

变量pendingIdleHandleCount用来保存注册到消息队列中的空闲消息处理器(idle handlers)的个数。

变量nextPollTimeoutMillis用来描述当消息队列中没有新的消息需要处理时,当前线程需要睡眠等待的时间。nextPollTimeoutMillis=x,表示要睡眠x时间。nextPollTimeoutMillis=0,表示线程不会进入睡眠。nextPollTimeoutMillis=-1,表示线程需要无限处于睡眠状态,直到被其他线程唤醒。

nextPollTimeoutMillis!=0,说明当前线程会在nativePollOnce中睡眠等待,Binder.flushPendingCommands用来处理那些正在等待处理的Binder间进程通信请求,避免它们长时间得不到处理。

nativePollOnce函数来检查当前线程的消息队列中是否有新的消息需要处理。

MessggeQueue类的成员变量mMessages描述消息的处理时间。当nativePollOnce函数执行完成后,如果有新的消息需要处理,则mMessage不等于null。

如果消息的处理时间小于等于系统的当前时间,说明当前线程需要马上对其进行处理;否则,当前线程进入睡眠等待,以在指定的时间对消息进行处理。每一个发送到消息队列的消息是按照它们的处理时间从小到大的顺序排列在消息队列里的。

当前线程进入睡眠前,会分发一个线程空闲消息给idle handlers处理。

(3)MessageQueue.nativePollOnce

frameworks/base/core/jni/android_os_MessageQueue.cpp

static void android_os_MessageQueue_nativePollOnce(JNIEnv *env, jobject obj, jnit ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(timeoutMillis);
}

(4)NativeMessageQueue.pollOnce

frameworks/base/core/jni/android_os_MessageQueue.cpp

void NativeMessageQueue::pollOnce(int timeoutMillis) {
    mLooper->pollOnce(timeoutMillis);
}

(5)Looper.pollOnce

frameworks/base/libs/utils/Looper.cpp

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    for(;;) {
        ......
        if (result != 0) {
            return result;
        }
        result = pollInner(timeoutMillis);
    }
}

如果有新的消息需要处理,pollInner函数返回值不为0。

(6)Looper.pollInner

frameworks/base/libs/utils/Looper.cpp

int Looper::pollInner(int timeoutMillis) {
    ......
    int result = ALOOPER_POLL_WAKE;
    ......
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    ......
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItem[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeReadPipeFd) {
            if (epollEvents & EPOLLIN) {
                awoken();
            }
            ......
        }
        ......
    }    
    ......
    return result;
}

在"1 创建线程消息队列 Looper"中,我们创建了以个epoll实例,并将fd保存在mEpollFd中。

epoll_wait用来监听注册在epoll实例中的文件描述符的读写事件。如果这些文件描述符都没有发生IO读写事件,那么当前线程会在epoll_wait中进入睡眠等待,等待的时间由timeoutMillis指定。

awoken()函数将与当前线程关联的一个管道数据读出来,以便当前线程下一次可以重新在这个管道上等待其他线程向它的消息队列发送一个新的消息。

(7)Looper.awoken

frameworks/base/libs/utils/Looper.cpp

void Looper::awoken() {
    ......
    char buffer[16];
    ssize_t nRead;
    do {
        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
        ......
    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}

3 线程消息发送过程 Handler

类图如下:

一般使用Handler类的默认构造函数来创建一个Handler对象,初始化mLooper和mQueue对象。

frameworks/base/core/java/android/os/Handler.java

public class Handler {
    public Handler() {
        ......
        mLooper = Looper.myLooper();
        mQueue = mLooper.mQueue;
    }
    ......
    final MessageQueue mQueue;
    final Looper mLooper;
}

从sendMessage函数开始分析线程消息发送过程。

顺序图如下:

(1)Handler.sendMessage

frameworks/base/core/java/android/os/Handler.java

public class 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)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        ......
        return sent;
    }
}

将msg的成员变量target设置为当前正在处理的Handler对象,发送出去。

(2)MessageQueue.enqueueMessage

frameworks/base/core/java/android/os/MessageQueue.java

public class MessageQueue {
    final boolean enqueueMessage(Message msg, long when) {
        ......
        final boolean needWake;
        synchronized (this) {
            ......
            msg.when = when;
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                // new head, might need to wake up
                needWake = mBlocked;
            } else {
                Message prev = null;
                while (p!=null && p.when < = when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                // still waiting on head, no need to wake up
                needWake = false;
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }
}

消息队列中的消息是按照它们的处理时间从小到大的顺序来排列的,分四种情况来讨论将一个消息插入到一个目标消息队列中。

(1)目标消息队列是一个空队列。

(2)插入的消息处理时间为0。

(3)插入的消息处理时间小于保存在目标消息队列头的消息处理时间。

(4)插入的消息处理时间大于等于保存在目标消息队列头的消息处理时间。

其中:

(4)将插入的消息保存在目标消息队列中间,因为头部消息没有发生变化,当前线程不需要对目标线程执行唤醒操作,needWake=false。

(1)~(3)将插入的消息保存在目标消息队列头部,因为头部消息发生变化,当前线程需要将目标线程唤醒,以便它可以对保存在目标消息队列头部的新消息进行处理。如果这时目标线程不是正处于睡眠状态,则不需要唤醒。成员变量mBlocked记录了目标线程是否处于睡眠状态。true表示目标线程处于睡眠状态。

(3)MessageQueue.nativeWake

frameworks/base/core/jni/android_os_MessageQueue.cpp

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jnit ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    return  nativeMessageQueue->wake();
}

wake唤醒目标线程

(4)NativeMassageQueue.wake

frameworks/base/core/jni/android_os_MessageQueue.cpp

void NativeMessageQueue::wake() {
    mLooper->wake();
}

(5)Looper.wake

frameworks/base/libs/utils/Looper.cpp

void Looper::wake() {
    ......
    ssize_t nWrite;
    do {
        nWrite = write(mWakeWritePipeFd, "W", 1);
    } while (nWrite == -1 && errno == EINTR);
    ......
}

write函数写的过程中如果中断,则返回-1,同时errno置为EINTR。

write函数向管道写端文件描述符写入了一个"W"字符,这时目标线程会因为管道发生了一个IO写事件而被唤醒。

4 线程消息处理过程 Handler

由"2 线程消息循环过程 Looper"知道,当线程没有新消息需要处理是,会在pollInner中进入睡眠;当这个线程有新消息需要处理时,它会在pollInner中被唤醒,然后沿着之前的调用路径返回到Java层的loop函数中,我们从loop函数分析一个线程的消息处理过程。

顺序图如下:

(1)Looper.loop

frameworks/base/core/java/android/os/Looper.java

public class Looper {
    ......
    public static final void loop() {
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
        ......
        while (true) {
            // might block
            Message msg = queue.next();
            ......
            if (msg != null) {
                if (msg.target == null) {
                    // for the quit message
                    return;
                }
                ......
                msg.target.dispatchMessage(msg);
            }
        }
    }
}

判断msg.target是否为null,如果为null,说明要处理的是一个退出消息。

由"3 线程消息发送过程 Handler"可知,msg.target指向的是一个Handler对象,继续分析。

(2)Handler.dispatchMessage

frameworks/base/core/java/android/os/Handler.java

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

Handler类的dispatchMessage函数按照以下顺序分发消息:

(1)要处理的消息发送时指定了一个回调时,用Handler类的handleCallback函数来处理。

(2)Handler类的成员mCallback指向一个回调时,用mCallback.handleMessage函数来处理。

(3)上述条件都不成立的话,最后用Handler类的handleMessage函数来处理。

分析这三种情况:

[1]Handler类的handleCallback函数

frameworks/base/core/java/android/os/Handler.java

public class Handler {
    private final void handleCallback(Message message) {
        message.callback.run();
    }
    public final boolean post(Runnable r)
    {
        return sendMessageDelayed(getPostMessage(r), 0);
    }
    private final Message getPostMessge(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
}

message.callback指向的是一个Runnable对象。

要处理的消息是什么时候指定了一个回调呢?是调用post的时候。

[2]Handler类内部定义Callback接口

frameworks/base/core/java/android/os/Handler.java

public class Handler {
    public Handler(Callback callback)
    {
        mLooper = Looper.myLooper();
        ......
        mCallback = callback;   
        
    }
    public interface Callback
    {
        public boolean handleMessage(Message msg);
    }
}

第3种情况如下:

(3)Handler.handleMessage

frameworks/base/core/java/android/os/Handler.java

public class Handler {
    // Subclass must implement this to receive messages
    public void handleMessage(Message msg) {
    }
}

Handler类的handleMessage实现是空的,一般子类重写了父类的handleMessage函数来处理消息。

线程消息的处理过程分析完毕,这些消息都是先发送到一个消息队列,然后从这个消息队列中取出来处理。

还有一种特殊的消息,是在线程空闲的时候处理的,称为线程空闲消息。线程空闲消息是由空闲消息处理器的对象来处理的,空闲消息处理器必须要实现IdleHandler接口。

frameworks/base/core/java/android/os/MessageQueue.java

public class MessageQueue {
    ......
    /* Return true to keep your idle handler active,false to have it removed.
    * Called when the message queue has run out of messages and will now wait for more.
    * Or may be called if there are still messages pending in the queue,
    * but they are all scheduled to be dispatched after the current time.
    */
    public static interface IdleHandler {
        boolean queueIdle();
    }
    public final void addIdleHandler(IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerExcepition("Can't add a null IdleHandler");
        }
        synchronized(this) {
            mIdleHandlers.add(handler);
        }
    }
    public final void removeIdleHandler(IdleHandler handler) {
        syschronized(this) {
            mIdleHandlers.remove(handler);
        }
    }
}

queueIdle函数用来接收线程空闲消息。

变量mIdleHandlers指向一个IdleHandler列表,用来保存一个线程的空闲消息处理器。函数addIdleHandler和removeIdleHandler用来注册和注销一个空闲消息处理器。

当一个线程的消息队列为空,或保存在消息队列头部的消息的处理时间大于系统的当前时间时,线程处于一种空闲状态,接下来它会进入睡眠状态。在进入睡眠状态前,线程会发出线程空闲消息给那些注册了的空闲消息处理器来处理。

frameworks/base/core/java/android/os/MessageQueue.java

public class MessageQueue {
    ......
    final Message next() {
        int pendingIdleHandleCount = -1;
        ......

        for(;;) {
            ......
            nativePollOnce(mPtr, nextPollTimeoutMillis);
            
            synchronized (this) {
                // Get the idle handlers
                ......
                if (pendingIdleHandleCount < 0) {
                    pendingIdleHandleCount = mIdleHandlers.size();
                }
                ......
                if (pendingIdleHandleCount == 0) {
                    continue;
                }
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandleCount, 4)];
                }
                mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handles
            for (int i=0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                // release the reference to the handler
                mPendingIdleHandlers[i] = null;
                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }
                if (!keep) {
                    synchonized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            // Reset the idle handler count to 0 so we do not run them again
            pendingIdleHandlerCount = 0;
        }
    }
}

只有两种情况下pendingIdleHandleCount等于0,一种情况是没有空闲消息处理器注册到当前线程的消息队列中;一种情况是之前已经发送过一个线程空闲消息了(queueIdle())。

可以看出,在next()的一次调用中,一个线程最多只会发出一个线程空闲消息。

通过注册空闲消息处理器,可以把不紧急的事情放到线程空闲的时候执行,充分利用线程的空闲时间。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值