¿Cómo usamos runOnUiThread en Android?

157

Soy nuevo en Android y estoy tratando de usar el UI-Thread, así que escribí una actividad de prueba simple. Pero creo que he entendido mal algo, porque al hacer clic en el botón, la aplicación ya no responde

public class TestActivity extends Activity {

    Button btn;
    int i = 0;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        btn = (Button)findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runThread();
            }
        });
    }

    private void runThread(){
        runOnUiThread (new Thread(new Runnable() {  
            public void run() {
                while(i++ < 1000){
                    btn.setText("#"+i);
                    try {
                        Thread.sleep(300);
                    } 
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
             }
        }));
    }
}
usuario1049280
fuente

Respuestas:

204

A continuación se muestra el fragmento de runThreadfunción corregido .

private void runThread() {

    new Thread() {
        public void run() {
            while (i++ < 1000) {
                try {
                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            btn.setText("#" + i);
                        }
                    });
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
}
Vipul Shah
fuente
¿No fue esto basura recolectada casi de inmediato? Probablemente necesite mantener alguna referencia al Hilo ()
Nick
14
@Nick: el recolector de basura también vigila la pila, es decir, cuando el hilo se está ejecutando, no se GC'ed.
Miro Kropacek
@Vipul, tenía una pregunta sobre la rotación del teléfono: una vez que gire mi teléfono, quiero que este hilo se ejecute y no se cree un nuevo hilo. ¿Puede darnos algunos consejos sobre cómo evitar la creación de un nuevo hilo una vez que se gira el teléfono?
user1836957
89

Simplemente envuélvalo como una función, luego llame a esta función desde su hilo de fondo.

public void debugMsg(String msg) {
    final String str = msg;
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mInfo.setText(str);
        }
    });
}

fuente
3
votó por mostrar cómo acceder a los datos en el ámbito externo ( final)
mariotomo
27

Lo tienes al revés. El clic de su botón genera una llamada runOnUiThread(), pero esto no es necesario, ya que el controlador de clics ya se está ejecutando en el hilo de la interfaz de usuario. Luego, su código runOnUiThread()está lanzando un nuevo subproceso en segundo plano, donde intenta realizar operaciones de IU, que luego fallan.

En su lugar, simplemente inicie el hilo de fondo directamente desde su controlador de clics. Luego, envuelva las llamadas a btn.setText()dentro de una llamada a runOnUiThread().

Graham Borland
fuente
Si bien es cierto que el controlador de clics ya está en el hilo de la interfaz de usuario, una llamada a runOnUiThread()es innecesaria pero debe ser inofensiva. El Javadoc para ese método dice "Ejecuta la acción especificada en el subproceso de la interfaz de usuario. Si el subproceso actual es el subproceso de la interfaz de usuario, la acción se ejecuta inmediatamente. Si el subproceso actual no es el subproceso de la interfaz de usuario, la acción se publica en la cola de eventos del hilo de la interfaz de usuario ".
k2col
15
runOnUiThread(new Runnable() {
                public void run() {
                //Do something on UiThread
            }
        });
Terranología
fuente
10

Hay varias técnicas que utilizan runOnUiThread (), veamos todas

Este es mi hilo principal (hilo de la interfaz de usuario) llamado AndroidBasicThreadActivity y lo voy a actualizar desde un hilo de trabajo de varias maneras:

public class AndroidBasicThreadActivity extends AppCompatActivity
{
    public static TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_android_basic_thread);

        textView = (TextView) findViewById(R.id.textview);

        MyAndroidThread myTask = new MyAndroidThread(AndroidBasicThreadActivity.this);
        Thread t1 = new Thread(myTask, "Bajrang");
        t1.start();
    }
}

1.) Al pasar la instancia de Activity como argumento en el subproceso de trabajo

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
    public void run()
    {

        //perform heavy task here and finally update the UI with result this way - 
        activity.runOnUiThread(new Runnable()
        {
            @Override
            public void run()
            {
                AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
            }
        });
    }
}

2.) Al utilizar el método de publicación de View (Runnable runnable) en el subproceso de trabajo

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
    public void run()
    {
     //perform heavy task here and finally update the UI with result this way - 
       AndroidBasicThreadActivity.textView.post(new Runnable()
      { 
        @Override
        public void run()
        {
            AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
        }
    });

    }
}

3.) Al usar la clase Handler del paquete android.os Si no tenemos el contexto (this / getApplicationContext ()) o la instancia de Activity (AndroidBasicThreadActivity.this), entonces tenemos que usar la clase Handler de la siguiente manera:

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
   public void run()
  {
  //perform heavy task here and finally update the UI with result this way - 
  new Handler(Looper.getMainLooper()).post(new Runnable() {
        public void run() {
            AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
        }
    });
  }
}
Bajrang Hudda
fuente
Gracias .. En lugar de simplemente repetir sobre runOnUIThread de la actividad, usted ha mencionado todas las formas posibles de la invoca ..
arun
Gracias .. En lugar de simplemente repetir sobre runOnUIThread de la actividad, usted ha mencionado todas las formas posibles de la invoca ..
arun
6

Si se usa en fragmentos, simplemente escriba

getActivity().runOnUiThread(new Runnable() {
    @Override
    public void run() {
        // Do something on UiThread
    }
});
Shivam Yadav
fuente
1

tu esto:

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


            }
        });
    }
MrGuy
fuente
1

Utilizamos Worker Thread para hacer que las aplicaciones sean más fluidas y evitar los ANR. Es posible que necesitemos actualizar la interfaz de usuario después del proceso pesado en la banda de rodadura de los trabajadores. La interfaz de usuario solo se puede actualizar desde UI Thread. En tales casos, usamos Handler o runOnUiThread, ambos tienen un método de ejecución Runnable que se ejecuta en UI Thread. El método onClick se ejecuta en el subproceso de la interfaz de usuario, por lo que no es necesario usar runOnUiThread aquí.

Usando Kotlin

Mientras está en actividad,

this.runOnUiThread {
      // Do stuff
}

Del fragmento,

activity?.runOnUiThread {
      // Do stuff
}

Usando Java ,

this.runOnUiThread(new Runnable() {
     void run() {
         // Do stuff
     }
});
Varun Chandran
fuente
0

Puede usar de esta muestra:

En el siguiente ejemplo, vamos a utilizar esta función para publicar el resultado de una búsqueda de sinónimos procesada por un subproceso en segundo plano.

Para lograr el objetivo durante la devolución de llamada de actividad OnCreate, configuraremos onClickListener para ejecutar searchTask en un hilo creado.

Cuando el usuario hace clic en el botón Buscar, crearemos una clase anónima Runnable que busca la palabra escrita en R.id.wordEt EditText e inicia el hilo para ejecutar Runnable.

Cuando finalice la búsqueda, crearemos una instancia de Runnable SetSynonymResult para publicar el resultado en el sinónimo TextView sobre el hilo de la interfaz de usuario.

Esta técnica a veces no es la más conveniente, especialmente cuando no tenemos acceso a una instancia de Actividad; por lo tanto, en los siguientes capítulos, vamos a discutir técnicas más simples y limpias para actualizar la interfaz de usuario desde una tarea informática en segundo plano.

public class MainActivity extends AppCompatActivity {

    class SetSynonymResult implements Runnable {
        String synonym;

        SetSynonymResult(String synonym) {
            this.synonym = synonym;
        }

        public void run() {
            Log.d("AsyncAndroid", String.format("Sending synonym result %s on %d",
                    synonym, Thread.currentThread().getId()) + " !");
            TextView tv = (TextView) findViewById(R.id.synonymTv);
            tv.setText(this.synonym);
        }
    }

    ;

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

        Button search = (Button) findViewById(R.id.searchBut);
        final EditText word = (EditText) findViewById(R.id.wordEt);
        search.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Runnable searchTask = new Runnable() {
                    @Override
                    public void run() {
                        String result = searchSynomim(word.getText().toString());
                        Log.d("AsyncAndroid", String.format("Searching for synonym for %s on %s",
                                word.getText(), Thread.currentThread().getName()));
                        runOnUiThread(new SetSynonymResult(result));
                    }
                };
                Thread thread = new Thread(searchTask);
                thread.start();
            }
        });

    }

    static int i = 0;

    String searchSynomim(String word) {
        return ++i % 2 == 0 ? "fake" : "mock";
    }
}

Fuente :

programación asincrónica de Android Helder Vasconcelos


fuente
0

Así es como lo uso:

runOnUiThread(new Runnable() {
                @Override
                public void run() {
                //Do something on UiThread
            }
        });
Josh Aquino
fuente
0
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        gifImageView = (GifImageView) findViewById(R.id.GifImageView);
        gifImageView.setGifImageResource(R.drawable.success1);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //dummy delay for 2 second
                    Thread.sleep(8000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //update ui on UI thread
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        gifImageView.setGifImageResource(R.drawable.success);
                    }
                });

            }
        }).start();

    }
Keshav Gera
fuente
0

Prueba esto: getActivity().runOnUiThread(new Runnable...

Eso es porque:

1) lo implícito en su llamada a runOnUiThread se refiere a AsyncTask , no a su fragmento.

2) Fragment no tiene runOnUiThread.

Sin embargo, la actividad sí.

Tenga en cuenta que Activity solo ejecuta Runnable si ya está en el hilo principal; de lo contrario, utiliza un Handler. Puede implementar un controlador en su fragmento si no quiere preocuparse por el contexto de esto, en realidad es muy fácil:

// Una instancia de clase

private Handler mHandler = new Handler(Looper.getMainLooper());

// en cualquier otro lugar de tu código

mHandler.post(<your runnable>);

// ^ esto siempre se ejecutará en el siguiente ciclo de ejecución en el hilo principal.

Pradeep Kumar
fuente