477 votes

Les bases d'Android : exécuter du code dans le thread de l'interface utilisateur

Du point de vue de l'exécution du code dans le thread UI, y a-t-il une différence entre :

MainActivity.this.runOnUiThread(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

ou

MainActivity.this.myView.post(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

et

private class BackgroundTask extends AsyncTask<String, Void, Bitmap> {
    protected void onPostExecute(Bitmap result) {
        Log.d("UI thread", "I am the UI thread");
    }
}

0 votes

Pour clarifier ma question : Je suppose que ces codes sont appelés depuis un thread de service, typiquement un listener. Je suppose également qu'il y a un travail important à accomplir soit dans la fonction doInBackground() de l'AsynkTask, soit dans une nouvelle Task(...) appelée avant les deux premiers extraits. De toute façon, la fonction onPostExecute() de l'AsyncTask est placée à la fin de la file d'attente des événements, n'est-ce pas ?

301voto

CommonsWare Points 402670

Aucun de ces éléments n'est exactement le même, mais ils auront tous le même effet net.

La différence entre le premier et le second est que si vous êtes sur le thread principal de l'application lors de l'exécution du code, le premier ( runOnUiThread() ) exécutera le Runnable immédiatement. Le deuxième ( post() ) met toujours le Runnable à la fin de la file d'attente des événements, même si vous êtes déjà sur le thread principal de l'application.

La troisième, en supposant que vous créez et exécutez une instance de BackgroundTask perdra beaucoup de temps à extraire un thread du pool de threads, pour exécuter un no-op par défaut. doInBackground() avant de faire ce qui revient à un post() . C'est de loin la moins efficace des trois. Utilisez AsyncTask si vous avez réellement du travail à faire dans un fil d'arrière-plan, et pas seulement pour l'utilisation de onPostExecute() .

28 votes

Notez également que AsyncTask.execute() vous oblige de toute façon à appeler depuis le thread de l'interface utilisateur, ce qui rend cette option inutile pour le cas d'utilisation consistant à simplement exécuter du code sur le thread de l'interface utilisateur à partir d'un thread d'arrière-plan, à moins que vous ne déplaciez tout votre travail d'arrière-plan dans l'application doInBackground() et utiliser AsyncTask correctement.

0 votes

@kabuko comment je peux vérifier que j'appelle. AsyncTask depuis le fil de l'interface utilisateur ?

0 votes

@NeilGaliaskarov Cela semble être une option solide : stackoverflow.com/a/7897562/1839500

274voto

pomber Points 1046

J'aime celui de Commentaire du HPP il peut être utilisé partout sans aucun paramètre :

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

4 votes

Quelle est l'efficacité de ce système ? Est-elle équivalente aux autres options ?

65voto

vasart Points 3417

Il existe une quatrième façon d'utiliser Handler

new Handler().post(new Runnable() {
    @Override
    public void run() {
        // Code here will run in UI thread
    }
});

59 votes

Vous devriez faire attention avec ça. En effet, si vous créez un handler dans un thread non UI, vous enverrez des messages au thread non UI. Par défaut, un handler envoie des messages au thread dans lequel il a été créé.

113 votes

À exécuter sur le fil d'exécution principal de l'interface utilisateur. new Handler(Looper.getMainLooper()).post(r) qui est la manière préférée comme Looper.getMainLooper() fait un appel statique à main, alors que postOnUiThread() doit avoir une instance de MainActivity dans le champ d'application.

1 votes

@HPP Je ne connaissais pas cette méthode, ce sera un bon moyen quand vous n'avez ni Activité ni Vue. Cela fonctionne très bien ! Merci beaucoup !

18voto

Joe Points 181

La réponse de Pomber est acceptable, mais je ne suis pas un grand fan de la création répétée de nouveaux objets. Les meilleures solutions sont toujours celles qui tentent d'atténuer les problèmes de mémoire. Oui, il y a le garbage collection automatique, mais la conservation de la mémoire dans un appareil mobile relève des meilleures pratiques. Le code ci-dessous met à jour un TextView dans un service.

TextViewUpdater textViewUpdater = new TextViewUpdater();
Handler textViewUpdaterHandler = new Handler(Looper.getMainLooper());
private class TextViewUpdater implements Runnable{
    private String txt;
    @Override
    public void run() {
        searchResultTextView.setText(txt);
    }
    public void setText(String txt){
        this.txt = txt;
    }

}

Il peut être utilisé de n'importe où comme ceci :

textViewUpdater.setText("Hello");
        textViewUpdaterHandler.post(textViewUpdater);

13voto

Richard Points 501

Depuis Android P, vous pouvez utiliser getMainExecutor() :

getMainExecutor().execute(new Runnable() {
  @Override public void run() {
    // Code will run on the main thread
  }
});

De la [Documents pour les développeurs Android](https://developer.android.com/reference/android/content/Context.html#getMainExecutor()) :

Renvoie un exécuteur qui exécutera les tâches en file d'attente sur le thread principal associé à ce contexte. Il s'agit du thread utilisé pour distribuer les appels aux composants de l'application (activités, services, etc.).

De la CommonsBlog :

Vous pouvez appeler getMainExecutor() sur Context pour obtenir un exécuteur qui exécutera ses tâches sur le thread principal de l'application. Il existe d'autres moyens d'accomplir cette tâche, en utilisant Looper et une implémentation personnalisée de l'exécuteur, mais cette méthode est plus simple.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X