Tareas Repetidas en Service en Android

Buenas tardes, en este ejemplo vamos a ver como podemos realizar Tareas repetidas en un Service en Android. Seguiremos para esto con nuestro anterior proyecto que podemos encontrar aquí.


Además de poder hacer tareas largas en un Service, puede que también queramos hacer algunas tareas repetidamente en el Service. Por ejemplo, puede que queramos escribir un servicio para que una alarma del reloj se ejecute persistentemente en segundo plano, por ejemplo. En este caso, nuestro servicio puede necesitar que se ejecute algún bloque de código para comprobar cuando una sincronización se ha alcanzado para que la alarma suene. Para ejecutar ese bloque de código en un intervalo de tiempo regular, podemos usar la clase Timer dentro de nuestro servicio.

Con esta pequeña introducción vamos a ponernos manos a la obra:

  • MyService

    Como ya conocemos, la clase “MyService.java” es la clase que contiene a nuestro Service. Lo que hacía hasta ahora, era ejecutar una AsyncTask que emulaba la descarga de imágenes de varias url.

    La vamos a modificar, para que en otro hilo independiente, se ejecute una tarea repetidamente, mientras en otro hilo, para ser exactos en la AsyncTask, se “descarguen” las imágenes.

    Vamos a ver su código para verlo todo mas claro:

    		package sekth.droid.Services;
    
    		import java.net.MalformedURLException;
    		import java.net.URL;
    		import java.util.Timer;
    		import java.util.TimerTask;
    		
    		import android.app.Service;
    		import android.content.Intent;
    		import android.os.AsyncTask;
    		import android.os.IBinder;
    		import android.util.Log;
    		import android.widget.Toast;
    		
    		public class MyService extends Service {
    			int counter = 0;
    			static final int UPDATE_INTERVAL = 1000;
    			private Timer timer = new Timer();
    		
    			@Override
    			public IBinder onBind(Intent intent) {
    				// TODO Auto-generated method stub
    				return null;
    			}
    		
    			@Override
    			public int onStartCommand(Intent intent, int flags, int startId) {
    				// TODO Auto-generated method stub
    		
    				// Queremos que este servicio se ejecute continuamente
    				// hasta que sea detenido manualmente, por lo que retornaremos
    				// START_STICKY
    		
    				doSomethingRepeatedly();
    		
    				try {
    					new DoBackgroundTask().execute(new URL(
    							"http://www.google.com/imagen1.png"), new URL(
    							"http://www.google.com/imagen2.png"), new URL(
    							"http://www.google.com/imagen3.png"), new URL(
    							"http://www.google.com/imagen4.png"));
    				} catch (MalformedURLException ex) {
    					ex.printStackTrace();
    				}
    		
    				return START_STICKY;
    			}
    		
    			private void doSomethingRepeatedly() {
    				timer.scheduleAtFixedRate(new TimerTask() {
    		
    					@Override
    					public void run() {
    						// TODO Auto-generated method stub
    						Log.d("MyService", String.valueOf(++counter));
    					}
    		
    				}, 0, UPDATE_INTERVAL);
    			}
    		
    			private int DownloadFile(URL url) {
    				try {
    					// Simulamos la descarga de un fichero
    					Thread.sleep(5000);
    				} catch (InterruptedException ex) {
    					ex.printStackTrace();
    				}
    		
    				return 100;
    			}
    		
    			private class DoBackgroundTask extends AsyncTask<URL, Integer, Long> {
    		
    				@Override
    				protected Long doInBackground(URL... urls) {
    					// TODO Auto-generated method stub
    					int count = urls.length;
    					long totalKBytesDownloaded = 0;
    					for (int i = 0; i < count; i++) {
    						totalKBytesDownloaded += DownloadFile(urls[0]);
    						// --Calculamos el porcentaje descargado y
    						// --reportamos el progreso
    						publishProgress((int) (((i + 1) / (float) count) * 100));
    					}
    					return totalKBytesDownloaded;
    				}
    		
    				@Override
    				protected void onProgressUpdate(Integer... values) {
    					// TODO Auto-generated method stub
    					super.onProgressUpdate(values);
    					Log.d("Descargando ficheros", String.valueOf(values[0])
    							+ "% descargado");
    					Toast.makeText(getBaseContext(), values[0] + "% descargado",
    							Toast.LENGTH_SHORT).show();
    				}
    		
    				@Override
    				protected void onPostExecute(Long result) {
    					// TODO Auto-generated method stub
    					super.onPostExecute(result);
    					Toast.makeText(getBaseContext(),
    							"Descargado " + result + " KBytes", Toast.LENGTH_SHORT)
    							.show();
    					stopSelf();
    				}
    			}
    		
    			@Override
    			public void onDestroy() {
    				// TODO Auto-generated method stub
    				super.onDestroy();
    				if (timer != null) {
    					timer.cancel();
    				}
    				Toast.makeText(getBaseContext(), "Servicio Detenido",
    						Toast.LENGTH_SHORT).show();
    			}
    		
    		}
    		

    Como podemos ver a primera vista, hemos definido 3 variables de instancia para la clase MyService, en este caso es un int llamado counter, una variable final static llamada UPDATE_INTERVAL, y nuestra clase Timer, la cual usaremos para hacer una tarea repetidamente.

    El resto es casi igual, excepto que hacemos una llamada al método doSomethingRepeatedly() antes de ejecutar nuestra BackgroundTask para descargar los ficheros.

    • doSomethingRepeatedly()

      Vamos a ver su código:

      				private void doSomethingRepeatedly() {
      					timer.scheduleAtFixedRate(new TimerTask() {
      			
      						@Override
      						public void run() {
      							// TODO Auto-generated method stub
      							Log.d("MyService", String.valueOf(++counter));
      						}
      			
      					}, 0, UPDATE_INTERVAL);
      				}
      				

      En este ejemplo hemos creado un objeto Timer y hemos llamado al método scheduleAtFixedRate():

      				timer.scheduleAtFixedRate(new TimerTask() {
      
      					@Override
      					public void run() {
      						// TODO Auto-generated method stub
      						Log.d("MyService", String.valueOf(++counter));
      					}
      		
      				}, 0, UPDATE_INTERVAL);
      				

      Hemos pasado una instancia de la clase TimerTask en el método scheduleAtFixedRate() en el que podemos ejecutar un bloque de código dentro del método run() repetidamente. El segundo parámetro en el scheduleAtFixedRate() especifica la cantidad de tiempo, en milisegundos, antes de la primera ejecución, y el tercer parámetro especifica la cantidad de tiempo, en milisegundos, entre secuencia de ejecuciones.

      Lo que hemos hecho en el ejemplo anterior, es imprimir el valor del contador cada segundo (1000 milisegundos). Esto se repetirá continuamente hasta que el servicio se termine.

    En este caso nuestro Service se terminará cuando se ejecute nuestra AsyncTask, ya que en el método onPostExecute() de nuestra AsyncTask llamamos al método stopSelf() que detendrá el Service.

Cuando ejecutamos nuestra aplicación, si consultamos el LogCat, vemos que el bloque de código de doSomethingRepeatedly() se ejecuta a la vez que se está ejecutando nuestra descarga en segundo plano de los ficheros. Esto puede hacerse porque la clase TimerTask implementa por sí misma la interface Runnable, la cual le permite ejecutarse en otro hilo separado.

A continuación vemos unas imágenes para ver como queda finalmente el LogCat para ver el progreso de la ejecución:

Logcat

Un vídeo en el que vemos todo el mecanimos en funcionamiento:



En esta entrada hemos visto que en nuestro Service hemos ejecutado 2 cosas a la vez, cada una en un mismo hilo, por tanto ninguna de ellas se molesta, y lo mas importante, el Service no bloquea el hilo del hilo principal de nuestra aplicación, por lo que podríamos interactuar tranquilamente mientras todo este proceso se va realizando por detrás. Aún así también vemos que la AsyncTask es usado como delimitador, es decir, cuando se complete, el Service se detendrá, por tanto la tarea repetida también se anulará.

El código de este ejemplo puedes descargarlo de aquí.

Sin más, cualquier aporte o corrección es bienvenido.

Saludos!!!

  • que tal amigo muy buenos tutoriales,tengo una pregunta, …necesito saber si puedo ejecutar, iniciar un servicio desde un intent service que me descargue unos archivos desde un servidor.saludos

    • Buenas Luis,

      Un IntentService de por sí ya es un servicio, pero a diferencia de los Service estos se ejecutan ya en otro hilo, por lo que no tienes que preocuparte de temas como crear una asynctask o ejecutarlo en un nuevo Thread.

      Así que respondiendo a tu pregunta, sí, puedes usar un IntentService para tareas de ese tipo sin ningún problema desde un Intent.

      Saludos!!!