web-dev-qa-db-fra.com

Exécuter du code dans le thread principal à partir d'un autre thread

Dans un service Android, j'ai créé un ou plusieurs threads pour effectuer certaines tâches en arrière-plan. 

Dans certaines situations, un fil doit poster certaines tâches dans la file de messages du fil principal, par exemple un Runnable.

Existe-t-il un moyen d'obtenir Handler du thread principal et d'y poster Message/Runnable à partir de mon autre thread?

Merci, 

265
Ahmed

NOTE: Cette réponse a tellement attiré l'attention que je dois la mettre à jour. Depuis que la réponse originale a été publiée, le commentaire de @dzeikei a attiré presque autant d'attention que la réponse d'origine. Alors voici 2 solutions possibles:

1. Si votre thread d'arrière-plan comporte une référence à un objet Context:

Assurez-vous que vos threads de travail d’arrière-plan ont accès à un objet Context (il peut s’agir du contexte Application ou du contexte Service). Ensuite, faites ceci dans le thread de travail en arrière-plan:

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

2. Si votre thread d'arrière-plan n'a pas (ou n'a pas besoin) d'objet Context

(suggéré par @dzeikei):

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);
542
David Wasser

Comme l'a souligné correctement un commentateur ci-dessous, il ne s'agit pas d'une solution générale pour les services, mais uniquement pour les threads lancés à partir de votre activité (un service peut être un tel thread, mais tous ne le sont pas) . activité communication s'il vous plaît lire toute la section Services de la doc officielle - c'est complexe, il serait donc utile de comprendre les bases: http://developer.Android.com/guide/components/services.html#Notifications

La méthode ci-dessous peut fonctionner dans les cas les plus simples.

Si je vous ai bien compris, vous avez besoin que du code soit exécuté dans le fil graphique de l’application (vous ne pouvez penser à rien d’autre appelé "fil" principal). Pour cela, il existe une méthode sur Activity:

someActivity.runOnUiThread(new Runnable() {
        @Override
        public void run() {
           //Your code to run in GUI thread here
        }//public void run() {
});

Doc: http://developer.Android.com/reference/Android/app/Activity.html#runOnUiThread%28Java.lang.Runnable%29

J'espère que c'est ce que vous recherchez.

125
Alexey Vassiliev

Il existe un autre moyen simple, si vous n’avez pas accès au contexte.

1). Créez un gestionnaire à partir du boucleur principal:

Handler uiHandler = new Handler(Looper.getMainLooper());

2) Implémenter une interface Runnable:

Runnable runnable = new Runnable() { // your code here }

3) Postez votre Runnable sur uiHandler:

uiHandler.post(runnable);

C'est tout ;-) Amusez-vous avec les threads, mais n'oubliez pas de les synchroniser.

28
tema_man

Si vous exécutez du code dans un fil de discussion, par exemple retarder certaines actions, vous devez alors appeler runOnUiThread à partir du contexte. Par exemple, si votre code est dans la classe MainActivity, utilisez ceci:

MainActivity.this.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        myAction();
    }
});

Si votre méthode peut être invoquée à partir de main (thread d'interface utilisateur) ou d'autres threads, vous devez effectuer une vérification comme suit:

public void myMethod() {
   if( Looper.myLooper() == Looper.getMainLooper() ) {
       myAction();
   }
   else {

}
26
Alexander Volkov

Un bloc de code condensé est le suivant:

   new Handler(Looper.getMainLooper()).post(new Runnable() {
       @Override
       public void run() {
           // things to do on the main thread
       }
   });

Cela n'implique pas la transmission de la référence d'activité ou de l'application.

Kotlin Equivalent:

    Handler(Looper.getMainLooper()).post(Runnable {
        // things to do on the main thread
    })
13
david m lee

Versions Kotlin

Quand vous êtes sur une activité, utilisez

runOnUiThread {
    //code that runs in main
}

Quand vous avez un contexte d'activité, mContext puis utilisez

mContext.runOnUiThread {
    //code that runs in main
}

Lorsque vous êtes quelque part où pas de contexte disponible, utilisez

Handler(Looper.getMainLooper()).post {  
    //code that runs in main
}
4

Une méthode à laquelle je peux penser est la suivante:

1) Laissez l'interface utilisateur se lier au service.
2) Exposer une méthode comme celle ci-dessous par la Binder qui enregistre votre Handler:

public void registerHandler(Handler handler) {
    mHandler = handler;
}

3) Dans le thread d'interface utilisateur, appelez la méthode ci-dessus après la liaison au service:

mBinder.registerHandler(new Handler());

4) Utilisez le gestionnaire dans le fil du service pour poster votre tâche:

mHandler.post(runnable);
4
Dheeraj V.S.

Je sais que c’est une vieille question, mais j’ai trouvé un fil conducteur unique que j’utilise aussi bien dans Kotlin que dans Java. Ce n'est peut-être pas la meilleure solution pour un service, mais pour appeler quelque chose qui va changer l'interface utilisateur à l'intérieur d'un fragment, c'est extrêmement simple et évident.

Java (8):

 getActivity().runOnUiThread(()->{
      //your main thread code
 });

Kotlin:

this.runOnUiThread {
     //your main thread code
}
2
mevdev

HandlerThread est la meilleure option pour les threads Java normaux dans Android.

  1. Créez un HandlerThread et démarrez-le
  2. Créer un Handler avec Looper à partir de HandlerThread: requestHandler
  3. post une Runnable tâche sur requestHandler

Communication avec le fil d'interface utilisateur de HandlerThread

  1. Créez une Handler avec Looper pour le thread principal: responseHandler et remplacez la méthode handleMessage
  2. À l'intérieur de la tâche Runnable d'un autre thread (HandlerThread dans ce cas), appelez sendMessage à responseHandler
  3. Cet invocation de résultat sendMessage de handleMessage dans responseHandler.
  4. Obtenir les attributs de la Message et le traiter, mettre à jour l'interface utilisateur

Exemple: Mettez à jour TextView avec les données reçues d'un service Web. Étant donné que le service Web doit être appelé sur un thread autre que l'interface utilisateur, créez HandlerThread pour l'opération sur le réseau. Une fois que vous avez récupéré le contenu du service Web, envoyez un message à votre gestionnaire de thread principal (UI Thread) et Handler gérera le message et mettra à jour l'interface utilisateur.

Exemple de code:

HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        txtView.setText((String) msg.obj);
    }
};

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Runnable", "Before IO call");
            URL page = new URL("http://www.your_web_site.com/fetchData.jsp");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ((line = buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Runnable", "After IO call:"+ text.toString());
            Message msg = new Message();
            msg.obj = text.toString();
            responseHandler.sendMessage(msg);


        } catch (Exception err) {
            err.printStackTrace();
        }
    }
};
requestHandler.post(myRunnable);

Articles utiles:

handlerthreads-and-pourquoi-vous-devriez-être-en-utilisant-les-dans-vos-applications-Android

Android-looper-handler-handlerthread-i

2
Ravindra babu

La manière la plus simple, surtout si vous n’avez pas de contexte, si vous utilisez RxAndroid, vous pouvez faire:

AndroidSchedulers.mainThread().scheduleDirect {
    runCodeHere()
}
2
Inn0vative1

Code Kotlin plus précis en utilisant handler:

Handler(Looper.getMainLooper()).post {  
 // your codes here run on main Thread
 }
1
Hamed Jaliliani

Suivez cette méthode. De cette manière, vous pouvez simplement mettre à jour l'interface utilisateur à partir d'un thread d'arrière-plan. runOnUiThread fonctionne sur le thread principal (UI). Je pense que cet extrait de code est moins complexe et facile, surtout pour les débutants.

AsyncTask.execute(new Runnable() {
            @Override
            public void run() {

            //code you want to run on the background
            someCode();

           //the code you want to run on main thread
 MainActivity.this.runOnUiThread(new Runnable() {

                    public void run() {

/*the code you want to run after the background operation otherwise they will executed earlier and give you an error*/
                        executeAfterOperation();

                   }
                });
            }
        });

dans le cas d'un service

créer un gestionnaire dans oncreate

 handler = new Handler();

puis utilisez-le comme ça

 private void runOnUiThread(Runnable runnable) {
        handler.post(runnable);
    }
1
MarGin
public void mainWork() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //Add Your Code Here
        }
    });
}

Cela peut également fonctionner dans une classe de service sans problème.

0
NUSKTECsoft

pour Kotlin, vous pouvez utiliser Anko corountines :

mettre à jour

doAsync {
   ...
}

obsolète 

async(UI) {
    // Code run on UI thread
    // Use ref() instead of this@MyActivity
}
0
Francis