web-dev-qa-db-fra.com

Meilleure utilisation de HandlerThread par rapport à d'autres classes similaires

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

Selon la définition:

"Classe utile pour démarrer un nouveau thread ayant une boucle. Cette boucle peut ensuite être utilisée pour créer des classes de gestionnaire. Notez que start () doit toujours être appelé."

Je peux me tromper, mais je peux obtenir des fonctionnalités similaires en utilisant Thread, Looper et Handler. Alors, quand devrais-je utiliser HandlerThread? Un exemple serait vraiment utile.

67
Androidme

Voici un exemple concret dans lequel HandlerThread devient pratique. Lorsque vous vous enregistrez pour des images d'aperçu de l'appareil photo, vous les recevez sous forme de rappel onPreviewFrame(). Le documentation explique que Ce rappel est appelé lorsque l'événement thread open (int) a été appelé à partir de.

Cela signifie généralement que le rappel est appelé sur le thread principal (UI). Ainsi, la tâche consistant à gérer les énormes tableaux de pixels peut rester bloquée lorsque les menus sont ouverts, les animations animées ou même si des statistiques sont imprimées à l'écran.

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

Notez que tous les autres travaux avec l'appareil photo peuvent être effectués comme d'habitude, vous n'avez pas à déléguer Camera.startPreview() ou Camera.setPreviewCallback() à HandlerThread. Pour être du bon côté, je attendez pour que le Camera.open(int) actuel soit terminé avant de continuer sur le fil principal (ou quel que soit le fil utilisé pour appeler Camera.open() avant la modification).


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

premier extrait comme si 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’ensemble notify () - wait () inter- la communication par fil 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’accléromètre dans un fil séparé

83
Alex Cohn

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

Si vous regardez les deux, vous verrez qu'un HandlerThread est exactement ce qu'il est écrit - un moyen pratique de démarrer un Thread qui a un Looper. Pourquoi cela existe-t-il? Parce que les threads, par défaut, n'ont pas de boucle de message . Le HandlerThread n'est qu'un moyen simple 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 - devine ce qui 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 ayez eu une situation nécessitant l'exécution de plusieurs tâches en même temps. Vous pouvez choisir, à l'aide de Executor, de les exécuter tous sur un seul thread afin qu'ils soient exécutés en série. Vous pouvez également 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.

15
Rarw