Android 7.0 메시지 큐 상세 설명
24000 단어 Android7.0MessageQueue
그동안 Message Queue 는 자바 층 의 추상 이 어야 한다 고 생각 했 지만 사실은 Message Queue 의 주요 부분 은 Native 층 에 있 었 다.
자신 은 Message Queue 가 Native 층 에서 일 하 는 것 에 익숙 하지 않 으 니 이 기 회 를 빌려 분석 해 보 자.
1.Message Queue 생 성
Looper 를 사용 해 야 할 때 저 희 는 Looper 의 prepare 함 수 를 호출 합 니 다.
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 ; Looper
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
// MessageQueue
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
1 NativeMessageQueue Message Queue 의 구조 함 수 를 봅 시다.
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//mPtr long?
mPtr = nativeInit();
}
Message Queue 의 구조 함수 에서 native 함수 가 호출 되 었 습 니 다.안 드 로 이 드 를 봅 시다os_Message Queue.cpp 의 실현:
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
//MessageQueue Native
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
............
// long , Java ; Java , native long ,
return reinterpret_cast<jlong>(nativeMessageQueue);
}
우 리 는 NativeMessage Queue 의 구조 함 수 를 따라 갑 니 다.
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
// Native Looper,
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
코드 를 보면 네 이 티 브 층 과 자바 층 은 모두 Looper 대상 이 있 고 모두 Message Queue 를 조작 한 것 으로 보인다.Message Queue 는 자바 층 과 네 이 티 브 층 에 각각 자바 층 과 네 이 티 브 층 의 메 시 지 를 저장 하 는 저장 구 조 를 가지 고 있다.2 Native 층 의 looper
Native 층 looper 의 구조 함 수 를 봅 시다.
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
// fd
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
.......
rebuildEpollLocked();
}
native 층 에서 Message Queue 의 Looper 가 초기 화 되 었 을 때 rebuildEpoll Locked 함수 도 호출 되 었 습 니 다.따라 가 보 겠 습 니 다.
void Looper::rebuildEpollLocked() {
// Close old epoll instance if we have one.
if (mEpollFd >= 0) {
close(mEpollFd);
}
// Allocate the new epoll instance and register the wake pipe.
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
............
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
// mEpollFd mWakeEventFd
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 fd
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
............
}
}
네 이 티 브 층 의 looper 를 보면 네 이 티 브 층 은 epoll 에 의존 하여 사건 처 리 를 구동 하 는 것 을 알 고 있 습 니 다.여기 서 우 리 는 먼저 대체적인 이미 지 를 보존 한 후에 상세 하 게 분석 할 것 이다.2.Message Queue 사용
1 메시지 쓰기
Android 에 서 는 자바 층 에서 Message Queue 에 메 시 지 를 쓸 수도 있 고,Native 층 에서 Message Queue 에 메 시 지 를 쓸 수도 있 습 니 다.우 리 는 각각 대응 하 는 조작 절 차 를 살 펴 보 자.
1.1 자바 계층 기록 메시지
자바 층 에서 Message Queue 에 메 시 지 를 기록 합 니 다.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) {
.....
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;
// , MessageQueue ,
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;
}
// ,needWake false
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;
}
위 코드 는 간단 합 니 다.주로 새로 추 가 된 Message 를 실행 시간 에 따라 기 존 대기 열 에 삽입 한 다음 상황 에 따라 nativeAwake 함 수 를 호출 합 니 다.우리 nativeAwake 를 따라 가자.
void NativeMessageQueue::wake() {
mLooper->wake();
}
void Looper::wake() {
uint64_t inc = 1;
// mWakeEventFd
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
.............
}
네 이 티 브 계층 의 looper 가 초기 화 되 었 을 때 네 이 티 브 계층 의 looper 는 epoll 을 이용 하여 이 벤트 를 구동 할 것 이 라 고 언급 한 적 이 있 습 니 다.그 중에서 구 조 된 epoll 핸들 은 mWakeEventFd 를 감청 합 니 다.실제로 Message Queue 에서 데 이 터 를 꺼 낼 때 데이터 가 오지 않 으 면 epoll 을 이용 하여 기 다 립 니 다.따라서 자바 층 이 메 시 지 를 쓸 때 대기 상태 인 Message Queue 를 깨 웁 니 다.
Message Queue 에서 정 보 를 추출 하 는 것 을 소개 할 때 이 문 제 를 다시 분석 할 것 입 니 다.
1.2 네 이 티 브 계층 이 메 시 지 를 기록 합 니 다.
Native 층 에서 메 시 지 를 기록 합 니 다.Native 층 looper 의 sendmessage 함수 에 의존 합 니 다.
void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
sendMessageAtTime(now, handler, message);
}
void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
const Message& message) {
size_t i = 0;
{
AutoMutex _l(mLock);
//
size_t messageCount = mMessageEnvelopes.size();
while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
i += 1;
}
// message MessageEnvelope
MessageEnvelope messageEnvelope(uptime, handler, message);
mMessageEnvelopes.insertAt(messageEnvelope, i, 1);
// Optimization: If the Looper is currently sending a message, then we can skip
// the call to wake() because the next thing the Looper will do after processing
// messages is to decide when the next wakeup time should be. In fact, it does
// not even matter whether this code is running on the Looper thread.
if (mSendingMessage) {
return;
}
}
// Wake the poll loop only when we enqueue a new message at the head.
if (i == 0) {
// , wake epoll
wake();
}
}
이상 은 Message Queue 에 메 시 지 를 추가 하 는 주요 절차 입 니 다.다음은 Message Queue 에서 메 시 지 를 꺼 내 는 절 차 를 살 펴 보 겠 습 니 다.2.메시지 추출
자바 층 의 Looper 대상 이 loop 함 수 를 호출 할 때 Message Queue 를 사용 하여 메 시 지 를 추출 합 니 다.
public static void loop() {
final Looper me = myLooper();
.......
for (;;) {
Message msg = queue.next(); // might block
.......
try {
// Message
msg.target.dispatchMessage(msg);
}........
}
}
Message Queue 의 next 함 수 를 살 펴 보 겠 습 니 다.
Message next() {
//mPtr NativeMessageQueue
final long ptr = mPtr;
.......
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
// Native , IPCThread talkWithDriver, Binder
// ?
Binder.flushPendingCommands();
}
// native , epoll blocked
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
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;
// next
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;
}
//MessageQueue IdleHandler , MessageQueue , IdleHandler
//pendingIdleHandlerCount IdleHandler, -1
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
//mIdleHandlers size 0, addIdleHandler
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
// IdleHandler PendingIdleHandlers
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
// ArrayList.toArray(T[]) ; Message.Next ,
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
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 {
// queueIdle ,
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
전체 정 보 를 추출 하 는 과정 은 대체로 위의 그림 과 같다.
자바 층 에서 Looper 는 Message Queue 의 메 시 지 를 꺼 내 는 것 외 에 도 대기 열 남 은 기간 에 IdleHandler 가 정의 하 는 함 수 를 실행 하 는 것 을 볼 수 있 습 니 다.
2.1 nativePollOnce
현재 유일한 의문점 은 nativePollOnce 가 Native 층 데 이 터 를 어떻게 처리 하 는 지 입 니 다.해당 하 는 native 함 수 를 살 펴 보 겠 습 니 다.
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
// Java native MessageQueue , long ptr
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
// Native looper pollOnce
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
.........
}
}
native 층 looper 의 pollOnce 함수 보기:
//timeoutMillis 。 -1 , ; 0 ,
//outFd null, :
//outEvents null, : outFd , 、 、
//outData null, : ,
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
// response, response
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
// pollInner ,
if (result != 0) {
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
// pollInner
result = pollInner(timeoutMillis);
}
}
pollInner 함수 따라 가기:
int Looper::pollInner(int timeoutMillis) {
// Adjust the timeout based on when the next message is due.
//timeoutMillis Java
//native native message
//
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
}
int result = POLL_WAKE;
//pollInner response
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
mPolling = true;
// epoll mEpollFd
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
mPolling = false;
// Acquire lock.
mLock.lock();
// rebuildEpollLocked , epoll request fd
if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
rebuildEpollLocked();
goto Done;
}
// Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
......
result = POLL_ERROR;
goto Done;
}
// Check for poll timeout.
if (eventCount == 0) {
result = POLL_TIMEOUT;
goto Done;
}
for (int i = 0; i < eventCount; i++) {
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
// , java native , mWakeEventFd, epoll
//awoken mWakeEventFd
awoken();
} else {
.........
}
} else {
//epoll request fd
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;
// fd response
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
..........
}
}
}
Done:
// Invoke pending message callbacks.
mNextMessageUptime = LLONG_MAX;
// Native Message
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {
// Remove the envelope from the list.
// We keep a strong reference to the handler until the call to handleMessage
// finishes. Then we drop it so that the handler can be deleted *before*
// we reacquire our lock.
{
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
// Native Message
handler->handleMessage(message);
}
mLock.lock();
mSendingMessage = false;
result = POLL_CALLBACK;
} else {
// The last message left at the head of the queue determines the next wakeup time.
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
// Release lock.
mLock.unlock();
// response
for (size_t i = 0; i < mResponses.size(); i++) {
Response& response = mResponses.editItemAt(i);
if (response.request.ident == POLL_CALLBACK) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
// response callback
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
removeFd(fd, response.request.seq);
}
response.request.callback.clear();
result = POLL_CALLBACK;
}
}
return result;
}
솔직히 native 층 의 코드 는 매우 어 지 럽 게 쓰 여 져 있 는데 이 함수 의 기능 이 비교적 많다.
위의 그림 에서 보 듯 이 nativePollOnce 에서 epoll 감청 을 이용 하여 데이터 가 왔 는 지 확인 한 다음 native message,native response 를 처리 합 니 다.
마지막 으로 네 이 티 브 층 에 request 를 어떻게 넣 는 지 살 펴 보 자.
3.모니터링 요청 추가
native 층 증가 request 는 looper 의 인터페이스 addFd 에 의존 합 니 다.
//fd
//ident
//events , EVENT_INPUT、EVENT_OUTPUT、EVENT_ERROR EVENT_HANGUP
//callback
//data
int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data);
}
위의 native 층 폴 링 대기 열 작업 과 결합 하여 우 리 는 대체적으로 알 수 있 습 니 다.addFd 의 목적 은 native 층 의 looper 로 하여 금 새로 가입 한 fd 에 지정 한 사건 이 발생 하 는 지 감시 하 게 하 는 것 입 니 다.지정 한 이벤트 가 발생 하면 리 셋 함수 와 매개 변수 구조 에 대응 하 는 response 를 이용 합 니 다.
native 층 의 looper 가 response 를 처리 할 때 대응 하 는 반전 함 수 를 실행 할 수 있 습 니 다.
실제 코드 보기:
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
........
{
AutoMutex _l(mLock);
// request
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
request.seq = mNextRequestSeq++;
request.callback = callback;
request.data = data;
if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1
struct epoll_event eventItem;
request.initEventItem(&eventItem);
// fd Request
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
//mEpollFd fd
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
.......
mRequests.add(fd, request);
} else {
//mEpollFd fd
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
if (epollResult < 0) {
if (errno == ENOENT) {
// Tolerate ENOENT because it means that an older file descriptor was
// closed before its callback was unregistered and meanwhile a new
// file descriptor with the same number has been created and is now
// being registered for the first time.
epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
.......
}
// , EpollRebuildLocked, epollFd fd
scheduleEpollRebuildLocked();
}
mRequests.replaceValueAt(requestIndex, request);
}
}
}
모니터링 요청 에 대한 처 리 는 위 에서 pollInner 함 수 를 소개 할 때 이미 분석 하 였 으 며,여 기 는 더 이상 군말 하지 않 습 니 다.3.총화
1.절차 총화
Message Queue 의 전체 프로 세 스 는 자바 부분 과 네 이 티 브 부분 을 포함 하고 있 으 며,그림 에서 네 이 티 브 층 의 비중 이 여전히 크다 는 것 을 알 수 있다.우 리 는 위의 그림 과 결합 하여 전체 Message Queue 에 대응 하 는 처리 절 차 를 회상 합 니 다.
1.자바 층 이 Looper 대상 을 만 들 때 자바 층 의 Message Queue 를 만 듭 니 다.자바 층 의 Message Queue 를 초기 화 할 때 Native 함 수 를 이용 하여 Native 층 의 Message Queue 를 만 듭 니 다.
2.Native 층 의 Message Queue 가 초기 화 되면 해당 하 는 Native Looper 대상 을 만 듭 니 다.Native 대상 이 초기 화 되면 epollFd 와 WakeEventFd 가 생 성 됩 니 다.이 가운데 epollFd 는 epoll 의 감청 핸들 로 초기 에 epollFd 는 WakeEventFd 만 감청 합 니 다.
3.그림 에서 빨간색 라인 은 Looper 가 Message Queue 에서 메 시 지 를 가 져 올 때 논리 적 인 흐름 을 처리 합 니 다.
3.1.자바 층 의 Looper 가 순환 을 시작 할 때 먼저 JNI 함 수 를 통 해 Native Looper 를 호출 하여 pollOnce 작업 을 해 야 합 니 다.
3.2,Native Looper 가 실행 되 기 시작 하면 epollFd 가 깨 어 날 때 까지 기 다 려 야 합 니 다.epollFd 가 시간 초과 나 감청 을 기다 리 는 핸들 에 사건 이 발생 하면 Native Looper 는 사건 을 처리 할 수 있 습 니 다.
3.3,Native 층 에서 Native Looper 는 Native Message Queue 의 메 시 지 를 먼저 처리 한 다음 에 Response 에 대응 하 는 리 셋 함 수 를 호출 합 니 다.
3.4,이번 순환 에서 네 이 티 브 계층 의 이벤트 처리 가 끝 난 후에 야 자바 계층 의 Message Queue 메 시 지 를 처리 하기 시 작 했 습 니 다.Message Queue 에 처리 할 메시지 가 없고 Message Queue 에 IdleHandler 가 존재 할 경우 IdleHandler 가 정의 하 는 처리 함 수 를 호출 합 니 다.
그림 에서 파란색 부분 이 대응 하 는 함수 호출:
자바 층 에서:
Message Queue 의 addIdleHandler 를 이용 하여 Message Queue 에 IdleHandler 를 추가 할 수 있 습 니 다.
Message Queue 의 enqueueMessage 를 이용 하여 Message Queue 에 메 시 지 를 추가 할 수 있 습 니 다.필요 할 때 네 이 티 브 함 수 를 이용 하여 네 이 티 브 층 의 WakeEventFd 에 메 시 지 를 기록 하여 epollFd 를 깨 웁 니 다.
Native 층 에서:
looper:sendmessage 를 이용 하여 Native Message Queue 에 메 시 지 를 추가 할 수 있 습 니 다.마찬가지 로,필요 할 때 네 이 티 브 층 의 WakeEventFd 에 메 시 지 를 써 서 epollFd 를 깨 웁 니 다.
looper:addFd 를 이용 하여 Native Looper 에 감청 요청 을 등록 할 수 있 고 감청 요청 은 감청 할 fd,감청 할 이벤트 및 대응 하 는 리 셋 함수 등 을 포함 하 며 감청 요청 에 대응 하 는 fd 는 epollFd 감청 대상 이 됩 니 다.감 청 된 fd 에 대응 하 는 이벤트 가 발생 하면 epollFd 를 깨 웁 니 다.이 때 대응 하 는 response 가 추 가 된 response List 를 생 성하 고 처 리 를 기다 리 겠 습 니 다.response 가 처리 되면 대응 하 는 반전 함 수 를 호출 합 니 다.
2.주의사항
Message Queue 는 자바 층 과 네 이 티 브 층 에 각각 저장 구 조 를 가지 고 있어 각각 메 시 지 를 추가 할 수 있다.처리 논리 상 네 이 티 브 층 의 Message 를 우선 처리 한 뒤 네 이 티 브 층 에서 생 성 된 response 를 처리 하고 마지막 으로 자바 층 의 Message 를 처리 합 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Android 에서 PopupWindow 가 7.0 이후 에 적합 한 해결 을 자세히 설명 합 니 다.본 고 는 안 드 로 이 드 에서 PopupWindow 가 7.0 이후 에 적합 한 해결 을 상세 하 게 설명 하고 여러분 에 게 공유 합 니 다.구체 적 으로 다음 과 같 습 니 다. 필요:위의 그림 왼쪽 효과 와...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.