English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
I messaggi sono conservati in una coda di messaggi, e il thread di loop dei messaggi gira attorno a questa coda in un ciclo infinito fino a quando il thread non viene uscito. Se c'è un messaggio nella coda, il thread di loop dei messaggi lo prenderà e lo distribuirà al rispettivo Handler per essere elaborato; se la coda non contiene messaggi, il thread di loop dei messaggi entrerà in uno stato di attesa libera, aspettando l'arrivo del prossimo messaggio. Quando si scrive un'applicazione Android, quando il compito eseguito dal programma è molto pesante, per evitare di bloccare la thread principale dell'UI e causare un ANR, il nostro metodo tipico è creare un sotto-thread per completare un compito specifico. Quando si crea un sotto-thread, ci sono due opzioni: una è creare un sotto-thread senza loop di messaggi attraverso l'oggetto Thread; l'altra è creare un sotto-thread con un loop di messaggi, e creare un sotto-thread con un loop di messaggi a causa di due metodi di implementazione, uno è utilizzare direttamente la classe HandlerThread fornita da Android per generare direttamente un oggetto di thread con un loop di messaggi, e l'altro metodo è avviare un loop di messaggi all'interno del metodo run() del thread utilizzando il seguente metodo:
1. Meccanismo di messaggi utilizzato
Di solito i messaggi sono composti da un thread di messaggi e un Handler, ora vediamo un messaggio Handler in PowerManagerService:
mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/); mHandlerThread.start(); mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
qui ServiceThread è un HandlerThread, quando si crea un Handler, è necessario passare il looper di HandlerThread, altrimenti sarà il looper della thread corrente di default.
e ogni handler, approssimativamente come segue:
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; } } }
Principio del meccanismo di messaggi di due
Quindi guardiamo prima la funzione run principale di HandlerThread:
public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper();//Dopo l'assegnazione notifyall,主要是因为getLooper funzione restituisce mLooper notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
Di nuovo guardiamo la funzione prepare di Lopper, alla fine crea un nuovo oggetto Looper e lo mette nella variabile locale del thread.
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Solo uno Looper può essere creato per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
La funzione di costruzione di Looper crea una MessageQueue
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
Di nuovo guardiamo la funzione di costruzione di MessageQueue, in cui nativeInit è un metodo nativo e il valore di ritorno è salvato in mPtr ovviamente utilizzando un variabile a lunghezza intera come puntatore
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }
La funzione nativa crea l'oggetto NativeMessageQueue e restituisce il puntatore variabile.
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); }
Il costruttore di NativeMessageQueue è per ottenere mLooper, se non esiste, crea un nuovo Looper
NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) { mLooper = Looper::getForThread(); if (mLooper == NULL) { mLooper = new Looper(false); Looper::setForThread(mLooper); } }
Poi vediamo il costruttore di Looper, che chiama eventfd per creare un fd, eventfd è principalmente utilizzato per la comunicazione tra processi o thread, possiamo vedere questo blog di introduzione a 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 Create epoll in the C layer
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, "Impossibile aggiungere l'evento wake fd all'istanza epoll. 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("Errore nell'aggiungere eventi epoll per fd %d durante la ricostruzione dell'insieme epoll, errno=%d", request.fd, errno); } } }
Torniamo alla funzione run di HandlerThread, continuiamo ad analizzare la funzione loop di Looper
public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
Vediamo la funzione loop di Looper:
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("Nessun Looper; Looper.prepare() non è stato chiamato su questo thread."); } final MessageQueue queue = me.mQueue; // ottieni il mQueue del Looper // Assicurati che l'identità di questo thread sia quella del processo locale, // e mantenere traccia di cosa sia effettivamente quell'id token. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block questa funzione può bloccarsi, il blocco principale è epoll_wait if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; // le impronte di stampa fatte da 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); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. 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 funzione next della classe MessageQueue chiama principalmente la funzione nativa nativePollOnce e poi estrae un Message dalla coda dei messaggi
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; // il puntatore precedentemente conservato if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis);
Ecco la funzione nativa nativePollOnce, che convertiamo il puntatore precedente in NativeMessageQueue e chiamiamo la sua funzione 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 Blocco epoll_wait a livello c
La funzione pollOnce, la while in precedenza di questa funzione di solito non esiste e gestisce solo il caso in cui indent sia maggiore di 0, che di solito non accade, quindi possiamo guardare direttamente la funzione 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 - tornando con l'identificatore segnalato %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; return ident; } } if (result != 0) { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - tornando con il risultato %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 funzione pollInner principale chiama epoll_wait in modo bloccante e il livello Java calcola il tempo di blocco di ogni volta e lo passa al livello C, aspettando che arrivi un mWakeEventFd o che il fd aggiunto in precedenza abbia un evento prima di tornare epoll_wait.
int Looper::pollInner(int timeoutMillis) { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - in attesa: timeoutMillis=%d", this, timeoutMillis); #endif // Regola il timeout in base a quando è previsto il prossimo messaggio. 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 - prossimo messaggio in %" PRId64 "ns, timeout regolato: timeoutMillis=%d", this, mNextMessageUptime - now, timeoutMillis); #endif } // Poll. int result = POLL_WAKE; mResponses.clear(); // Svuota mResponses mResponseIndex = 0; // Stiamo per entrare in attesa. mPolling = true; struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); // epoll_wait blocca principalmente il thread qui, il tempo di blocco è anche passato dal livello java // Non più in attesa. mPolling = false; // Acquisire il blocco. mLock.lock(); // Ri-costruire il set epoll se necessario. if (mEpollRebuildRequired) { mEpollRebuildRequired = false; rebuildEpollLocked(); goto Fatto; } // Controllare l'errore di poll. if (eventCount < 0) { if (errno == EINTR) { goto Fatto; } ALOGW("Poll ha fallito con un errore inatteso, errno=%d", errno); result = POLL_ERROR; goto Fatto; } // Controllare il timeout di poll. if (eventCount == 0) { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - timeout", this); #endif result = POLL_TIMEOUT; goto Fatto; } // Gestire tutti gli eventi. #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - gestendo eventi da %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) {//Notificare l'evento di risveglio del thread if (epollEvents & EPOLLIN) { awoken(); } else { ALOGW("Ignorando eventi epoll inattesi 0x%x su evento di risveglio fd.", epollEvents); } } else { ssize_t requestIndex = mRequests.indexOfKey(fd);//Aggiungere evento fd prima 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));//Mettere in mResponses } else { ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " "no longer registered.", epollEvents, fd); } } } Fatto: ; // Invocare callback di messaggi in sospeso. mNextMessageUptime = LLONG_MAX; while (mMessageEnvelopes.size() != 0) { // This part is mainly for c layer messages, java layer messages are managed by themselves 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 - sending message: handler=%p, what=%d", this, handler.get(), message.what); #endif handler->handleMessage(message); } // rilascia handler mLock.lock(); mSendingMessage = false; result = POLL_CALLBACK; } else { // L'ultimo messaggio lasciato all'inizio della coda determina il prossimo tempo di sveglia. mNextMessageUptime = messageEnvelope.uptime; break; } } // Release lock. mLock.unlock(); // Invoke all response callbacks. for (size_t i = 0; i < mResponses.size(); i++) { // This is the handling of the previous addFd event, mainly to traverse mResponses and then call its callback 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 - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", this, response.request.callback.get(), fd, events, data); #endif //Chiamare il callback. Notare che il descrittore di file potrebbe essere chiuso da //il callback (e potenzialmente anche riutilizzato) prima che la funzione torni così //deve essere un po' attento quando rimuove il descrittore di file dopo. int callbackResult = response.request.callback->handleEvent(fd, events, data); if (callbackResult == 0) { removeFd(fd, response.request.seq); } //Cancella la riferimento del callback nella struttura della risposta immediatamente perché noi //non cancella la risposta della vettore fino alla prossima poll. response.request.callback.clear(); result = POLL_CALLBACK; } } return result; }
Continue to analyze the loop function of Looper, you can add your own printing to debug the code, previously call Message's target's dispatchMessage to allocate messages
for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging;// own printing 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); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. 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 Aggiungere stampa di debug
Vediamo prima di tutto come aggiungere la stampa, attraverso la funzione setMessageLogging di Lopper per stampare
public void setMessageLogging(@Nullable Printer printer) { mLogging = printer; } Printer è un'interfaccia public interface Printer { /** * Scrivi una riga di testo nell'output. Non c'è bisogno di terminare * La stringa fornita con una nuova riga. */ void println(String x); }
2.4 Gestione dei messaggi di livello java
Passiamo ora alla distribuzione dei messaggi, prima di tutto chiamando la funzione obtainMessage di Handler
Message msg = mHandler.obtainMessage(MSG_CHECK_WAKE_LOCK_ACQUIRE_TIMEOUT); msg.setAsynchronous(true); mHandler.sendMessageDelayed(msg, WAKE_LOCK_ACQUIRE_TOO_LONG_TIMEOUT);
Prima di tutto, vediamo che obtainMessage chiama la funzione obtain di Message
public final Message obtainMessage(int what) { return Message.obtain(this, what); }
La funzione obtain di Message crea un nuovo Message e poi imposta il suo target come il suo Handler
public static Message obtain(Handler h, int what) { Message m = obtain();//Si tratta di creare un nuovo Message m.target = h; m.what = what; return m; }
Rivediamo la distribuzione dei messaggi precedenti
msg.target.dispatchMessage(msg);Alla fine chiama la funzione dispatchMessage di Handler, alla fine, nel Handler, si gestisce il messaggio in base a diverse situazioni.
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg);//Questo è il caso di invio tramite post, con Runnable } else { if (mCallback != null) {//Questo è il caso in cui il handler passa come argomento mCallback come callback if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg);//Alla fine si tratta della gestione del messaggio implementata } }
2.3 Invio di messaggi a livello java
Rivediamo poi l'invio di messaggi a livello java, che principalmente chiama le funzioni sendMessage post di Handler, alla fine chiama questa funzione sottostante
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue\ Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); }
Rivediamo poi che l'invio di messaggi a livello java alla fine chiama la funzione enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
Finalmente in enqueueMessage, aggiungi il messaggio alla coda dei messaggi e, se necessario, chiama la funzione nativeWake del livello C
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) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread" 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) { // 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); } } return true; }
我们来看看这个native方法,最后也是调用了Looper的wake函数
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(); }
Looper类的wake函数,只是向mWakeEventfd中写入了一些内容,这个fd只是一个通知,类似于pipe,最后会唤醒epoll_wait,线程就不会阻塞,继续先发送c层消息,然后处理之前addFd的事件,然后处理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 Invio messaggi a livello c
È possibile inviare messaggi anche a livello c, principalmente chiamando la funzione sendMessageAtTime di Looper, i parametri includono un handler che è una callback, mettiamo il messaggio in 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; { // acquire lock 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); // Ottimizzazione: Se Looper sta attualmente inviando un messaggio, allora possiamo saltare // la chiamata a wake() perché la cosa successiva che Looper farà dopo il processing // messages per decidere quando dovrebbe essere il prossimo tempo di riawakenamento. Infatti, fa // non importa nemmeno se questo codice sta girando sul thread Looper. if (mSendingMessage) { return; } } // 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 - sending message: handler=%p, what=%d", this, handler.get(), message.what); #endif handler->handleMessage(message); } // rilascia handler mLock.lock(); mSendingMessage = false; result = POLL_CALLBACK; } else { // L'ultimo messaggio lasciato all'inizio della coda determina il prossimo tempo di sveglia. mNextMessageUptime = messageEnvelope.uptime; break; } }
C'è un file Looper_test.cpp, che spiega molti modi di usare Looper, guardiamo
sp<StubMessageHandler> handler = new StubMessageHandler(); mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1)); StubMessageHandler eredita MessageHandler deve implementare il metodo handleMessage class StubMessageHandler : public MessageHandler { public: Vector<Message> messages; virtual void handleMessage(const Message& message) { messages.push(message); } };
Guardiamo anche Message e la classe MessageHandler
struct Message { Message() : what(0) { } Message(int what) : what(what) { } /* Il tipo di messaggio. (l'interpretazione è lasciata al gestore) */ int what; }; /** * Interfaccia per un gestore di messaggi Looper. * * The Looper mantiene un riferimento forte al gestore dei messaggi ogni volta che ha * Un messaggio da consegnare. Assicurati di chiamare Looper::removeMessages * to remove any pending messages destined for the handler so that the handler * can be destroyed. */ class MessageHandler : public virtual RefBase { protected: virtual ~MessageHandler() { } public: /** * Handles a message. */ virtual void handleMessage(const Message& message) = 0; };
2.5 c layer addFd
We can also add fd to the epoll thread in Looper.cpp's addFd, and when fd has data, we can also handle the corresponding data. Let's first look at the addFd function, and we notice that there is a 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("Invalid attempt to set NULL callback but not allowed for this looper."); return -1; } if (ident < 0) { ALOGE("Invalid attempt to set NULL callback with ident < 0."); return -1; } } else { ident = POLL_CALLBACK; } { // acquire lock 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; // riserva numero di sequenza -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); // aggiungi a epoll if (epollResult < 0) { ALOGE("Errore nell'aggiungere eventi epoll per fd %d, errno=%d", fd, errno); return -1; } mRequests.add(fd, request); // aggiungi a mRequests } else { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); // aggiornamento if (epollResult < 0) { if (errno == ENOENT) { // tollera ENOENT perché significa che è stato creato un descrittore di file più vecchio // chiuso prima che il callback venisse deregistrato e nel frattempo un nuovo // è stato creato un descrittore di file con lo stesso numero e ora è // registrato per la prima volta. Questo errore può verificarsi naturalmente // quando un callback ha l'effetto collaterale di chiudere il descrittore di file // prima di tornare e deregistrarsi. Numero di sequenza di callback // verifica ulteriormente che la corsa sia benigna. }} // Sfortunatamente a causa delle limitazioni del kernel dobbiamo ricostruire l'epoll // impostare da zero perché potrebbe contenere un vecchio file handle che stiamo // ora impossibile rimuoverlo poiché il suo file descriptor non è più valido. // Non sarebbe mai successo se avessimo utilizzato il sistema poll // chiamare invece, ma questo approccio comporta altri svantaggi. #if DEBUG_CALLBACKS ALOGD("%p ~ addFd - EPOLL_CTL_MOD fallito a causa del file descriptor " "in fase di riciclaggio, tornando a EPOLL_CTL_ADD, errno=%d", this, errno); #endif epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); if (epollResult < 0) { ALOGE("Errore nella modifica o aggiunta degli eventi epoll per fd %d, errno=%d", fd, errno); return -1; } scheduleEpollRebuildLocked(); } else { ALOGE("Errore nella modifica degli eventi epoll per fd %d, errno=%d", fd, errno); return -1; } } mRequests.replaceValueAt(requestIndex, request); } } return 1; }
Nel metodo pollOnce, inizialmente cerchiamo l'fd corrispondente in mRequests, quindi creiamo una nuova Response in pushResponse e la abbiniamo alla 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 " "no longer registered.", epollEvents, fd); } }
Below we will iterate over mResponses and then call the callback in its 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 - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", this, response.request.callback.get(), fd, events, data); #endif //Chiamare il callback. Notare che il descrittore di file potrebbe essere chiuso da //il callback (e potenzialmente anche riutilizzato) prima che la funzione torni così //deve essere un po' attento quando rimuove il descrittore di file dopo. int callbackResult = response.request.callback->handleEvent(fd, events, data); if (callbackResult == 0) { removeFd(fd, response.request.seq); } //Cancella la riferimento del callback nella struttura della risposta immediatamente perché noi //non cancella la risposta della vettore fino alla prossima poll. response.request.callback.clear(); result = POLL_CALLBACK; } }
Vediamo come viene utilizzato Looper_test.cpp?
Pipe pipe; StubCallbackHandler handler(true); handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);
Vediamo la funzione setCallback di handler
class CallbackHandler { public: void setCallback(const sp<Looper>& looper, int fd, int events) { looper->addFd(fd, 0, events, staticHandler, this);//chiamata alla funzione addFd di looper e callback } protected: virtual ~CallbackHandler() { } virtual int handler(int fd, int events) = 0; private: static int staticHandler(int fd, int events, void* data) { // Questo è la funzione di callback 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) { // Questo è chiamato qui tramite la funzione di callback callbackCount += 1; this->fd = fd; this->events = events; return nextResult; } };
Uniamo addFd di Looper per vedere, quando callback è presente, creiamo un nuovo 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); }
qui Looper_callbackFunc è un typedef
typedef int (*Looper_callbackFunc)(int fd, int events, void* data);
Rivediamo 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); }
Alla fine chiamiamo callback->handleEvent(fd, events, data), e callback è SimpleLooperCallback, qui data è il puntatore this passato prima come CallbackHandler.
Quindi alla fine chiamiamo staticHandler, e data->handler è this->handler, infine chiamiamo la funzione handler di StubCallbackHandler.
Naturalmente, possiamo evitare di rendere le cose così complicate e utilizzare direttamente la seconda funzione addFd, ovviamente dobbiamo definire una classe ourselves per implementare la classe LooperCallBack, il che rende tutto molto più semplice.
int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);
2.6 Aggiunta di fd a livello java
Credevo che si potesse aggiungere fd solo nel Looper di livello c, ma in realtà anche a livello java è stato implementato tramite JNI.
Possiamo implementare questa funzione tramite addOnFileDescriptorEventListener nella 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); } }
Vediamo di nuovo il 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); }
Successivamente è stato chiamato il metodo updateOnFileDescriptorEventListenerLocked
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 viene salvato nell'oggetto FileDescriptorRecord mFileDescriptorRecords.put(fdNum, record);//mFileDescriptorRecords viene salvato } else { record.mListener = listener; record.mEvents = events; record.mSeq += 1; } nativeSetFileDescriptorEvents(mPtr, fdNum, events);//chiama la funzione nativa } else if (record != null) { record.mEvents = 0; mFileDescriptorRecords.removeAt(index); } }
native ha chiamato la funzione setFileDescriptorEvents di 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 funzione, questo addFd è chiamato il secondo addFd, quindi possiamo essere certi che NativeMessageQueue eredita 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); } }
Effettivamente, è necessario implementare la funzione handleEvent
class NativeMessageQueue : public MessageQueue, public LooperCallback { public: NativeMessageQueue(); virtuale ~NativeMessageQueue(); virtuale 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 è chiamato dopo epoll_wait nel looper, quando il fd aggiunto ha dati, viene chiamata questa funzione
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); // chiama il callback if (!newWatchedEvents) { return 0; // deregistra il fd } if (newWatchedEvents != oldWatchedEvents) { setFileDescriptorEvents(fd, newWatchedEvents); } return 1; }
Infine, nel java di MessageQueue, dispatchEvents è chiamato a livello JNI e poi chiama le funzioni di callback registrate in precedenza
// Chiamato dal codice nativo. private int dispatchEvents(int fd, int events) { // Ottieni la registrazione del descrittore di file e qualsiasi stato che potrebbe cambiare. final FileDescriptorRecord record; final int oldWatchedEvents; final OnFileDescriptorEventListener listener; final int seq; synchronized (this) { record = mFileDescriptorRecords.get(fd);//Ottieni FileDescriptorRecord tramite fd if (record == null) { return 0; // falso, nessun ascoltatore registrato } oldWatchedEvents = record.mEvents; events &= oldWatchedEvents; // filtra gli eventi in base al set osservato corrente if (events == 0) { return oldWatchedEvents; // falso, gli eventi osservati sono cambiati } listener = record.mListener; seq = record.mSeq; } // Esegui l'ascoltatore al di fuori del blocco. int newWatchedEvents = listener.onFileDescriptorEvents(//callback dell'ascoltatore record.mDescriptor, events); if (newWatchedEvents != 0) { newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR; } // Aggiorna la registrazione del descrittore di file se l'ascoltatore ha cambiato l'insieme di // eventi da osservare e l'ascoltatore stesso non è stato aggiornato da allora. 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); } } } } // Restituisce il nuovo set di eventi da osservare per che il codice nativo si occupi di essi. return newWatchedEvents; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#oldtoolbag.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。