Je sais que les threads ont une file d'attente de messages et les gestionnaires peuvent leur envoyer des runnables ou des messages, mais lorsque je profile mon Android en utilisant Android outils Studio, il y a un processus étrange:
Android.os.MessageQueue.nativePollOnce
Il utilise le CPU plus que tous les autres processus. Qu'est-ce que c'est et comment puis-je réduire le temps que le processeur y consacre? Vous pouvez trouver le résultat du profileur ci-dessous.
Réponse courte:
La méthode nativePollOnce
est utilisée pour "attendre" jusqu'à ce que le prochain Message
devienne disponible. Si le temps passé pendant cet appel est long, votre thread principal (UI) n'a pas vraiment de travail à faire et attend que les prochains événements soient traités. Il n'y a pas lieu de s'en inquiéter.
Explication:
Parce que le thread "principal" est chargé de dessiner l'interface utilisateur et de gérer divers événements, il est Runnable
a une boucle qui traite tous ces événements. La boucle est gérée par un Looper
et son travail est assez simple: il traite tous les messages dans le MessageQueue
.
Un Message
est ajouté à la file d'attente par exemple en réponse aux événements d'entrée, comme rappel de rendu de trame ou même vos propres appels _Handler.post
_. Parfois, le thread principal n'a aucun travail à faire (c'est-à-dire, aucun message dans la file d'attente), ce qui peut arriver par exemple juste après avoir terminé le rendu d'une seule image (le fil vient de dessiner une image et est prêt pour la suivante, attend juste le bon moment). Deux méthodes Java de la classe MessageQueue
nous intéressent: Message next()
et boolean enqueueMessage(Message, long)
. Message next()
, comme son nom l'indique, prend et renvoie le message suivant de la file d'attente. Si la file d'attente est vide (et qu'il n'y a rien à retourner), la méthode appelle native void nativePollOnce(long, int)
qui se bloque jusqu'à ce qu'un nouveau message soit ajouté. À ce stade, vous pourriez vous demander comment nativePollOnce
sait quand se réveiller. Voilà une très bonne question. Lorsqu'un Message
est ajouté à la file d'attente, le framework appelle la méthode enqueueMessage
, qui non seulement insère le message dans la file d'attente, mais appelle également native static void nativeWake(long)
, si nécessaire pour réveiller la file d'attente. La magie de base de nativePollOnce
et nativeWake
se produit dans le code natif (en fait, C++) . Native MessageQueue utilise un appel système Linux nommé epoll
, qui permet de surveiller un descripteur de fichier pour les événements IO. nativePollOnce
appelle _epoll_wait
_ sur un certain descripteur de fichier, tandis que nativeWake
écrit dans le descripteur, qui est l'une des opérations IO, _epoll_wait
_ attend. Le noyau retire alors le thread en attente d'epoll de l'état d'attente et le thread procède à la gestion du nouveau message. Si vous connaissez les méthodes Object.wait()
et Object.notify()
de Java, vous pouvez imaginer que nativePollOnce
est un équivalent approximatif de Object.wait()
et nativeWake
pour Object.notify()
, sauf qu'ils sont implémentés de manière complètement différente: nativePollOnce
utilise epoll
et Object.wait()
utilise futex
Appel Linux. Il convient de noter que ni nativePollOnce
ni Object.wait()
ne gaspillent les cycles de processeur, car lorsqu'un thread entre dans l'une ou l'autre méthode, il devient désactivé à des fins de planification des threads (en citant le javadoc pour le Object
classe). Cependant, certains profileurs peuvent reconnaître par erreur les threads en attente d'époll (ou même en attente d'objet) comme exécutant et consommant du temps CPU, ce qui est incorrect. Si ces méthodes gaspillaient réellement les cycles de processeur, toutes les applications inactives utiliseraient 100% du processeur, chauffant et ralentissant l'appareil.
Conclusion:
Vous ne devriez pas vous soucier de nativePollOnce
. Cela indique simplement que le traitement de tous les messages est terminé et que le thread attend le prochain. Eh bien, cela signifie simplement que vous ne donnez pas trop de travail à votre thread principal;)