Meilleure utilisation de HandlerThread sur d’autres classes similaires

J’essaie de comprendre le meilleur cas d’utilisation de HandlerThread .

Selon la définition:

“Classe pratique pour démarrer un nouveau thread qui a un looper. Le looper peut ensuite être utilisé pour créer des classes de gestionnaires. Notez que start () doit toujours être appelé.”

Je peux avoir tort mais des fonctionnalités similaires que je peux atteindre en utilisant un Thread , un Looper et un Handler . Alors, quand dois-je utiliser HandlerThread ? Un exemple serait vraiment utile.

Voici un exemple concret où HandlerThread devient pratique. Lorsque vous vous enregistrez pour les frameworks d’aperçu de l’appareil photo, vous les recevez dans le rappel onPreviewFrame() . La documentation explique que ce rappel est appelé sur le thread d’événement ouvert (int) à partir duquel il a été appelé .

En général, cela signifie que le rappel sera appelé sur le thread principal (UI). Ainsi, la tâche de traiter les énormes tableaux de pixels peut restr bloquée lorsque les menus sont ouverts, les animations sont animées ou même si des statistiques sont imprimées à l’écran.

La solution simple est de créer un new HandlerThread() et de déléguer Camera.open() à ce thread (je l’ai fait via post(Runnable) , vous n’avez pas besoin d’implémenter Handler.Callback ).

Notez que tout le travail avec Camera peut être effectué comme d’habitude, vous n’avez pas à déléguer Camera.startPreview() ou Camera.setPreviewCallback() au HandlerThread. Pour être du bon côté, j’attends que Camera.open(int) se termine avant de continuer sur le thread principal (ou quel autre thread a été utilisé pour appeler Camera.open() avant le changement).


Donc, si vous commencez avec le code

 try { mCamera = Camera.open(1); } catch (RuntimeException e) { Log.e(LOG_TAG, "failed to open front camera"); } // some code that uses mCamera immediately 

D’abord, extrayez-le tel quel dans une méthode privée:

 private void oldOpenCamera() { try { mCamera = Camera.open(1); } catch (RuntimeException e) { Log.e(LOG_TAG, "failed to open front camera"); } } 

et au lieu d’appeler oldOpenCamera() utilisez simplement newOpencamera() :

 private void newOpenCamera() { if (mThread == null) { mThread = new CameraHandlerThread(); } synchronized (mThread) { mThread.openCamera(); } } private CameraHandlerThread mThread = null; private static class CameraHandlerThread extends HandlerThread { Handler mHandler = null; CameraHandlerThread() { super("CameraHandlerThread"); start(); mHandler = new Handler(getLooper()); } synchronized void notifyCameraOpened() { notify(); } void openCamera() { mHandler.post(new Runnable() { @Override public void run() { oldOpenCamera(); notifyCameraOpened(); } }); try { wait(); } catch (InterruptedException e) { Log.w(LOG_TAG, "wait was interrupted"); } } } 

Notez que l’intégralité de la communication inter-thread notify ()wait () n’est pas nécessaire si vous n’accédez pas à mCamera dans le code d’origine immédiatement après l’avoir ouvert.

Mise à jour: Ici, la même approche est appliquée à l’accéléromètre: Capteur d’ accéléromètre dans un fil séparé

Voici un lien vers le code source pour HandlerThread et Looper .

Si vous regardez les deux, vous verrez qu’un HandlerThread est exactement ce qu’il dit – un moyen pratique de démarrer un Thread avec un Looper . Pourquoi cela existe-t-il? Parce que les threads, par défaut, n’ont pas de boucle de message . Le HandlerThread est juste un moyen facile d’en créer un. Pourriez-vous dupliquer cette fonction avec Handler , Thread et Looper – à en juger par le code source – la réponse est oui.

Un Executor est différent. Un Executor prend les tâches exécutables soumises et – devinez quoi – les exécute. Pourquoi est-ce nécessaire? Il vous permet de découpler l’exécution de la tâche de sa substance réelle . Quand utiliseriez-vous cela? Supposons que vous deviez exécuter plusieurs tâches à la fois. Vous pouvez choisir, à l’aide d’un Executor , de les exécuter tous sur un seul thread afin qu’ils soient exécutés sérieusement. Ou vous pouvez utiliser un pool de threads fixe pour que certains, mais pas tous, soient exécutés en même temps. Dans les deux cas, la substance de la tâche – c’est-à-dire ce qu’elle fait réellement – est distincte de la manière dont elle est exécutée.