English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Les messages sont stockés dans une file de messages, et le thread de boucle de message est dans une boucle infinie autour de cette file de messages, jusqu'à ce que le thread sorte. Si la file contient des messages, le thread de boucle de message les retire et les distribue aux handlers correspondants pour traitement ; si la file ne contient pas de messages, le thread de boucle de message entre dans un état d'attente passive, en attendant l'arrivée du prochain message. Lors de la création d'une application Android, lorsque les tâches à exécuter sont assez lourdes pour ne pas bloquer le thread principal UI et provoquer un ANR, nous créons généralement un thread secondaire pour achever une tâche spécifique. Lors de la création d'un thread secondaire, il y a deux choix, l'un est de créer un Thread object pour créer un thread secondaire sans boucle de message ; l'autre est de créer un thread secondaire avec une boucle de message, et en raison des deux implémentations, l'une utilise directement la classe HandlerThread encapsulée par Android pour générer directement un objet de thread avec une boucle de message, et l'autre utilise la méthode suivante pour démarrer une boucle de message dans la méthode run() de l'implémentation du thread :
I. Utilisation du mécanisme de message
Généralement, un message est composé d'un thread de message et d'un Handler, regardons maintenant un message Handler dans PowerManagerService :
mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/); mHandlerThread.start(); mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
ici, ServiceThread est un HandlerThread, lors de la création d'un Handler, il est nécessaire d'injecter le looper de HandlerThread, sinon c'est le looper de la thread actuelle par défaut.
et chaque handler, est généralement ainsi :
private final class PowerManagerHandler extends Handler { public PowerManagerHandler(Looper looper) { super(looper, null, true /*async*/); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_USER_ACTIVITY_TIMEOUT: handleUserActivityTimeout(); break; case MSG_SANDMAN: handleSandman(); break; case MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT: handleScreenBrightnessBoostTimeout(); break; case MSG_CHECK_WAKE_LOCK_ACQUIRE_TIMEOUT: checkWakeLockAquireTooLong(); Message m = mHandler.obtainMessage(MSG_CHECK_WAKE_LOCK_ACQUIRE_TIMEOUT); m.setAsynchronous(true); mHandler.sendMessageDelayed(m, WAKE_LOCK_ACQUIRE_TOO_LONG_TIMEOUT); break; } } }
Deuxième partie : principe du mécanisme des messages
Alors, regardons d'abord la fonction run principale de HandlerThread :
public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper();//Après l'affectation, notifyall, car getLooper retourne mLooper notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
Prenons un autre regard sur la fonction prepare de Lopper, qui crée finalement un objet Looper et le place dans la variable locale du thread.
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { lancer une RuntimeException("Seul un Looper peut être créé par thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
Le constructeur de Looper crée MessageQueue
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
Regardons à nouveau la fonction de construction de MessageQueue, où nativeInit est une méthode native et où la valeur de retour est stockée dans mPtr, évidemment, c'est un pointeur sauvegardé avec une variable de type long
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }
La fonction native crée principalement l'objet NativeMessageQueue et renvoie le pointeur de variable.
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (!nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return 0; } nativeMessageQueue->incStrong(env); return reinterpret_cast<jlong>(nativeMessageQueue); }
Le constructeur de NativeMessageQueue est utilisé pour obtenir mLooper, et le crée s'il n'existe pas
NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) { mLooper = Looper::getForThread(); if (mLooper == NULL) { mLooper = new Looper(false); Looper::setForThread(mLooper); } }
Ensuite, regardons la fonction de construction de Looper, qui affiche l'appel à eventfd pour créer un fd, eventfd étant principalement utilisé pour la communication entre processus ou threads, nous pouvons consulter cet article sur l'introduction à eventfd
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); LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd. errno=%d", errno); AutoMutex _l(mLock); rebuildEpollLocked(); }
2.1 c layer creates epoll
Let's take a look at the rebuildEpollLocked function, which creates epoll, adds mWakeEventFd to epoll, and also adds the fd of mRequests to epoll
void Looper::rebuildEpollLocked() { // Close old epoll instance if we have one. if (mEpollFd >= 0) { #if DEBUG_CALLBACKS ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this); #endif close(mEpollFd); } // Allocate the new epoll instance and register the wake pipe. mEpollFd = epoll_create(EPOLL_SIZE_HINT); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); 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; int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance. errno=%d", errno); for (size_t i = 0; i < mRequests.size(); i++) { const Request& request = mRequests.valueAt(i); struct epoll_event eventItem; request.initEventItem(&eventItem); 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); } } }
Revenons à la fonction run de HandlerThread, nous continuons à analyser la fonction loop de Looper
public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
Voyons la fonction loop de Looper :
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue;//Obtenir le mQueue de Looper // Assurez-vous que l'identité de ce thread est celle du processus local, // et suivre ce que ce jeton d'identité représente réellement. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block cette fonction peut bloquer, le blocage est principalement epoll_wait if (msg == null) { // Aucun message ne signifie que la file de messages est en train de quitter. return; } // Cela doit être dans une variable locale, au cas où un événement UI définisse le logger Printer logging = me.mLogging;//imprimé par soi-même if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Assurez-vous que pendant le cours de la distribution de // l'identité du thread n'a pas été corrompue. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }
La fonction next de la classe MessageQueue appelle principalement la fonction nativePollOnce, puis tire un Message de la file de messages
Message next() { // Retournez ici si le boucle de messages a déjà quitté et a été désactivée. // Cela peut se produire si l'application essaie de redémarrer un looper après avoir quitté // ceci n'est pas pris en charge. final long ptr = mPtr;//pointeur conservé précédemment if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 seulement pendant la première itération int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis);
Nous allons principalement examiner la fonction native nativePollOnce, convertissons d'abord le pointeur précédent en NativeMessageQueue, puis appelons sa fonction pollOnce
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->pollOnce(env, obj, timeoutMillis); }
2.2 blocage epoll_wait au niveau c
La fonction pollOnce, généralement, le while avant cette fonction n'est pas exécuté, elle ne traite que le cas où indent est supérieur à 0, qui n'est généralement pas le cas, donc nous pouvons directement consulter la fonction pollInner
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { int result = 0; for (;;) { 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 DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce"} - retourne l'identifiant signalé %d: " "fd=%d, events=0x%x, data=%p", this, ident, fd, events, data); #endif if (outFd != NULL) *outFd = fd; if (outEvents != NULL) *outEvents = events; if (outData != NULL) *outData = data; retourne ident; } } if (result != 0) { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce"} - retourne le résultat %d", this, result); #endif if (outFd != NULL) *outFd = 0; if (outEvents != NULL) *outEvents = 0; if (outData != NULL) *outData = NULL; return result; } result = pollInner(timeoutMillis); } }
La fonction pollInner appelle principalement epoll_wait en mode bloquant, et la couche java calcule le temps de blocage à chaque fois et le transmet à la couche c, en attendant que mWakeEventFd ou l'un des descripteurs fd ajoutés précédemment ait un événement, puis epoll_wait retourne.
int Looper::pollInner(int timeoutMillis) { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce"} - en attente: timeoutMillis=%d", this, timeoutMillis); #endif // Ajustez le délai d'attente en fonction du moment où le prochain message est dû. 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; } #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce"} - next message in %" PRId64 "ns, adjusted timeout: timeoutMillis=%d", this, mNextMessageUptime - now, timeoutMillis); #endif } // Poll. int result = POLL_WAKE; mResponses.clear();//Clear mResponses mResponseIndex = 0; // We are about to idle. mPolling = true; struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);//The main thread of epoll_wait is blocked here, and the blocking time is also passed from the java layer // No longer idling. mPolling = false; // Acquire lock. mLock.lock(); // Rebuild epoll set if needed. if (mEpollRebuildRequired) { mEpollRebuildRequired = false; rebuildEpollLocked(); goto Done; } // Check for poll error. if (eventCount < 0) { if (errno == EINTR) { goto Done; } ALOGW("Poll failed with an unexpected error, errno=%d", errno); result = POLL_ERROR; goto Done; } // Vérifiez le délai d'attente de poll. if (eventCount == 0) { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce"} - timeout", this); #endif result = POLL_TIMEOUT; goto Done; } // Traitez tous les événements. #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce"} - traitement des événements provenant de %d fds", this, eventCount); #endif for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeEventFd) {//Notifiez l'événement de réveil du thread. if (epollEvents & EPOLLIN) { awoken(); } else { ALOGW("Ignorant les événements epoll inattendus 0x%x sur l'événement de réveil fd.", epollEvents); } } else { ssize_t requestIndex = mRequests.indexOfKey(fd);//L'événement addFd précédent 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));//Mettre dans mResponses } else { ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " "plus plus registered.", epollEvents, fd); } } } Terminé: ; // Appeler les rappels de messages en attente. mNextMessageUptime = LLONG_MAX; while (mMessageEnvelopes.size() != 0) {// Ce segment est principalement le message de couche C, le message de couche Java est géré par lui-même 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. { // obtain handler sp<MessageHandler> handler = messageEnvelope.handler; Message message = messageEnvelope.message; mMessageEnvelopes.removeAt(0); mSendingMessage = true; mLock.unlock(); #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD("%p ~ pollOnce"} - envoi de message: handler=%p, what=%d\ this, handler.get(), message.what); #endif handler->handleMessage(message); } // libérer le gestionnaire mLock.lock(); mSendingMessage = false; result = POLL_CALLBACK; } else { // Le dernier message laissé à la tête de la file détermine le prochain temps de réveil. mNextMessageUptime = messageEnvelope.uptime; break; } } // Libérer le verrou. mLock.unlock(); // Appeler tous les rappels de réponse. for (size_t i = 0; i < mResponses.size(); i++) {//C'est le traitement de l'événement addFd précédent, principalement l'itération de mResponses, puis l'appel de son rappel 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; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD("%p ~ pollOnce"} - invitant le callback %p : fd=%d, events=0x%x, data=%p", this, response.request.callback.get(), fd, events, data); #endif // Appeler le callback. Notez que le descripteur de fichier peut être fermé par // le callback (et potentiellement même réutilisé) avant que la fonction ne retourne donc // il faut être un peu prudent lors de la suppression du descripteur de fichier après cela. int callbackResult = response.request.callback->handleEvent(fd, events, data); if (callbackResult == 0) { removeFd(fd, response.request.seq); } // Effacer la référence de callback dans la structure de réponse immédiatement car nous // ne pas effacer le vecteur de réponse lui-même jusqu'à la prochaine poll. response.request.callback.clear(); result = POLL_CALLBACK; } } return result; }
Continuez l'analyse de la fonction loop de Looper, vous pouvez ajouter vos propres impressions pour déboguer le code, avant d'appeler dispatchMessage de l'objet cible de Message pour allouer le message
for (;;) { Message msg = queue.next(); // peut bloquer if (msg == null) { // Aucun message ne signifie que la file de messages est en train de quitter. return; } // Cela doit être dans une variable locale, au cas où un événement UI définisse le logger Printer logging = me.mLogging;//impression personnelle if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Assurez-vous que pendant le cours de la distribution de // l'identité du thread n'a pas été corrompue. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }
2.3 Ajouter l'impression de débogage
Nous allons d'abord voir comment nous ajoutons l'impression, qui peut être effectuée via la fonction setMessageLogging de Lopper
public void setMessageLogging(@Nullable Printer printer) { mLogging = printer; } Printer est un interface public interface Printer { /** * Écrire une ligne de texte à la sortie. Il n'est pas nécessaire de terminer * la chaîne fournie avec une nouvelle ligne. */ void println(String x); }
2.4 distribution des messages au niveau Java
Revenons à la distribution des messages, qui commence par appeler la fonction obtainMessage de Handler
Message msg = mHandler.obtainMessage(MSG_CHECK_WAKE_LOCK_ACQUIRE_TIMEOUT); msg.setAsynchronous(true); mHandler.sendMessageDelayed(msg, WAKE_LOCK_ACQUIRE_TOO_LONG_TIMEOUT);
Nous regardons d'abord l'appel de la fonction obtain de Message par obtainMessage
public final Message obtainMessage(int what) { return Message.obtain(this, what); }
La fonction obtain de Message crée un nouveau Message, puis son target est configuré pour le Handler
public static Message obtain(Handler h, int what) { Message m = obtain();//C'est la création d'un nouveau Message m.target = h; m.what = what; return m; }
Nous revenons à la distribution des messages précédemment mentionnée
msg.target.dispatchMessage(msg); Enfin, on appelle la fonction dispatchMessage de Handler, puis, dans Handler, on traite les messages selon différentes situations.
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg);//C'est un envoi en forme de post, accompagné d'un Runnable } else { if (mCallback != null) {//C'est ainsi que, lors de la transmission de paramètres au handler, le mCallback est appelé en tant que callback if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg);//Enfin, c'est dans le traitement handleMessage que nous l'implémentons nous-mêmes } }
2.3 Envoi de messages au niveau Java
Nous regardons à présent l'envoi de messages au niveau Java, qui est principalement appelé par les fonctions sendMessage post de Handler, qui finissent toujours par appeler la fonction suivante
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException(" this + "sendMessageAtTime() appelé sans mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); }
Revenons à l'envoi de messages à partir de la couche java, qui finira toujours par appeler la fonction enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
Enfin, dans enqueueMessage, ajoutez le message à la file de messages, puis appellez la fonction nativeWake de la couche C si nécessaire
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Le message doit avoir une cible."); } if (msg.isInUse()) { throw new IllegalStateException(msg + "Ce message est déjà en usage."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException(" msg.target + "envoi un message à un Handler sur un thread mort); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // Nouveau point de tête, réveillez la file des événements si bloqué. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inséré au milieu de la file. Généralement, nous n'avons pas besoin de réveiller // monter dans la file des événements, sauf s'il y a une barrière en tête de la file // et que le message est le premier message asynchrone dans la file. 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; } // Nous pouvons supposer que mPtr != 0 car mQuitting est faux. if (needWake) { nativeWake(mPtr); } } return true; }
Nous allons examiner cette méthode native, qui appelle également la fonction wake de Looper
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->wake();}} } void NativeMessageQueue::wake() { mLooper->wake();}} }
La fonction wake de la classe Looper ne fait que rédiger un certain contenu dans mWakeEventfd, ce fd ne sert qu'à notifier, semblable à un tube, il va finalement réveiller epoll_wait, et le thread ne restera pas bloqué et continuera d'envoyer les messages de la couche C en premier, puis de traiter les événements ajoutés précédemment, puis de traiter les messages de la couche Java.
void Looper::wake() { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ wake", this); #endif 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); } } }
2.4 Envoi de messages à la couche C
Dans la couche C, il est également possible d'envoyer des messages, principalement en appelant la fonction sendMessageAtTime de Looper, où un handler est un callback, nous plaçons le message dans mMessageEnvelopes.
void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler, const Message& message) { #if DEBUG_CALLBACKS ALOGD("%p ~ sendMessageAtTime - uptime=%" PRId64 ", handler=%p, what=%d", this, uptime, handler.get(), message.what); #endif size_t i = 0; { // acquérir le verrou AutoMutex _l(mLock); size_t messageCount = mMessageEnvelopes.size(); while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) { i += 1; } 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; } } // release lock // Wake the poll loop only when we enqueue a new message at the head. if (i == 0) { wake(); } }
When in pollOnce, after epoll_wait, it will iterate through the messages in mMessageEnvelopes and then call the handleMessage function of its handler
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. { // obtain handler sp<MessageHandler> handler = messageEnvelope.handler; Message message = messageEnvelope.message; mMessageEnvelopes.removeAt(0); mSendingMessage = true; mLock.unlock(); #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD("%p ~ pollOnce"} - envoi de message: handler=%p, what=%d\ this, handler.get(), message.what); #endif handler->handleMessage(message); } // libérer le gestionnaire mLock.lock(); mSendingMessage = false; result = POLL_CALLBACK; } else { // Le dernier message laissé à la tête de la file détermine le prochain temps de réveil. mNextMessageUptime = messageEnvelope.uptime; break; } }
Il y a un fichier Looper_test.cpp qui présente de nombreuses méthodes d'utilisation de Looper, regardons
sp<StubMessageHandler> handler = new StubMessageHandler(); mLooper->envoyerMessageADate(now + ms2ns(100), handler, Message(MSG_TEST1)); StubMessageHandler hérite de MessageHandler doit implémenter la méthode handleMessage class StubMessageHandler : public MessageHandler { public: Vector<Message> messages; virtual void handleMessage(const Message& message) { messages.push(message); } };
Regardons également les classes Message et MessageHandler
struct Message { Message() : what(0) { } Message(int what) : what(what) { } /* Le type de message. (l'interprétation est laissée à la discrétion du gestionnaire) */ int what; }; /** * Interface for a Looper message handler. * * The Looper hold a strong reference to the message handler whenever it has * un message à livrer. Assurez-vous d'appeler Looper::removeMessages * pour supprimer tout message en attente destiné au gestionnaire afin que le gestionnaire * peut être détruit. */ class MessageHandler : public virtual RefBase { protected: virtual ~MessageHandler() { } public: /** * Traite un message. */ virtual void handleMessage(const Message& message) = 0; };
2.5 c layer addFd
Nous pouvons également ajouter fd dans le epoll de thread dans Looper.cpp à l'aide de addFd, lorsque fd a des données, nous pouvons également traiter les données correspondantes. regardons d'abord la fonction addFd, nous notons qu'il y a un callBack callback
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); } int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) { #if DEBUG_CALLBACKS ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, events, callback.get(), data); #endif if (!callback.get()) { if (! mAllowNonCallbacks) { ALOGE("Tentative de réglage de callback NULL mais non autorisé pour cet itérateur."); return -1; } if (ident < 0) { ALOGE("Tentative de réglage de callback NULL avec ident < 0."); return -1; } } else { ident = POLL_CALLBACK; } { // acquérir le verrou AutoMutex _l(mLock); 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; // réserver un numéro de séquence -1 struct epoll_event eventItem; request.initEventItem(&eventItem); ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex < 0) { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);//ajouter à epoll if (epollResult < 0) { ALOGE("Erreur lors de l'ajout d'événements epoll pour fd %d, errno=%d", fd, errno); return -1; } mRequests.add(fd, request);//mettre dans mRequests } else { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);//mise à jour if (epollResult < 0) { if (errno == ENOENT) { // Tolérer ENOENT car cela signifie qu'un descripteur de fichier plus ancien était // fermé avant que son rappel ne soit désenregistré et en même temps un nouveau // un descripteur de fichier avec le même numéro a été créé et est maintenant // étant enregistré pour la première fois. Cette erreur peut se produire naturellement // when a callback has the side-effect of closing the file descriptor // before returning and unregistering itself. Callback sequence number // checks further ensure that the race is benign. // // Unfortunately due to kernel limitations we need to rebuild the epoll // set from scratch because it may contain an old file handle that we are // now unable to remove since its file descriptor is no longer valid. // No such problem would have occurred if we were using the poll system // call instead, but that approach carries others disadvantages. #if DEBUG_CALLBACKS ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor " "being recycled, falling back on EPOLL_CTL_ADD, errno=%d", this, errno); #endif epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); if (epollResult < 0) { ALOGE("Error modifying or adding epoll events for fd %d, errno=%d", fd, errno); return -1; } scheduleEpollRebuildLocked(); } else { ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); return -1; } } mRequests.replaceValueAt(requestIndex, request); } } // release lock return 1; }
在pollOnce函数中,我们先寻找mRequests中匹配的fd,然后在pushResponse中新建一个Response,然后把Response和Request匹配起来。
} 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 " "plus plus registered.", epollEvents, fd); } }
下面我们就会遍历mResponses中的Response,然后调用其request中的回调
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; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD("%p ~ pollOnce"} - invitant le callback %p : fd=%d, events=0x%x, data=%p", this, response.request.callback.get(), fd, events, data); #endif // Appeler le callback. Notez que le descripteur de fichier peut être fermé par // le callback (et potentiellement même réutilisé) avant que la fonction ne retourne donc // il faut être un peu prudent lors de la suppression du descripteur de fichier après cela. int callbackResult = response.request.callback->handleEvent(fd, events, data); if (callbackResult == 0) { removeFd(fd, response.request.seq); } // Effacer la référence de callback dans la structure de réponse immédiatement car nous // ne pas effacer le vecteur de réponse lui-même jusqu'à la prochaine poll. response.request.callback.clear(); result = POLL_CALLBACK; } }
De plus, examinons comment Looper_test.cpp l'utilise ?
Pipe pipe; StubCallbackHandler handler(true); handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
Nous allons examiner la fonction setCallback de l'objet handler
class CallbackHandler { public: void setCallback(const sp<Looper>& looper, int fd, int events) { looper->addFd(fd, 0, events, staticHandler, this);//就是调用了looper的addFd函数,并且回调 } protected: virtual ~CallbackHandler() { } virtual int handler(int fd, int events) = 0; private: static int staticHandler(int fd, int events, void* data) {//这个就是回调函数 return static_cast<CallbackHandler*>(data)->handler(fd, events); } }; class StubCallbackHandler : public CallbackHandler { public: int nextResult; int callbackCount; int fd; int events; StubCallbackHandler(int nextResult) : nextResult(nextResult), callbackCount(0), fd(-1), events(-1) { } protected: virtual int handler(int fd, int events) {//这个是通过回调函数再调到这里的 callbackCount += 1; this->fd = fd; this->events = events; return nextResult; } };
我们结合Looper的addFd一起来看,当callback是有的,我们新建一个SimpleLooperCallback
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); }
这里的Looper_callbackFunc是一个typedef
typedef int (*Looper_callbackFunc)(int fd, int events, void* data);
Revenons à SimpleLooperCallback
class SimpleLooperCallback : public LooperCallback { protected: virtual ~SimpleLooperCallback(); public: SimpleLooperCallback(Looper_callbackFunc callback); virtual int handleEvent(int fd, int events, void* data); private: Looper_callbackFunc mCallback; };SimpleLooperCallback::SimpleLooperCallback(Looper_callbackFunc callback) : mCallback(callback) { } SimpleLooperCallback::~SimpleLooperCallback() { } int SimpleLooperCallback::handleEvent(int fd, int events, void* data) { return mCallback(fd, events, data); }
Finalement, nous appelons callback->handleEvent(fd, events, data), et callback est SimpleLooperCallback, ici data, ce qui a été transmis précédemment est le pointeur this de CallbackHandler
Par conséquent, finalement, c'est staticHandler qui est appelé, et data->handler, c'est this->handler, et enfin, appel de la fonction handler de StubCallbackHandler
Bien sûr, nous pouvons éviter de rendre les choses aussi complexes et utiliser directement la deuxième fonction addFd, bien sûr, nous devons définir nous-mêmes une classe pour réaliser la classe LooperCallBack, ce qui rend les choses beaucoup plus simples.
int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);
2.6 Ajout de fd au niveau Java
Jusqu'à présent, je pensais que l'ajout de fd ne pouvait être réalisé que dans le Looper du niveau C, mais en fait, cette fonction a été réalisée à travers JNI au niveau Java.
Nous pouvons réaliser cette fonction en utilisant l'addEventListener pour le FileDescriptor dans MessageQueue
public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd, @OnFileDescriptorEventListener.Events int events, @NonNull OnFileDescriptorEventListener listener) { if (fd == null) { throw new IllegalArgumentException("fd must not be null"); } if (listener == null) { throw new IllegalArgumentException("listener must not be null"); } synchronized (this) { updateOnFileDescriptorEventListenerLocked(fd, events, listener); } }
Alors, regardons de plus près le callback OnFileDescriptorEventListener
public interface OnFileDescriptorEventListener { public static final int EVENT_INPUT = 1 << 0; public static final int EVENT_OUTPUT = 1 << 1; public static final int EVENT_ERROR = 1 << 2; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag=true, value={EVENT_INPUT, EVENT_OUTPUT, EVENT_ERROR}) public @interface Events {} @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events); }
ensuite, la fonction updateOnFileDescriptorEventListenerLocked a été appelée
private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,) OnFileDescriptorEventListener listener) { final int fdNum = fd.getInt$(); int index = -1; FileDescriptorRecord record = null; if (mFileDescriptorRecords != null) { index = mFileDescriptorRecords.indexOfKey(fdNum); if (index >= 0) { record = mFileDescriptorRecords.valueAt(index); if (record != null && record.mEvents == events) { return; } } } if (events != 0) { events |= OnFileDescriptorEventListener.EVENT_ERROR; if (record == null) { if (mFileDescriptorRecords == null) { mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>(); } record = new FileDescriptorRecord(fd, events, listener);//fd est stocké dans l'objet FileDescriptorRecord mFileDescriptorRecords.put(fdNum, record);//mFileDescriptorRecords est ensuite stocké } else { record.mListener = listener; record.mEvents = events; record.mSeq += 1; } nativeSetFileDescriptorEvents(mPtr, fdNum, events);//Appel de la fonction native } else if (record != null) { record.mEvents = 0; mFileDescriptorRecords.removeAt(index); } }
native a appelé la fonction setFileDescriptorEvents de NativeMessageQueue
static void android_os_MessageQueue_nativeSetFileDescriptorEvents(JNIEnv* env, jclass clazz, jlong ptr, jint fd, jint events) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->setFileDescriptorEvents(fd, events); }
setFileDescriptorEvents函数,这个addFd就是调用的第二个addFd,因此我们可以肯定NativeMessageQueue继承了LooperCallback
void NativeMessageQueue::setFileDescriptorEvents(int fd, int events) { if (events) { int looperEvents = 0; if (events & CALLBACK_EVENT_INPUT) { looperEvents |= Looper::EVENT_INPUT; } if (events & CALLBACK_EVENT_OUTPUT) { looperEvents |= Looper::EVENT_OUTPUT; } mLooper->addFd(fd, Looper::POLL_CALLBACK, looperEvents, this, reinterpret_cast<void*>(events)); } else { mLooper->removeFd(fd); } }
果然是,需要实现handleEvent函数
class NativeMessageQueue : public MessageQueue, public LooperCallback { public: NativeMessageQueue(); virtual ~NativeMessageQueue(); virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj); void pollOnce(JNIEnv* env, jobject obj, int timeoutMillis); void wake(); void setFileDescriptorEvents(int fd, int events); virtual int handleEvent(int fd, int events, void* data);
handleEvent est appelée après epoll_wait dans looper, lorsque le fd ajouté a des données, cette fonction est appelée
int NativeMessageQueue::handleEvent(int fd, int looperEvents, void* data) { int events = 0; if (looperEvents & Looper::EVENT_INPUT) { events |= CALLBACK_EVENT_INPUT; } if (looperEvents & Looper::EVENT_OUTPUT) { events |= CALLBACK_EVENT_OUTPUT; } if (looperEvents & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP | Looper::EVENT_INVALID)) { events |= CALLBACK_EVENT_ERROR; } int oldWatchedEvents = reinterpret_cast<intptr_t>(data); int newWatchedEvents = mPollEnv->CallIntMethod(mPollObj, gMessageQueueClassInfo.dispatchEvents, fd, events); //Appeler le rappel if (!newWatchedEvents) { return 0; // Désenregistrer le fd } if (newWatchedEvents != oldWatchedEvents) { setFileDescriptorEvents(fd, newWatchedEvents); } return 1; }
Enfin, dans java, la méthode dispatchEvents de MessageQueue est appelée en retour au niveau JNI, puis appelle les fonctions de rappel enregistrées précédemment
// Appelé à partir du code natif. private int dispatchEvents(int fd, int events) { // Obtenir l'enregistrement du descripteur de fichier et tout état susceptible de changer. final FileDescriptorRecord record; final int oldWatchedEvents; final OnFileDescriptorEventListener listener; final int seq; synchronized (this) { record = mFileDescriptorRecords.get(fd);//Obtenir FileDescriptorRecord par fd if (record == null) { return 0; // faux, aucun écouteur enregistré } oldWatchedEvents = record.mEvents; events &= oldWatchedEvents; // filtrer les événements en fonction de l'ensemble surveillé actuel if (events == 0) { return oldWatchedEvents; // faux, les événements surveillés ont changé } listener = record.mListener; seq = record.mSeq; } // Appeler l'écouteur en dehors du verrou. int newWatchedEvents = listener.onFileDescriptorEvents(//appel de retour de l'écouteur record.mDescriptor, events); if (newWatchedEvents != 0) { newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR; } // Mettre à jour l'enregistrement du descripteur de fichier si l'écouteur a changé l'ensemble // les événements à surveiller et l'écouteur lui-même n'ont pas été mis à jour depuis. if (newWatchedEvents != oldWatchedEvents) { synchronized (this) { int index = mFileDescriptorRecords.indexOfKey(fd); if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record && record.mSeq == seq) { record.mEvents = newWatchedEvents; if (newWatchedEvents == 0) { mFileDescriptorRecords.removeAt(index); } } } } // Retourner le nouveau jeu d'événements à surveiller pour que le code natif s'en occupe. return newWatchedEvents; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#oldtoolbag.com(在发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。