[Ayuda]Timer en Android

Iniciado por PabloPbl, 12 Abril 2015, 05:41 AM

0 Miembros y 1 Visitante están viendo este tema.

PabloPbl

Pues cuando yo trabajaba en Java puro, recuerdo que usaba muchísimo la clase Timer la cual sirve para hacer correr procesos particulares o en diferentes hilos.

Pues ahora eso mismo me gustaría hacerlo en Android, me he fijado la Api de Android y si esta disponible la misma, solo que Android la ha cambiado bastante y lo encuentro un tanto complicado, nada que ver como lo hacia en Java.

He buscado por la red y he encontrado bastante material, por ejemplo este y otros:
http://stackoverflow.com/questions/4597690/android-timer-how
Trato de entenderlo, pero me es complicado, ¿alguien me podría ayudar a entender un poco de esto?

Se que hay otras formas como los Services, pero es demasiado para lo que requiero, necesito algo sencillo, por eso elige este, si alguien conoce alguna clase mejor, que la diga.

Usuario Invitado

Utiliza Calendar de Java, que creo está disponible en el kit de Android. Es fácil de manejar.


Saludos.
"La vida es muy peligrosa. No por las personas que hacen el mal, si no por las que se sientan a ver lo que pasa." Albert Einstein

PabloPbl

Calendar sirve para hacer acciones en otro hilo?, tengo entendido que sirve para obtener la fecha y la hora, me acabo de fijar en la API de Android y pues si, esta ahí.

Salu2

Usuario Invitado

Ya veo, tu me hablas de concurrencia. ¿Existe alguna diferencia notoria entre Timer de Java y Android?

Es decir, en Android es tan diferente hacer ésto:

Código (java) [Seleccionar]
class SomeTask extends TimerTask {
   @Override
   public void run() {
     System.out.println("Time's up!");
   }
}


Código (java) [Seleccionar]
public class Main {
   private Timer timer;

   public void initialize() {
       timer = new Timer();
       timer.schedule(new SomeTask(), 1000, 5000); // inicia en 1 segundo y se repite cada 5
   }
   public void cancel() {
       if(timer != null) {
           timer.cancel();
           timer = null;
       }
   }
}
"La vida es muy peligrosa. No por las personas que hacen el mal, si no por las que se sientan a ver lo que pasa." Albert Einstein

PabloPbl

Haber si entiendo, estas creando una clase que hereda de TimerTask y sobrescribes su método run, que es el que se va a ejecutar cada cierto tiempo.

Y desde otra clase, creas un Timer:
Código (java) [Seleccionar]
timer = new Timer();

Y con esto especificas que acción se va a ejecutar cada cierto tiempo,  la cual es la clase TimerTask y tiene 2 parámetros más, en el primero se especifica cuando iniciara, y el segundo especificas cada cuanto se va a ejecutar la acción.
Código (java) [Seleccionar]
timer.schedule(new SomeTask(), 1000, 5000); // inicia en 1 segundo y se repite cada 5

En resumen este código mostraría Time's up! en pantalla cada 5 segundos?

Usuario Invitado

Exactamente. El primer parámetro de schedule indica el timeout, es decir, la cuenta regresiva para que se inicie por primera véz la tarea, y el segundo parámetro el intérvalo entre ejecuciones. Ésto es ideal cuando se quiere mostrar avisos cada X tiempo, por ejemplo.


Un saludo.
"La vida es muy peligrosa. No por las personas que hacen el mal, si no por las que se sientan a ver lo que pasa." Albert Einstein

PabloPbl

#6
Perfecto, muchas gracias Gus  ;D, he hecho pruebas y funciona de maravilla, pero hasta cierto punto.

La primera prueba la hice de la siguiente manera:

Código (java) [Seleccionar]

public class Prueba extends ActionBarActivity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_1);

       Timer timer = new Timer();
       timer.schedule(new classProcess(),1000, 1000);
   }
   
   class classProcess extends TimerTask{

   public void run() {
       Log.e("HILO CORRIENDO", "HILO CORRIENDO");
   }
   }
}


Pues el código de arriba funciona perfecto, he utilizado un clase interna(sabia que alguna día me iba a ser útil) que extiende de TimerTask y sobreescribo el método run y pongo que se mostrara un Log en la consola. Todo esto se ejecutara desde el Timer, que lo cree en el método onCreate, le paso como parámetro una instancia de la clase interna "classProcess", iniciara en 1 segundo y se repetirá cada 5 segundos, esto lo hace de lujo.

Pero cuando quiero, modificar el valor de un TextView cada cierto tiempo, en este caso cada segundo, me arroja un error en el emulador. El código lo he hecho de 2 formas, pero ninguna me anda.

Antes de mostrar el código, quería comentarles lo que quiero hacer. Lo que quiero lograr es que un textView cambie su valor cada segundo, osea funciona como un temporizador. Por defecto le pongo un texto que dice 30 segundos,  y bueno con la ayuda de Timer quiero hacer que el TextView vaya bajando cada segundo hasta llegar a 0 y ahí detenerse.

Esta es la primera forma, he puesto un par de comentarios en el código para que se entienda más el mismo:
Código (java) [Seleccionar]

public class Prueba extends ActionBarActivity {

   private TextView tv;

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

       tv = (TextView)findViewById(R.id.tvTemporizador);

       Timer timer = new Timer();
       timer.schedule(new classProcess(),1000, 1000);
   }
   
   class classProcess extends TimerTask{

       public void run() {
           int bajar1Segundo = Integer.parseInt(tv.getText().toString())-1;//Obtengo el valor y lo bajo 1, por ejemplo, si es 30 ahora sera 29
           tv.setText(String.valueOf(bajar1Segundo));//Cambio el valor del TextView

           if(bajar1Segundo == 0) {
               timer.cancel();//No se si este método parara el Timer, No llegue a comprobar, ya que no llega a hasta este punto, ya que apenas inicia el programa se traba al querer cambiar los valores de los TextView
               Toast.makeText(getApplicationContext(), "El tiempo se ha terminado", Toast.LENGTH_LONG);
           }
       }
   }
}

En el código de arriba, creo que el error estaba en que: desde la clase interna quería acceder a las variables de clase que son privadas, de la clase principal, ustedes me dirán, ¿Por que no te diste cuenta de esto antes? pues es la primera vez que lo hago así, aparte el IDE no me arrojaba error, no me marcaba con lineas roja ni nada, y es por eso que he intentado también de esta forma:

Código (java) [Seleccionar]

public class Prueba extends ActionBarActivity {

   private TextView tv;

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

       tv = (TextView)findViewById(R.id.tvTemporizador);

       Timer timer = new Timer();
       timer.schedule(new ClassProcess(this.tv),1000, 1000);
   }
   
   class ClassProcess extends TimerTask{
       private TextView tv;
       
       public classProcess(TextView tv) {
           this.tv = tv;
       }
       
       public void run() {
           int bajar1Segundo = Integer.parseInt(tv.getText().toString())-1;//Obtengo el valor y lo bajo 1, por ejemplo, si es 30 ahora sera 29
           tv.setText(String.valueOf(bajar1Segundo));//Cambio el valor del TextView

           if(bajar1Segundo == 0) {
               Toast.makeText(getApplicationContext(), "El tiempo se ha terminado", Toast.LENGTH_LONG);
           }
       }
   }
}


Pues la diferencia entre el código que esta arriba y el que esta arriba de este de arriba(ya saben xD), es que la clase interna recibe parámetros en su constructor, recibe un TextView. Entonces en vez en vez de interactuar directamente con el TextView de la clase principal, interactuo con el TextView que recibo por parámetro de la clase principal, pero también me da error, y no se que puede ser.

Si no entienden algo, díganmelo.

Salu2

PabloPbl

#7
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Ese error, creo que me esta diciendo que solo puedo modificar las Views(Elementos visuales) desde el Thread original y no desde otro Hilo.

¿Que alguien me diga si esto es cierto?

Usuario Invitado

#8
Así es. Tienes que obtener el thread en donde está corriendo el Activity. Intenta:

Código (java) [Seleccionar]

getActivity().runOnUiThread(new Runnable() {
   @Override
   public void run() {
       //Obtengo el valor y lo bajo 1, por ejemplo, si es 30 ahora sera 29
       int bajar1Segundo = Integer.parseInt(tv.getText().toString())-1;
       tv.setText(String.valueOf(bajar1Segundo)); //Cambio el valor del TextView
       if(bajar1Segundo == 0) {
           Toast.makeText(getApplicationContext(), "El tiempo se ha terminado", Toast.LENGTH_LONG);
       };
   }
});


El código anterior debe ir dentro del run de ClassProcess.
"La vida es muy peligrosa. No por las personas que hacen el mal, si no por las que se sientan a ver lo que pasa." Albert Einstein

PabloPbl

Pongo el código dentro del run de la clase interna, pero me da error en esta linea, creo que no reconoce el método getActivity()

Código (java) [Seleccionar]
getActivity().runOnUiThread(new Runnable() {

También he intentado de esta forma, pero también me da error:

Código (java) [Seleccionar]
getApplication().runOnUiThread(new Runnable() {