Esecuzione del codice nel thread principale da un altro thread


326

In un servizio Android ho creato thread per eseguire alcune attività in background.

Ho una situazione in cui un thread deve pubblicare determinate attività nella coda dei messaggi del thread principale, ad esempio a Runnable.

C'è un modo per ottenere Handleril thread principale e post Message/ Runnablea dal mio altro thread?

Grazie,


2
Puoi anche utilizzare il ricevitore di trasmissione personalizzato .... prova qui la mia risposta, [ricevitore per trasmissione interna] [1] [1]: stackoverflow.com/a/22541324/1881527
Melbourne Lopes

Ci sono molti modi. A parte la risposta di David e il commento di dzeikei nella sua risposta, (3) è possibile utilizzare un ricevitore broadcast o (4) passare il gestore negli extra di Intent utilizzati per avviare il servizio, quindi recuperare il gestore del thread principale all'interno del servizio utilizzando getIntent ( ) .getExtras ().
Ashok Bijoy Debnath il

2
@ sazzad-hissain-khan, Perché taggare questa domanda del 2012 con principalmente risposte in Java con il tag kotlin?
Tenfour04,

Risposte:


617

NOTA: questa risposta ha ricevuto così tanta attenzione che devo aggiornarla. Da quando è stata pubblicata la risposta originale, il commento di @dzeikei ha ricevuto quasi la stessa attenzione della risposta originale. Quindi ecco 2 possibili soluzioni:

1. Se il tuo thread in background ha un riferimento a un Contextoggetto:

Assicurarsi che i thread del lavoratore in background abbiano accesso a un oggetto Context (può essere il contesto dell'applicazione o il contesto del servizio). Quindi fai questo nel thread di lavoro in background:

// 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. Se il tuo thread in background non ha (o non ha bisogno) di un Contextoggetto

(suggerito da @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);

1
Grazie David ha funzionato per me, un'altra cosa con cui potresti aiutarmi, se estendere Handler e impl handleMessage () impedirebbe al thread principale di gestire i suoi messaggi? questa è solo una domanda per curiosità ..
Ahmed,

2
No. Se effettui la sottoclasse Handler(o usi l' Handler.Callbackinterfaccia), il tuo handleMessage()metodo verrà chiamato SOLO per i messaggi che sono stati pubblicati usando il tuo gestore. Il thread principale sta utilizzando un gestore diverso per pubblicare / elaborare i messaggi in modo che non vi siano conflitti.
David Wasser,

205
Credo che non avrai nemmeno bisogno del contesto se usiHandler mainHandler = new Handler(Looper.getMainLooper());
dzeikei

6
Punto minore; il tuo codice non va dove attualmente è ... Dovrebbe esserenew Runnable(){@Override public void run() {....}};
Richard Tingle il

1
@SagarDevanga Questo non è il posto giusto per porre una domanda diversa. Si prega di inviare una nuova domanda, non un commento a una risposta non correlata. In questo modo otterrai una risposta migliore e più veloce.
David Wasser,

138

Come ha indicato correttamente un commentatore di seguito, questa non è una soluzione generale per i servizi, solo per i thread avviati dalla tua attività (un servizio può essere un tale thread, ma non tutti lo sono). Per quanto riguarda l'argomento complicato della comunicazione attività-servizio, leggi l'intera sezione Servizi del documento ufficiale: è complessa, quindi pagherebbe per comprendere le basi: http://developer.android.com/guide/components/services.html #Notifications

Il metodo seguente può funzionare nei casi più semplici.

Se ti capisco correttamente, devi eseguire un po 'di codice nel thread GUI dell'applicazione (non riesco a pensare a nient'altro chiamato thread "principale"). Per questo c'è un metodo su 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

Spero che questo sia ciò che stai cercando.


20
OP dice che sta eseguendo discussioni in a Service. Non è possibile utilizzare runOnUiThread()in a Service. Questa risposta è fuorviante e non affronta la domanda posta.
David Wasser,

31

Esiste un altro modo semplice, se non si ha accesso al contesto.

1). Crea un gestore dal looper principale:

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

2). Implementare un'interfaccia Runnable:

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

3). Pubblica il tuo Runnable su uiHandler:

uiHandler.post(runnable);

Questo è tutto ;-) Divertiti con i thread, ma non dimenticare di sincronizzarli.


29

Se esegui il codice in un thread, ad esempio ritardando alcune azioni, devi invocare runOnUiThreaddal contesto. Ad esempio, se il tuo codice è all'interno della MainActivityclasse, utilizza questo:

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

Se il tuo metodo può essere richiamato dal main (thread dell'interfaccia utente) o da altri thread, è necessario un controllo come:

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

}

7
OP dice che sta eseguendo discussioni in a Service. Non è possibile utilizzare runOnUiThread()in a Service.
David Wasser,

@DavidWasser È documentato ovunque? I documenti del metodo non ne menzionano nulla. developer.android.com/reference/android/app/…
Greg Brown,

1
@GregBrown Come hai indicato dal tuo link alla Activitydocumentazione, runOnUiThread()è un metodo di Activity. Non è un metodo di Service, quindi non è possibile utilizzarlo. Cosa potrebbe esserci di più chiaro di così?
David Wasser,

@DavidWasser Abbastanza giusto. Non ricordo nemmeno perché ho posto quella domanda ora (è stata pubblicata quasi un anno fa).
Greg Brown,

24

Un blocco di codice condensato è il seguente:

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

Ciò non comporta la trasmissione del riferimento Attività o del riferimento Applicazione.

Equivalente di Kotlin:

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

20

Versioni di Kotlin

Quando sei su un'attività , quindi usa

runOnUiThread {
    //code that runs in main
}

Quando hai un contesto di attività , mContext quindi usa

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

Quando ti trovi in ​​un luogo in cui non è disponibile alcun contesto , utilizza

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

Non sono sicuro di quale versione di Kotlin sia, ma ho dovuto aggiungere delle parentesi graffe:runOnUiThread(){...}
Wex

5

Il modo più semplice soprattutto se non hai un contesto, se stai usando RxAndroid puoi fare:

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

5

Codice Kotlin più preciso usando l'handler:

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

4

Un metodo a cui riesco a pensare è questo:

1) Lascia che l'interfaccia utente si associ al servizio.
2) Esporre un metodo come quello sotto quello Binderche registra il tuo Handler:

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

3) Nel thread dell'interfaccia utente, chiama il metodo sopra dopo aver eseguito l'associazione al servizio:

mBinder.registerHandler(new Handler());

4) Utilizzare il gestore nel thread del servizio per pubblicare l'attività:

mHandler.post(runnable);

3

HandlerThread è l'opzione migliore per i normali thread Java in Android.

  1. Crea un HandlerThread e avvialo
  2. Crea un gestore con Looper da HandlerThread:requestHandler
  3. postun Runnablecompito surequestHandler

Comunicazione con UI Thread da HandlerThread

  1. Crea un metodo Handlerwith Looperper main thread: responseHandlerand overridehandleMessage
  2. All'interno Runnablecompito di altro thread ( HandlerThreadin questo caso), chiamare sendMessageilresponseHandler
  3. Questo sendMessagerisultato invoca di handleMessagein responseHandler.
  4. Ottieni attributi da Messageed elaboralo, aggiorna l'interfaccia utente

Esempio : aggiornamento TextViewcon i dati ricevuti da un servizio Web. Poiché il servizio Web deve essere richiamato su thread non UI, creato HandlerThreadper il funzionamento in rete. Una volta ottenuto il contenuto dal servizio Web, invia un messaggio al gestore del thread principale (thread dell'interfaccia utente) che Handlergestirà il messaggio e aggiornerà l'interfaccia utente.

Codice di esempio:

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);

Articoli utili:

handlerthreads-e-perché-si-deve-essere-utilizzando-li-in-your-android-apps

Android-crochet-gestore-handlerthread-i


2

So che questa è una vecchia domanda, ma mi sono imbattuto in una discussione principale che uso sia in Kotlin che in Java. Questa potrebbe non essere la soluzione migliore per un servizio, ma per chiamare qualcosa che cambierà l'interfaccia utente all'interno di un frammento è estremamente semplice ed ovvio.

Java (8):

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

Kotlin:

this.runOnUiThread {
     //your main thread code
}

1

Segui questo metodo. In questo modo puoi semplicemente aggiornare l'interfaccia utente da un thread in background. runOnUiThread funziona sul thread principale (UI). Penso che questo frammento di codice sia meno complesso e facile, soprattutto per i principianti.

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();

                   }
                });
            }
        });

nel caso di un servizio

crea un gestore in oncreate

 handler = new Handler();

quindi usalo in questo modo

 private void runOnUiThread(Runnable runnable) {
        handler.post(runnable);
    }

Grazie per questo frammento di codice, che potrebbe fornire un aiuto limitato e immediato. Una spiegazione adeguata migliorerebbe notevolmente il suo valore a lungo termine mostrando perché questa è una buona soluzione al problema e lo renderebbe più utile ai futuri lettori con altre domande simili. Si prega di modificare la risposta di aggiungere qualche spiegazione, tra le ipotesi che hai fatto.
iBug

1

per Kotlin, puoi usare le corountine Anko :

aggiornare

doAsync {
   ...
}

deprecato

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

1

Quindi la cosa più utile è fare una sorta di:

import android.os.AsyncTask
import android.os.Handler
import android.os.Looper

object Dispatch {
    fun asyncOnBackground(call: ()->Unit) {
        AsyncTask.execute {
            call()
        }
    }

    fun asyncOnMain(call: ()->Unit) {
        Handler(Looper.getMainLooper()).post {
            call()
        }
    }
}

E dopo:

Dispatch.asyncOnBackground {
    val value = ...// super processing
    Dispatch.asyncOnMain { completion(value)}
}

0
public void mainWork() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //Add Your Code Here
        }
    });
}

Questo può funzionare anche in una classe di servizio senza problemi.


Mentre questo codice potrebbe rispondere alla domanda, potresti comunque considerare l'aggiunta di alcune frasi esplicative in quanto ciò aumenta il valore della tua risposta per gli altri utenti!
MBT,

0

Con Kotlin, è proprio così all'interno di qualsiasi funzione:

runOnUiThread {
   // Do work..
}

2
Questo è solo quando sei su un Activity.
Farsheel
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.