Android线程通信(Handler)

Android应用程序的线程间通信(消息循环)是通过Handler,Looper和MessageQueue三者协同实现的.

Handler发送和处理消息,Looper创建消息队列和进入消息循环,MessageQueue描述消息队列.

消息循环涉及到Java层和native层,其中,Looper和MessageQueue在Java层和native层都有相应的类,Handler只在Java层有.Java层的Looper持有Java层MessageQueue的引用,在消息循环的时候Looper调用MessageQueue的next()方法获取需要处理的消息.Java层的MessageQueue又持有NativeMessageQueue的引用,NativeMessageQueue持有native层Looper的引用.native层的Looper有一个管道文件,管道文件的读端文件描述符和写端文件描述符,还有监听文件描述符I/O事件的epoll.当一个线程的消息队列中没有消息时,该线程就阻塞在管道的读端文件描述符上面,直到有其他线程通过这个管道的写端文件描述符来唤醒他.


相关类文件:
./frameworks/base/core/java/android/os/Handler.java
./frameworks/base/core/java/android/os/Looper.java
./system/core/include/utils/Looper.h
./system/core/libutils/Looper.cpp
./frameworks/base/core/java/android/os/MessageQueue.java
./frameworks/base/core/jni/android_os_MessageQueue.cpp

整体流程为Handler发送消息给MessageQueue,然后Looper从MessageQueue中将消息取出来交给Hander处理.

下面按照
1.消息队列创建
2.消息发送
3.消息循环和处理
三个步骤分析


1. 消息队列的创建

android应用的消息队列是用MessageQueue对象来描述的,他可以通过Looper的静态成员函数prepareMainLooper()或者prepare()方法创建.其中prepareMainLooper()为主线程创建消息队列,prepare()为子线程创建消息队列,但前者也是调用的后者.

下面以主线程消息队列的创建为例进行分析

应用进程创建后, 首先进入其主线程的ActivityThread.main()方法执行,在该方法中调用Looper.prepareMainLooper()方法创建主线程的消息队列.

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

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
首先调用prepare(false);

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
new一个Looper实例,并存储在ThreadLocal中。也就是说每个线程都有一个Looper和MessageQueue.
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

./frameworks/base/core/java/android/os/MessageQueue.java
new一个MessageQueue实例
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
从MessageQueue的构造函数可以看出来,调用了native方法,并将返回值赋给成员变量mPtr.该变量是native层MessageQueue对象的地址,这样Java和native层的MessageQueue对象就能关联起来。
android_os_MessageQueue.cpp

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); //初始化native消息队列 
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }
    nativeMessageQueue->incStrong(env);
    return reinterpret_cast<jlong>(nativeMessageQueue);
}

创建了NativeMessageQueue实例
NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread(); //获取TLS中的Looper对象
    if (mLooper == NULL) {
        mLooper = new Looper(false); //创建native层的Looper 
        Looper::setForThread(mLooper); //保存native层的Looper到TLS中
    }
}

创建了Looper实例并跟当前线程关联
./system/core/libutils/Looper.cpp

Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    mWakeEventFd = eventfd(0, EFD_NONBLOCK);// 构建唤醒事件的fd
    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd.  errno=%d", errno);

    AutoMutex _l(mLock);
    rebuildEpollLocked();  //重建epoll事件
}

void Looper::rebuildEpollLocked() {
    if (mEpollFd >= 0) {
        close(mEpollFd); //关闭旧的epoll实例
    }
    mEpollFd = epoll_create(EPOLL_SIZE_HINT); //创建新的epoll实例,并注册wake管道
    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); //把未使用的数据区域进行置0操作
    eventItem.events = EPOLLIN; //可读事件
    eventItem.data.fd = mWakeEventFd;
    //将唤醒事件(mWakeEventFd)添加到epoll实例(mEpollFd)
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);

    for (size_t i = 0; i < mRequests.size(); i++) {
        const Request& request = mRequests.valueAt(i);
        struct epoll_event eventItem;
        request.initEventItem(&eventItem);
        //将request队列的事件,分别添加到epoll实例
        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);
        }
    }
}
用epoll机制监听管道的唤醒事件,有唤醒事件到来说明有其他线程向本线程发消息,没有唤醒事件,本线程就进入睡眠状态。

总结:
1) 调用prepare()方法创建Java层Looper
2) 在Java层Looper的构造函数里面创建Java层消息对象MessageQueue
3) 在Java层消息队列里面,通过jni创建native层消息队列nativeMessageQueue
4) 在nativeMessageQueue的构造函数里面,创建native层Looper
5) 在natvie层Looper里面创建管道文件的读端描述符,写端描述符和epoll实例,,然后用epoll监听管道读端描述符的唤醒事件.

2. 发送消息
在Hanlder中,主要有以下三个方法发送消息:
1)sendMessage(Message msg)
2)sendMessageDelayed(Message msg, long delayMillis)
3)sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
其中第一个是立刻发送,第二个是延迟多久发送,第三个是指定时间发送。不过,最终都会调用sendMessageAtTime()方法

然后进入MessageQueue.enqueueMessage(Message msg, long when)
    boolean enqueueMessage(Message msg, long when) {

        synchronized (this) {

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) { //消息队列为空,消息处理时间为0,和消息处理时间小于消息队列头部消息的处理时间,三种情况下将消息插入到消息头部。
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
消息队列中的消息是根据消息处理时间从小到大进行排列。
该方法首先根据参数when,将消息插入到合适的位置。然后判断是否需要唤醒线程。只有在消息插入到消息头部,且当前线程处于阻塞状态的时候才需要唤醒线程。mBlocked参数用来表示当前线程是否处于阻塞状态。

唤醒线程方法nativeWake(mPtr)最终调用到了native层Looper.wake()方法。

void Looper::wake() {

    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);
        }
    }
}
可以看出唤醒线程就是向唤醒事件的管道里面写数据。数据是多少并不重要,写进去就行.

总结:
1)在Java层向消息队列里面添加消息
2)在native层向管道里面写数据,以便唤醒线程

3. 消息循环

./frameworks/base/core/java/android/os/Looper.java
从Looper.loop()开始循环:

    public static void loop() {

        final Looper me = myLooper();

        final MessageQueue queue = me.mQueue;

        ...

        for (;;) {
            Message msg = queue.next(); // 获取需要处理的消息,可能会阻塞

            ...

            msg.target.dispatchMessage(msg);//调用handler.dispatchMessage()进行分发处理

           ...

            msg.recycleUnchecked();
        }
    }

然后进入MessageQueue.next()方法
    Message next() {

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

                ...

        }
    }
在for循环里面,首先调用nativePollOnce()方法检查是否有新消息需要处理,如果有就从Message里面取出来并返回。如果没有,就检查线程中是否有注册空闲消息处理器,如果有空闲消息处理器,就发送空闲消息给空闲消息处理器进程处理,处理完后就进入睡眠状态,没有的话就直接进入睡眠状态。
现在分析native方法nativePollOnce()。该方法最终调用到Looper.cpp的pollinner()方法。
int Looper::pollInner(int timeoutMillis) {

    ...

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); //监听注册的文件描述符的IO读写事件,如果没有,则进入睡眠状态

    ...

    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        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);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }

    ...

    return result;
}
在native方法返回后,MessageQueue就取出最新的消息并返回给Java层的Looper,Looper再调用Handler.dispatchMessage(msg)方法将消息分发给handler进行处理。

总结:
1) 在java层的Looper里面循环调用MessageQueue.next()方法获取消息,如果没有消息线程就阻塞在这里.
2)在native层利用epoll机制层监听管道是否有唤醒事件,如果有唤醒事件就通知Java层获取消息并处理.


注意:
1)native层应用的线程通信也是用的这套机制
2)空闲消息和空闲消息处理器机制,线程在进入睡眠之前会判断是否有注册空闲消息处理器,有的话就会发送空闲消息,不重要和不紧急的任务可以交给空闲消息处理器来处理。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值