源码角度分析native层消息机制与java层消息机制的关联

来源:互联网 发布:手机撒谎软件下载 编辑:程序博客网 时间:2024/05/18 22:46

上文从源码分析Handler机制中从java层分析了消息机制,接下来本文从native层去分析Android中的消息机制。

在一个消息驱动的系统中,最重要的就是消息队列和消息获取和处理,从上一篇文章可以看出handler的消息机制主要是靠MessageQueue进行消息列队,靠Looper进行消息循环,Looper的loop方法中进行轮询消息的实际操作还是依靠MessageQueue的next方法来获取消息,也就是说在这个消息驱动机制中最重要的就是MessageQueue这个类了。在Android 2.3之前,只有java层中可以往MessageQueue中添加消息使得消息驱动正常的运作,在2.3之后,MessageQueue的核心部分移到了native层,MessageQueue兼顾了两个世界的来保证消息的运作。
在MessageQueue的构造方法中:

?
1
2
3
4
5
<code>MessageQueue(booleanquitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}
</code>

构造函数调用nativeInit,该函数由Native层实现,native层的真正实现为android_os_MessageQueue.cpp中的android_os_MessageQueue_nativeInit方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<code>staticvoid android_os_MessageQueue_nativeInit(JNIEnv* env,     jobject obj) {
    NativeMessageQueue* nativeMessageQueue = new   NativeMessageQueue();
    if(! nativeMessageQueue) {
        jniThrowRuntimeException(env, Unable to allocate nativequeue);
        return;
    }
    android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}
 
NativeMessageQueue::NativeMessageQueue() {
    mLooper = Looper::getForThread();
    if(mLooper == NULL) {
        mLooper = newLooper(false);
        Looper::setForThread(mLooper);
    }
}
</code>

android_os_MessageQueue_nativeInit函数中创建以一个与java层MessageQueue对应点nativeMessageQueue消息队列,NativeMessageQueue构造中从当前线程中获取一个looper,如果当前线程没有到话,就实例化一个并且绑定到当前线程。

前一篇文章提到,当与消息机制相关的几个对象初始化完毕后,就要开始loop操作,而loop其实也就是循环的执行MessageQueue的next方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<code>Message next() {
    intpendingIdleHandlerCount = -1;// -1 only during first iteration
    intnextPollTimeoutMillis = 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.
            finallong 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();
                    returnmsg;
                }
            }else{
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
 
            // Process the quit message now that all pending messages have been handled.
            if(mQuitting) {
                dispose();
                returnnull;
            }
 
            // 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 = newIdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
 
        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        for(inti = 0; i < pendingIdleHandlerCount; i++) {
            finalIdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null;// release the reference to the handler
 
            booleankeep = 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;
    }
}
</code>

nativePollOnce方法返回后,就代表next方法就可以从mMessages中获取一个消息,也就是说如果消息队列中没有消息存在nativePollOnce就不会返回。
在MessageQueue的enqueueMessage方法中

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<code>booleanenqueueMessage(Message msg, longwhen) {
    if(msg.isInUse()) {
        thrownew AndroidRuntimeException(msg +  This message is already in use.);
    }
    if(msg.target == null) {
        thrownew AndroidRuntimeException(Message must have a target.);
    }
 
    synchronized(this) {
        if(mQuitting) {
            RuntimeException e = newRuntimeException(
                    msg.target +  sending message to a Handler on a dead thread);
            Log.w(MessageQueue, e.getMessage(), e);
            returnfalse;
        }
 
        msg.when = when;
        Message p = mMessages;
        booleanneedWake;
        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);
        }
    }
    returntrue;
}
</code>

添加完message后,调用了native层的nativeWake方法,这个应该是触发上面提到的nativePollOnce方法返回,好让加入的message得到分发处理。

在android_os_MessageQueue.cpp中:

?
1
2
3
4
5
6
7
8
9
<code>staticvoid android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<nativemessagequeue*>(ptr);
    returnnativeMessageQueue->wake();
}
 
voidNativeMessageQueue::wake() {
    mLooper->wake();
}
</nativemessagequeue*></code>

在Looper.cpp中:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<code>voidLooper::wake() {
    #ifDEBUG_POLL_AND_WAKE
        LOGD(%p ~ wake, this);
    #endif
 
    #ifdef LOOPER_STATISTICS
        // FIXME: Possible race with awoken() but this code is for testing only and is rarely enabled.
        if(mPendingWakeCount++ == 0) {
            mPendingWakeTime = systemTime(SYSTEM_TIME_MONOTONIC);
        }
    #endif
 
        ssize_t nWrite;
        do{
            nWrite = write(mWakeWritePipeFd, W, 1);
        }while(nWrite == -1&& errno == EINTR);
 
        if(nWrite != 1) {
            if(errno != EAGAIN) {
                LOGW(Could not write wake signal, errno=%d, errno);
            }
        }
}
</code>

在wake方法中,惊讶的发现是往管道中写入了一个”w”,难道这样就可以唤醒nativePollOnce方法返回么?是不是也就意味着nativePollOnce方法中承载着这个管道的读操作呢?如果真是这样那在nativePollOnce方法的执行过程中肯定有这么一个监控这个管道的过程吧?这个都是猜测,我们接下来分析nativePollOnce方法的具体实现。

nativePollOnce的实现在android_os_MessageQueue.cpp中:

?
1
2
3
4
<code>voidNativeMessageQueue::pollOnce(inttimeoutMillis) {
    mLooper->pollOnce(timeoutMillis);
}
</code>

在Looper.cpp中:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<code>intLooper::pollOnce(inttimeoutMillis, int* outFd, int* outEvents, void** outData) {
    intresult = 0;
    for(;;) {
        while(mResponseIndex < mResponses.size()) {
            constResponse& response = mResponses.itemAt(mResponseIndex++);
            if(! response.request.callback) {
            #ifDEBUG_POLL_AND_WAKE
                LOGD(%p ~ pollOnce - returning signalled identifier %d:
                        fd=%d, events=0x%x, data=%p, this,
                        response.request.ident, response.request.fd,
                        response.events, response.request.data);
            #endif
                if(outFd != NULL) *outFd = response.request.fd;
                if(outEvents != NULL) *outEvents = response.events;
                if(outData != NULL) *outData = response.request.data;
                returnresponse.request.ident;
            }
        }
 
        if(result != 0) {
            #ifDEBUG_POLL_AND_WAKE
            LOGD(%p ~ pollOnce - returning result %d, this, result);
            #endif
            if(outFd != NULL) *outFd = 0;
            if(outEvents != NULL) *outEvents = NULL;
            if(outData != NULL) *outData = NULL;
            returnresult;
        }
 
        result = pollInner(timeoutMillis);
    }
}
</code>

在Looper::pollOnce方法中你会发现使用了#if 和 #endif,这就代表着looper采用了编译选项来控制是否使用epoll机制来进行I/O复用。在linux的网络编程中,很长的一段时间都在使用select来做事件触发,在linux新的内核中使用了epoll来替换它,相比于select,epoll最大的好处在于它不会随着监听文件描述符数目的增长而效率降低,select机制是采用轮询来处理的,轮询的fd数目越多,效率也就越低。epoll的接口非常简单就只有三个函数:

int epoll_create(int size);创建一个epoll句柄,当这个句柄创建完成之后,在/proc/进程id/fd中可以看到这个fd。 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);注册事件函数。 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);等待事件的发生,参数timeout是超时时间毫秒值,0会立即返回,-1将不确定,也就是说有可能永久阻塞。该函数返回需要处理的事件数目,如返回0表示已超时。

再回到Looper::pollOnce方法中,每次的for循环都会调用一个函数,不妨前去看看。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
<code>intLooper::pollInner(inttimeoutMillis) {
    #ifDEBUG_POLL_AND_WAKE
    LOGD(%p ~ pollOnce - waiting: timeoutMillis=%d, this, timeoutMillis);
    #endif
 
    intresult = ALOOPER_POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;
 
    #ifdef LOOPER_STATISTICS
    nsecs_t pollStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
    #endif
 
    #ifdef LOOPER_USES_EPOLL
    // 这里表明是使用epoll的io复用凡方式
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    // 调用epoll_wait等待事件的发生
    inteventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    bool acquiredLock = false;
    #else
    // Wait for wakeAndLock() waiters to run then set mPolling to true.
    mLock.lock();
    while(mWaiters != 0) {
        mResume.wait(mLock);
    }
    mPolling = true;
    mLock.unlock();
 
    size_t requestedCount = mRequestedFds.size();
    inteventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
    #endif
 
    if(eventCount < 0) {
        if(errno == EINTR) {
            gotoDone;
        }
 
        LOGW(Poll failed with an unexpected error, errno=%d, errno);
        result = ALOOPER_POLL_ERROR;
        gotoDone;
    }
 
    if(eventCount == 0) {
    #ifDEBUG_POLL_AND_WAKE
        LOGD(%p ~ pollOnce - timeout, this);
    #endif
        result = ALOOPER_POLL_TIMEOUT;
        gotoDone;
    }
 
    #ifDEBUG_POLL_AND_WAKE
    LOGD(%p ~ pollOnce - handling events from %d fds, this, eventCount);
    #endif
 
    #ifdef LOOPER_USES_EPOLL
    for(inti = 0; i < eventCount; i++) {
        intfd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if(fd == mWakeReadPipeFd) {
            if(epollEvents & EPOLLIN) {
                awoken();
            }else{
                LOGW(Ignoring unexpected epoll events 0x%x on wake read pipe., epollEvents);
            }
        }else{
            if(! acquiredLock) {
                mLock.lock();
                acquiredLock = true;
            }
 
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if(requestIndex >= 0) {
                intevents = 0;
                if(epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
                if(epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
                if(epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
                if(epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));
            }else{
                LOGW(Ignoring unexpected epoll events 0x%x on fd %d that is
                        no longer registered., epollEvents, fd);
            }
        }
    }
    if(acquiredLock) {
        mLock.unlock();
    }
    Done: ;
    #else
    for(size_t i = 0; i < requestedCount; i++) {
        conststruct pollfd& requestedFd = mRequestedFds.itemAt(i);
 
        shortpollEvents = requestedFd.revents;
        if(pollEvents) {
            if(requestedFd.fd == mWakeReadPipeFd) {
                if(pollEvents & POLLIN) {
                    // 是管道读端发生命令直接读取管道中的数据
                    awoken();
                }else{
                    LOGW(Ignoring unexpected poll events 0x%x on wake read pipe., pollEvents);
                }
            }else{
                intevents = 0;
                if(pollEvents & POLLIN) events |= ALOOPER_EVENT_INPUT;
                if(pollEvents & POLLOUT) events |= ALOOPER_EVENT_OUTPUT;
                if(pollEvents & POLLERR) events |= ALOOPER_EVENT_ERROR;
                if(pollEvents & POLLHUP) events |= ALOOPER_EVENT_HANGUP;
                if(pollEvents & POLLNVAL) events |= ALOOPER_EVENT_INVALID;
                pushResponse(events, mRequests.itemAt(i));
            }
            if(--eventCount == 0) {
                break;
            }
        }
    }
 
    Done:
    // Set mPolling to false and wake up the wakeAndLock() waiters.
    mLock.lock();
    mPolling = false;
    if(mWaiters != 0) {
        mAwake.broadcast();
    }
    mLock.unlock();
    #endif
 
    #ifdef LOOPER_STATISTICS
    nsecs_t pollEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
    mSampledPolls += 1;
    if(timeoutMillis == 0) {
        mSampledZeroPollCount += 1;
        mSampledZeroPollLatencySum += pollEndTime - pollStartTime;
    }elseif (timeoutMillis > 0&& result == ALOOPER_POLL_TIMEOUT) {
        mSampledTimeoutPollCount += 1;
        mSampledTimeoutPollLatencySum += pollEndTime - pollStartTime
                - milliseconds_to_nanoseconds(timeoutMillis);
    }
    if(mSampledPolls == SAMPLED_POLLS_TO_AGGREGATE) {
        LOGD(%p ~ poll latency statistics: %0.3fms zero timeout, %0.3fms non-zero timeout, this,
                0.000001f * float(mSampledZeroPollLatencySum) / mSampledZeroPollCount,
                0.000001f * float(mSampledTimeoutPollLatencySum) / mSampledTimeoutPollCount);
        mSampledPolls = 0;
        mSampledZeroPollCount = 0;
        mSampledZeroPollLatencySum = 0;
        mSampledTimeoutPollCount = 0;
        mSampledTimeoutPollLatencySum = 0;
    }
    #endif
 
    for(size_t i = 0; i < mResponses.size(); i++) {
        constResponse& response = mResponses.itemAt(i);
        if(response.request.callback) {
    #ifDEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
            LOGD(%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p, this,
                    response.request.fd, response.events, response.request.data);
    #endif
            intcallbackResult = response.request.callback(
                    response.request.fd, response.events, response.request.data);
            if(callbackResult == 0) {
                removeFd(response.request.fd);
            }
 
            result = ALOOPER_POLL_CALLBACK;
        }
    }
    returnresult;
}
</code>

在上述调用epoll_wait方法等待事件的发生,timeoutMillis是我们在java层传递过来的,在MessageQueue的next方法中如果没有消息的时候其中的nextPollTimeoutMillis = -1,也就是说timeoutMillis为-1,那么在等待事情发生的时候,就有可能会造成永久阻塞,直到某个事件发生。如果有事件发生并且是管道读端的事件,那么就会直接读取管道中的数据。之前在分析Looper::woke方法时,就往管道中写入了数据。

原创粉丝点击