En este tutorial explicaremos el funcionamiento de los threads y los handlers. A simple vista parece algo complicado pero lo explicaremos lo más ameno posible. Primero hay que saber que és cada cosa:
Thread (Hilo):
Es la función encargada de crear algún proceso en segundo plano. Su funcionamiento es bastan te simple aunque como todo siempre se puede complicar. Se pueden crear tantos hilos (Thread) como se quiera, teniendo en cuenta que el hilo deja de formar parte de la aplicación y funciona de manera independiente. A simple visto no hay ningún problema pero el hilo no puede modificar ni insertar datos en el hilo principal (aplicación) esto causaría error, ¿Entonces?, ¿como podemos utilizar los hilos para volcar información a nuestra aplicación? La solución se llama Handler.
Handler:
Para que lo entendamos todos un handler es el puente que hay entre un hilo secundario (thread) y el hilo principal (aplicación). No hay mucho más que explicar, para que se entienda bien:
Crear una barra de progreso en Android:
El ejemplo que vamos a utilizar es una barra de progreso (ProgressBar) que mostraremos inicialmente, a continuación crearemos un thread y en su interior la función que queramos, en este ejemplo simplemente será un contador (tendremos que simular algún proceso que tarde algo para poder ver funcionando el thread y no sea simplemente un parpadeo) y utilizaremos un handlet para ir actualizando la barra de progreso (situada en el hilo principal) como hemos comentado para actualizar datos de pantalla desde un hilo secundario (en nuestro caso el contador) hace falta un puente, es decir, nuestro handler.
Como vamos a ver el Handler hay que declararlo al inicio:
private Handler puente = new Handler() { @Override public void handleMessage(Message msg) { } };
Como vemos hay que pasarle un Message, en nuestro caso sera el contador hacia la progressbar.
Ahora mostramos el progressbar:
progressDialog = new ProgressDialog(EjemplotutoActivity.this); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setMessage("Loading..."); progressDialog.setMax(10000); progressDialog.setProgress(0); progressDialog.setCancelable(false); progressDialog.show();
La barra de progreso la hemos puesto a 0 y su máximo será 10000 así nos dará tiempo a verlo.
Creamos el Thread y lo ponemos en marcha con la funcion start() :
Thread th1 = new Thread(new Runnable() { @Override public void run() { } }); th1.start();
Ahora crearemos nuestro contrador:
Thread th1 = new Thread(new Runnable() { @Override public void run() { for(int a=1;a<9999;a++){ int contador = a; } } }); th1.start();
Ahora vamos hacer el paso importante pasar nuestro contador por un 'puente' handler hacia el progressBar, lo haremos através de un Message :
Thread th1 = new Thread(new Runnable() { @Override public void run() { for(int a=1;a<9999;a++){ int contador = a; Message msg = new Message(); msg.obj = a; puente.sendMessage(msg); } } });Como vemos enviamos un Message a nuestro Handler 'puente' y lo hará 9998 veces ya que esta dentro de un túnel, la velocidad como veréis va un poco saturada al menos en el emulador. Ya solo queda recivir el contador en el Handler e insertarlo en el ProgressBar:
private Handler puente = new Handler() { @Override public void handleMessage(Message msg) { progressDialog.setProgress((Integer)msg.obj); } };Como ya véis hay que hacer un casteo '(Integer)msg.obj' para convertir el mensaje en su forma original, un Integer y añadirlo al progressBar através del .setProgress.
Espero que haya quedado bastante claro y aunque parezca un poco largo, al ponerlo en práctica se reduce bastante.
Resumen: Pasar datos de un hilo secundario (Thread) al principal (Aplicación).
Muchas Gracias a todos y espero vuestros comentarios!
Hola, he estado viendo el código y creo que puede servirme de ayuda pero tengo una duda.
ResponderEliminar¿Que sucede cuando la operativa del hilo forma parte de otra clase?
Es decir, yo tengo mi clase lanzadora que muestra una tabla con datos. Esta misma clase crea un hilo de una clase, pero claro, cuando hago referencia al handler puente desde la otra clase (la del hilo) no lo reconoce, puesto que la definición está en la clase principal (la lanzadora).
No se si me explico
Estuve pensando en eso justamente el otro día y la verdad es que se me plantean algunas alternativas (no tienen porque funcionar xD) pero aún no he podido ponerlas en practica.
EliminarMe gustaría que si encontraras la respuesta la comentaras aquí también, para ayudar a otras personas que lo necesiten. Por mi parte aré lo mismo.
Muchas gracias por comentar!
Ha pasado mucho tiempo desde que hicieron este comentario... pero la solucion ideal, seria para estos casos usar un broadcast (http://developer.android.com/reference/android/content/BroadcastReceiver.html).
EliminarMuchas gracias por la respuesta, me sirvió mucho Heriberto.
EliminarVictor, agradezco que hayas hecho esta publicación. Estuve leyendo varias pero de todas la más fácil de comprender ha sido esta.
ResponderEliminarSaludos y gracias.
José Enrique Zúñiga Marín
Muchas gracias por tu comentario, cualquier cosa quí estamos :D
EliminarTutorial muy claro y útil. Gracias.
ResponderEliminarmil gracias a todas estas personas que comparten sus conocimientos, pues sin ellas, jamás lograríamos entender los entresijos de la programación android
ResponderEliminarMuchas gracias, seguiré ampliando el blog con más tutoriales.
EliminarPues que Dios te lo pague, porque desde luego...os ganais el cielo
EliminarEspectacular, muchas gracias !
ResponderEliminarGracias Probando ..
ResponderEliminarNo es nada...
ResponderEliminar¿No es nada?
Eliminarchiste facil de nuestro amigo anonimo probando
EliminarExcelente!!!!!
ResponderEliminarSaludos, Se ve perfectamente claro sin embargo a mi me sigue sin funcionar
ResponderEliminarDe nuevo aparece el mismo error CalledFromWrongViewException cuando el mensaje llega.
Estoy usando este handler dentro de un Fragment, tiene algo que ver?
gracias
Buenos días!
EliminarPrimero deberías probarlo sin Fragment a ver que resultado tienes. Y si que tendrá algo que ver ya que en el error CalledFromWrongViewException te está ablando del View en este caso Fragment. ¿Utilizas también el Progress Dialog? habría que tener en cuenta donde lo creas si dentro del Fragment o no.
Espero a verte ayudado. cualquier cosa intentaremos ayudarte entre todos. Gracias!
Si en lugar de poner la orden (en el Thread)
ResponderEliminarpuente.sendMessage(msg);
pongo directamente
progressDialog.setProgress(msg);
me funciona.
¿Lo que me pregunto es para qué quiero el Handler?
En este caso progressDialog es un ejemplo, el Handler se utiliza para sacar datos del Thread a la aplicaión en tiempo de ejecución. Si no utilizas Handler la barra de progreso no te medirá en tiempo real ya que no utilizas una apliación en segundo plano. Seguramente estás reando todo el progress dialog dentro del Thread. Gracias!
Eliminarintenta cambiar textos de un textview o imagenes con un hilo y me cuentas si el handler, es solo por molestar que lo ponemos.
Eliminara y muyyy util el tuto muchas gracias
intenta cambiar textos de un textview o imagenes con un hilo y me cuentas si el handler, es solo por molestar que lo ponemos.
Eliminara y muyyy util el tuto muchas gracias
+1 No cuesta nada
ResponderEliminarGracias por tu aporte, se entiende perfectamente!
ResponderEliminarSaludos!
Muy buen tuto, Gracias :)
ResponderEliminarPorque no puedo usar hilo.stop o hilo.resume... en android
ResponderEliminarSupongo que abra que utilizarlo en condiciones especiales, no lo he mirado.
Eliminar1+
ResponderEliminarBuen tuto, gracias :)
Muy pero muy bueno, corto y concreto :)
ResponderEliminaruna pregunta, en el ejemplo se utiliza un solo hilo y si fueran mas hilos digamos 4, tenemos que usar 4 puentes? como se maneja en este caso para varios hilos,
ResponderEliminargracias
Si serían más de un puente, aunque hay una manera más óptima que se llama asynctask. Esta semana aré un tutorial sobre eso :D muchas gracias por comentar!
Eliminar+1
hola victor, tendrias el codigo del proyecto; porque no se en que sector va cada trozo de codigo!!
ResponderEliminarGracias!!!
Los proyectos no los guardo, pero igualmente deberías empezar por tutoriales más sencillos. Gracias!
EliminarSaludos a todos, bueno realicé esta forma con AsyncTask
ResponderEliminarLa primera clase es esta la de AsyncTask
package com.ex.proyecto;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.widget.Button;
import android.widget.ProgressBar;
import android.app.Activity;
public class UpdateProgress extends AsyncTask{
int progress;
Button button;
ProgressBar progressBar;
ProgressDialog pDialog;
Activity ok;
public UpdateProgress(Button button,ProgressBar progressBar,ProgressDialog pDialog, Activity ok) {
this.button = button;
this.progressBar = progressBar;
this.pDialog = pDialog;
this.ok = ok;
}
@Override
protected void onPostExecute(Void result){
this.button.setClickable(true);
this.button.setText(R.string.bnt_text);
this.progressBar.setProgress(0);
//pDialog.dismiss();//ocultamos progess dialog.
}
@Override
protected void onPreExecute(){
this.progress = 0;
/* para el progress dialog
pDialog = new ProgressDialog(ok);
pDialog.setTitle("Procesando");
pDialog.setMessage("Espere por favor ....");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show(); */
}
@Override
protected void onProgressUpdate(Integer... values){
this.progressBar.setProgress(values[0]);
}
@Override
protected Void doInBackground(Void... arg0){
while(progress<100){
this.progress++;
publishProgress(this.progress);
SystemClock.sleep(50);
}
return null;
}
}
Luego tenemos la Otra Clase del Activity
ResponderEliminarpackage com.ex.proyecto;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity {
Button button, butonSumar;
ProgressBar progressBar;
EditText text1, text2;
TextView respuesta2;
int result = 0;
private ProgressDialog pDialog;
/** Called when the activity is first created. */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.task);
butonSumar = (Button)findViewById(R.id.button1);
progressBar = (ProgressBar)findViewById(R.id.progressbar);
respuesta2 = (TextView)findViewById(R.id.respuesta);
/* El Proceso de AsynTask se Incia una Vez se le da click al Boton
* Pulse para Inicar la Carga*/
button.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View arg0){
//Se cambia el Boton a Proceso corriendo Espere y
//Se coloca deshabilita el Boton para que si lo pisan no haga nada
button.setText(R.string.Corriendo);
button.setClickable(false);
/* Por último se llama al Metodo onPreExecute con la palabra execute de la
* Sub-Clase UpdateProgress para que esta realice las Tareas de Forma Automaticas,
* hay que recordar que solo se llama al Metodo execute y este realiza los trabajo
* solos hasta Finalizae con el Metodo onPostExecute, bajo ningun concepto
* se debe llamar a los metodos de la clase AsyncTask*/
new UpdateProgress(button,progressBar,pDialog,MainActivity.this).execute();
}
});
butonSumar.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View arg0){
result = result + 1;
String IntStr = String.valueOf(result);
respuesta2.setText(IntStr);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
ResponderEliminary por último tenemos el Archivo String en la Carpeta Values
Proyecto
Settings
Hello world!
Pulse para Iniciar la Carga
Proceso Corriendo espere !!!!
El Proceso Finalizó !!
%
Puedes correr un proceso primario y un hilo al mismo tiempo y el hilo modifica al primario.
saludos a todo.
Muchas graciias por compartir, sobre el tema del tutorial.
EliminarAnimaros y dar +1 nos ayuda a todos!
No me dejo publicar el archivo xml de la pantalla
ResponderEliminarpero lo pueden sacar por los archivos clases
Intentaré publicar el proyecto en cuanto tenga un momento. Muchas gracias!
EliminarComo mando una aplicación entera a segundo plano.
ResponderEliminarHola tengo una pregunta, es que quiero realizar un clientsocket que comunica con una aplicacion externa dentro del hilo thread y enviar los datos que recibo en ella y mostrarlos en la aplicacion como hago esto usando un handler ¿Uso tambien setprogress?
ResponderEliminargenial gracias, espero poder seguir leyendo tus entradas..y otra vez, muchas gracias.
ResponderEliminaruna pregunta, como puedo suscribirme, para poder seguir el blog.
ResponderEliminarHe añadido un botón de seguir bajo el menú.
EliminarMuchas gracias por tu interés!
Muchas gracias por ejemplo, estoy haciendo un listado de ejercicios para un nuevo post en mi blog,ya que estoy empezando a manejar los threats, el ejercicio que has puesto en bruto sera el primero. Muchas gracias
ResponderEliminarhttp://thebestandroide.blogspot.com.es/
Perfecto, pero deberías poner el link de este blog. Recuerda el CC creative Commons
EliminarJope, que bien explicado. Así da gusto buscar información en Internet.
ResponderEliminarMuchas gracias!
EliminarHola, me quedo claro en su mayoría lo que explicaste.. Pero tengo una duda, yo hice una pagina web (mapa) con php y base de datos, quiero que esta pag aparezca en mi webView en Android Studio y que cuando le de clic a un marcador me muestre su información (que se almacena en la BD ¿Como le hago para hacer que funcione en si?, es decir, ¿Que la app muestre un toast al dar clic en un marcador y esta muestre información desde la BD?
ResponderEliminarExcelente gracias
ResponderEliminarVictor Muchisimas Gracias!!!
ResponderEliminarAtte: Renato
Excelente!!!
ResponderEliminarGracias!!
Graciasss!!
ResponderEliminar