Ejecutar código en el hilo principal desde otro hilo


326

En un servicio de Android, he creado hilos para realizar alguna tarea en segundo plano.

Tengo una situación en la que un hilo necesita publicar cierta tarea en la cola de mensajes del hilo principal, por ejemplo a Runnable.

¿Hay una manera de conseguir Handlerdel hilo principal y post Message/ Runnablea de mi otro hilo?

Gracias,


2
También puede usar el receptor de transmisión personalizado ... pruebe mi respuesta aquí, [Receptor de transmisión interno] [1] [1]: stackoverflow.com/a/22541324/1881527
Melbourne Lopes

Hay muchas maneras. Además de la respuesta de David y el comentario de dzeikei en su respuesta, (3) puede usar un receptor de difusión, o (4) pasar el controlador en extras de Intent utilizado para iniciar el servicio, y luego recuperar el controlador interno del hilo principal usando getIntent ( ) .getExtras ().
Ashok Bijoy Debnath

2
@ sazzad-hissain-khan, ¿Por qué etiquetar esta pregunta de 2012 con respuestas principalmente en Java con la etiqueta kotlin?
Tenfour04

Respuestas:


617

NOTA: Esta respuesta ha recibido tanta atención que necesito actualizarla. Desde que se publicó la respuesta original, el comentario de @dzeikei ha recibido casi tanta atención como la respuesta original. Así que aquí hay 2 posibles soluciones:

1. Si su hilo de fondo tiene una referencia a un Contextobjeto:

Asegúrese de que sus subprocesos de trabajo en segundo plano tengan acceso a un objeto de contexto (puede ser el contexto de la aplicación o el contexto del servicio). Luego solo haz esto en el hilo de fondo del trabajador:

// 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 su hilo de fondo no tiene (o necesita) un Contextobjeto

(sugerido por @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
Gracias David, funcionó para mí, una cosa más si me pudieras ayudar, si extiendo Handler e impl handleMessage (), ¿evitaría que el hilo principal maneje sus mensajes? eso es solo una pregunta por curiosidad ..
Ahmed

2
No. Si subclasifica Handler(o usa la Handler.Callbackinterfaz), su handleMessage()método SOLO será llamado para los mensajes que se hayan publicado usando su controlador. El hilo principal está usando un controlador diferente para publicar / procesar mensajes para que no haya conflicto.
David Wasser

205
Creo que ni siquiera necesitarás contexto si lo usasHandler mainHandler = new Handler(Looper.getMainLooper());
dzeikei

66
Punto menor; su código no va donde está el ... actualmente. Debería sernew Runnable(){@Override public void run() {....}};
Richard Tingle

1
@SagarDevanga Este no es el lugar correcto para hacer una pregunta diferente. Publique una nueva pregunta, no un comentario a una respuesta no relacionada. Obtendrá una respuesta mejor y más rápida de esa manera.
David Wasser

138

Como un comentador a continuación señaló correctamente, esta no es una solución general para los servicios, solo para los hilos lanzados desde su actividad (un servicio puede ser un hilo, pero no todos lo son). Sobre el complicado tema de la comunicación de actividad de servicio, lea toda la sección de Servicios del documento oficial: es complejo, por lo que sería útil comprender los conceptos básicos: http://developer.android.com/guide/components/services.html # Notificaciones

El siguiente método puede funcionar en los casos más simples.

Si te entiendo correctamente, necesitas ejecutar algún código en el hilo GUI de la aplicación (no puedo pensar en nada más llamado hilo "principal"). Para esto hay un método sobre 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

Espero que esto sea lo que estás buscando.


20
OP dice que está ejecutando hilos en a Service. No se puede usar runOnUiThread()en a Service. Esta respuesta es engañosa y no aborda la pregunta formulada.
David Wasser

31

Hay otra forma simple, si no tiene acceso al contexto.

1) Cree un controlador desde el looper principal:

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

2) Implemente una interfaz Runnable:

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

3) Publique su Runnable en el uiHandler:

uiHandler.post(runnable);

Eso es todo ;-) Diviértete con los hilos, pero no olvides sincronizarlos.


29

Si ejecuta código en un hilo, por ejemplo, retrasando alguna acción, entonces necesita invocar runOnUiThreaddesde el contexto. Por ejemplo, si su código está dentro de la MainActivityclase, use esto:

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

Si su método se puede invocar desde main (UI thread) o desde otros hilos, necesita una verificación como:

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

}

77
OP dice que está ejecutando hilos en a Service. No se puede usar runOnUiThread()en a Service.
David Wasser

@DavidWasser ¿Está documentado eso en alguna parte? Los documentos del método no mencionan nada al respecto. developer.android.com/reference/android/app/…
Greg Brown

1
@GregBrown Como ha indicado en su enlace a la Activitydocumentación, runOnUiThread()es un método de Activity. No es un método Service, por lo tanto, no puede usarlo. ¿Qué podría ser más claro que eso?
David Wasser

@DavidWasser Bastante justo. Ni siquiera recuerdo por qué hice esa pregunta ahora (fue publicada hace casi un año).
Greg Brown

24

Un bloque de código condensado es el siguiente:

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

Esto no implica pasar la referencia de Actividad o la referencia de Aplicación.

Equivalente de Kotlin:

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

20

Versiones Kotlin

Cuando estés en una actividad , usa

runOnUiThread {
    //code that runs in main
}

Cuando tenga contexto de actividad , mContext luego use

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

Cuando se encuentre en un lugar donde no haya contexto disponible , use

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

No estoy seguro de qué versión de Kotlin es esta, pero tuve que agregar llaves:runOnUiThread(){...}
Wex

5

La forma más sencilla, especialmente si no tiene un contexto, si está utilizando RxAndroid, puede hacer:

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

5

Código de Kotlin más preciso utilizando el controlador:

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

4

Un método que se me ocurre es este:

1) Deje que la interfaz de usuario se una al servicio.
2) Exponga un método como el siguiente Binderque registra su Handler:

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

3) En el subproceso de la interfaz de usuario, llame al método anterior después de vincularse al servicio:

mBinder.registerHandler(new Handler());

4) Use el controlador en el hilo del Servicio para publicar su tarea:

mHandler.post(runnable);

3

HandlerThread Es una mejor opción para los hilos normales de Java en Android.

  1. Cree un subproceso de controlador e inícielo
  2. Crear un Handler con Looper de HandlerThread:requestHandler
  3. postuna Runnabletarea enrequestHandler

Comunicación con UI Thread de HandlerThread

  1. Cree un Handlercon Looperpara hilo principal: responseHandlery handleMessagemétodo de anulación
  2. RunnableTarea interna de otro hilo ( HandlerThreaden este caso), llame sendMessagealresponseHandler
  3. La sendMessageinvocación de este resultado handleMessageen responseHandler.
  4. Obtenga atributos del Messageproceso y actualícelo, actualice la interfaz de usuario

Ejemplo : actualización TextViewcon datos recibidos de un servicio web. Dado que el servicio web debe invocarse en un subproceso que no sea UI, creado HandlerThreadpara la Operación de red. Una vez que obtenga el contenido del servicio web, envíe un mensaje a su manejador de subprocesos principales (UI Thread) y queHandler manejará el mensaje y actualizará la IU.

Código de muestra:

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

Artículos utiles:

handlerthreads-and-why-you-should-should-uses-them-in-your-android-apps

android-looper-handler-handlerthread-i


2

Sé que esta es una vieja pregunta, pero me encontré con un hilo principal que utilizo tanto en Kotlin como en Java. Puede que esta no sea la mejor solución para un servicio, pero para llamar a algo que cambiará la interfaz de usuario dentro de un fragmento, esto es extremadamente simple y obvio.

Java (8):

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

Kotlin:

this.runOnUiThread {
     //your main thread code
}

1

Sigue este método. De esta manera, simplemente puede actualizar la interfaz de usuario desde un hilo de fondo. runOnUiThread funciona en el hilo principal (UI). Creo que este fragmento de código es menos complejo y fácil, especialmente para principiantes.

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

                   }
                });
            }
        });

en el caso de un servicio

crear un controlador en el oncreate

 handler = new Handler();

entonces úsalo así

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

Gracias por este fragmento de código, que podría proporcionar una ayuda limitada e inmediata. Una explicación adecuada mejoraría en gran medida su valor a largo plazo al mostrar por qué esta es una buena solución al problema y lo haría más útil para futuros lectores con otras preguntas similares. Por favor, editar su respuesta a añadir un poco de explicación, incluyendo los supuestos realizados.
iBug

1

para Kotlin, puede usar las corountinas de Anko :

actualizar

doAsync {
   ...
}

obsoleto

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

1

Así que lo más útil es hacer algo así:

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

Y después:

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

Esto también puede funcionar en una clase de servicio sin problemas.


Si bien este código puede responder a la pregunta, aún podría considerar agregar algunas oraciones explicativas, ya que esto aumenta el valor de su respuesta para otros usuarios.
MBT

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.