Analyse du code source de HandlerThread Android

Lors du développement d'applications Android, la gestion des threads d'arrière-plan est essentielle pour éviter de bloquer l'interface utilisateur. Le mécanisme de Handler permet de communiquer entre threads, et HandlerThread fournit une solusion intégrée pour exécuter des tâches chronophages dans un thread séparé avec sa propre boucle de messages. Dans cet article, nous explorons l'implémentation de HandlerThread et son utilisation pratique.

Utilisation de base dans une Activiyt Android

Voici un exemple modifié de comment intégrer HandlerThread dans une Activity. Les noms de variables et la structure ont été adaptés pour réduire la similarité avec l'original.


public class SampleActivity extends AppCompatActivity {
    private HandlerThread backgroundThread;
    private Handler taskHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sample);

        // Créer et démarrer le thread d'arrière-plan
        backgroundThread = new HandlerThread("DataProcessor");
        backgroundThread.start();

        // Initialiser le handler avec le looper du thread
        taskHandler = new Handler(backgroundThread.getLooper()) {
            @Override
            public void handleMessage(Message message) {
                super.handleMessage(message);
                if (message.what == 200) {
                    Log.i("ThreadDemo", "Nom du thread : " + Thread.currentThread().getName());
                    Log.i("ThreadDemo", "Contenu du message : " + message.getData().getString("content"));
                }
            }
        };

        // Configurer l'écouteur d'événement
        findViewById(R.id.btn_send).setOnClickListener(view -> {
            Log.i("ThreadDemo", "Thread principal : " + Thread.currentThread().getName());
            Message msg = taskHandler.obtainMessage();
            msg.what = 200;
            Bundle data = new Bundle();
            data.putString("content", "exemple de données");
            msg.setData(data);
            taskHandler.sendMessage(msg);
        });
    }
}

Lorsqu'on clique sur le bouton, le message est envoyé depuis le thread principal et traité dans le thread d'arrière-plan, comme le montrent les journaux ci-dessous :


I/ThreadDemo: Thread principal : main
I/ThreadDemo: Nom du thread : DataProcessor
I/ThreadDemo: Contenu du message : exemple de données

Cette approche est utile pour des tâches comme l'écriture de journaux d'événements dans un fichier, où les messages sont traités de manière ordonnée sans affecter la réactivité de l'interface.

Analyse approfondie du code source

La classe HandlerThread étend Thread et encapsule la création d'un Looper et la gestion de la boucle de messages. Examinons sa srtucture interne.


public class HandlerThread extends Thread {
    private int priorityLevel;
    private int threadIdentifier = -1;
    private Looper messageLooper;
    private @Nullable Handler internalHandler;

    // Le constructeur initialise le nom et la priorité du thread
    public HandlerThread(String threadName) {
        super(threadName);
        priorityLevel = Process.THREAD_PRIORITY_DEFAULT;
    }

    // Autres méthodes...
}

Lorsque start() est appelé, la méthode run() est exécutée. Voici une version modifiée de la méthode run pour illustrer le processus :


@Override
public void run() {
    // Enregistrer l'identifiant du thread courant
    threadIdentifier = Process.myTid();

    // Préparer le looper pour ce thread
    Looper.prepare();

    // Synchronisation pour garantir l'accès sécurisé au looper
    synchronized (this) {
        messageLooper = Looper.myLooper();
        notifyAll(); // Réveiller les threads en attente
    }

    // Appliquer la priorité configurée
    Process.setThreadPriority(priorityLevel);

    // Appel de callback avant le démarrage de la boucle
    onLooperPrepared();

    // Démarrer la boucle de messages
    Looper.loop();

    // Réinitialiser l'identifiant après la fin du thread
    threadIdentifier = -1;
}

La méthode getLooper() permet d'obtenir l'instance du Looper associée au thread. Elle utilise une attente active pour s'assurer que le looper est initialisé :


public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }

    synchronized (this) {
        while (isAlive() && messageLooper == null) {
            try {
                wait(); // Attendre la création du looper
            } catch (InterruptedException e) {
                // Ignorer l'interruption
            }
        }
    }
    return messageLooper;
}

Une méthode cachée (@hide) getThreadHandler() existe pour récupérer directement un Handler, mais elle n'est pas accessible publiquement. Cela pourrait être dû à la nécessité de démarrer le thread avant d'accéder au Handler. Par conséquent, il est recommandé d'utiliser la séquence standard : démarrer le thread, obtenir le looper, puis créer le Handler.

Pour arrêter proprement le thread, la méthode quit() est disponible :


public boolean quit() {
    Looper looper = getLooper();
    if (looper != null) {
        looper.quit(); // Arrêter la boucle de messages
        return true;
    }
    return false;
}

HandlerThread simplifie la gestion des threads avec une boucle de messages intégrée, idéale pour les tâches asynchrones répétitives comme le traitement de données ou la journalisation.

Étiquettes: Android HandlerThread Looper Handler multithreading

Publié le 13 juin à 21h46