Android 7.0 메시지 큐 상세 설명

24000 단어 Android7.0MessageQueue
Android 의 메시지 처리 메커니즘 은 Handler 에 대량으로 의존 합 니 다.Handler 마다 대응 하 는 Looper 가 있 으 며,대응 하 는 Message Queue 에서 끊임없이 메 시 지 를 꺼 내 처리 합 니 다.
그동안 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 를 처리 합 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기