«

»

nov 19 2012

Android Services (3/4) – Implementación de Services y NotificationManager

Hola a todos, en este tercer tutorial vamos a aprender varias cosas. Inicio de Services automáticos desde Receivers, conocimiento de los PendingIntents, ejemplo de uso de IntentService para descargar un archivo, y creación y ejemplo de un NotificationManager.
Espero que os sirva :)

 

 

Inicio automático de Services desde Receivers

Para iniciar automáticamente un Service después de que el sistema Android se inicia, podemos registrar un BroadcastReceiver al evento del sistema android.intent.action.BOOT_COMPLETED. Esta acción requiere el permiso android.permission.RECEIVE_BOOT_COMPLETED.
En el siguiente código mostramos como el AndroidManifest.xml registra un receiver para el evento BOOT_COMPLETED.
 

           <?xml version="1.0" encoding="utf-8"?>
           <manifest xmlns:android="http://schemas.android.com/apk/res/android"
	       package="es.androcode.android.ownservice.local"
	       android:versionCode="1"
	       android:versionName="1.0"

	       <uses-sdk android:minSdkVersion="10"/>
	       <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
	
	       <application
	          android:icon="@drawable/icon"
	          android:label="@string/app_name"
		  <activity
		     android:name=".ServiceConsumerActivity"
		     android:label="@string/app_name">
		     <intent-filter>
			 <action android:name="android.intent.action.MAIN" />
			 <category android:name="android.intent.category.LAUNCHER"/>
		     </intent-filter>
		  </activity>
		  <receiver android:name="MyScheduleReceiver"
			<intent-filter>
			    <action android:name="android.intent.action.BOOT_COMPLETED">
			</intent-filter>
		</receiver>
		<receiver android:name="MyStartServiceReceiver">
		</receiver>
              </application>
           </manifest>
           

 

En el método onReceive() del correspondiente BroadcastReceiver comenzará el servicio.
 

            import android.content.BroadastReceiver;
            import android.content.Context;
            import android.content.Intent;

            public class MiReceiver extends BroadcastReceiver{
	         public void onReceive(Context context,Intent intent){
		   Intent service = new Intent(context,WordService.class);
		   context.startService(service);
	         }
            }
          

 

Si nuestra aplicación está instalada en la tarjeta SD, entonces no está disponible después del evento android.intent.action.BOOT_COMPLETED. Para esto tenemos que registrar nosotros mismos en este caso el evento android.intent.action.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.
Tenemos que tener en cuenta que a partir de la versión Android 3.0, el usuario debe haber iniciado la aplicación al menos una vez antes que la aplicación pueda recibir el evento android.intent.action.BOOT_COMPLETED.

 

Programación de Services desde AlarmManager

Al igual que con las Activities, el sistema Android podrá dar por terminado el proceso de un Service en cualquier momento para ahorrar recursos.
Por esto, no podemos usar un sencillo TimerTask en el servicio para asergurarnos de que se ejecuta regularmente. El siguiente código muestra lo que NO se debe hacer, ya que Android podrá terminar nuestro Service.
 

            import android.content.BroadastReceiver;
            public void onCreate() {
              super.onCreate();
              pollForUpdates();
            }

            // MAL, NO HACEMOS ESTO!
             private void pollForUpdates() {
             timer.scheduleAtFixedRate(new TimerTask() {
             @Override
             public void run() {
               if (list.size() >= 6) {
                list.remove(0);
               }
               list.add(fixedList[index++]);
               if (index >= fixedList.length) {
                index = 0;
                }
              }
            }, 0, UPDATE_INTERVAL);
            Log.i(getClass().getSimpleName(), "Timer started.");
            } 
          

 
Para una correcta programación del Service se usa la clase AlarmManager

 

Pending Intent

Un PendingIntent es un token que se le da a otra aplicación(por ejemplo Administrador de notificaciones, AlarmManager u otras aplicaciones), lo que permite a esta otra aplicación usar los permisos de nuestra aplicación para ejecutar una pieza de código predefinido.
Para realizar un broadcast o transmisión a través de una PendingIntent para obtener un PendingIntent lo hacemos a través de PendingIntent.getBroadcast().
Para realizar una Activity desde un PendingIntent se recibe la actividad a través de PendingIntent.getActivity().

 

Usando IntentService para descargar un archivo

Ahora vamos a explicar cómo usar una clase IntentService para descargar un archivo desde Internet. Una vez hecho el IntentService usará una instancia de la clase Messenger para informar al Activity que empezó el servicio sobre la ubicación del archivo descargado.

Creamos un nuevo proyecto llamado “es.androcode.android.intentService.download” con una Activity llamada MainActivity.

Creamos un servicio “DownloadService” para crear la siguiente clase y la entrada en el AndroidManifest.xml. También añadimos el permiso para escribir en una memoria externa y para acceder a Internet.

 

            package es.androcode.android.intentservice.download;

            import java.io.File;
            import java.io.FileOutputStream;
            import java.io.IOException;
            import java.io.InputStream;
            import java.io.InputStreamReader;
            import java.net.URL;

            import android.app.Activity;
            import android.app.IntentService;
            import android.content.Intent;
            import android.net.Uri;
            import android.os.Bundle;
            import android.os.Environment;
            import android.os.Message;
            import android.os.Messenger;
            import android.util.Log;

            public class DownloadService extends IntentService{
	       private int result = Activity.RESULT_CANCELED;
	       public DownloadService(){
		  super("DownloadService");	
	       }
	
	       protected void onHandleIntent(Intent intent){
		   Uri data = intent.getData();
		   String urlPath = intent.getStringExtra("urlPath");
		   String fileName = data.getLastPathSegment();
		   File output = new File(Environment.getExternalStorageDirectory(),fileName);
		   if(output.exists()){
		      output.delete();		
		    }	
		
		   InputStream stream = null;
		   FileOutputStream fos = null;
		   try{
		      URL url = new URL(urlPath);
		      stream = url.openConnection().getInputStream();
		      InputStreamReader reader = new InputStreamReader(stream);
		      fos = new FileOutputStream(output.getPath());
		      int next = -1;
		      while((next==reader.read()) != -1){
			fos.write(next);			
		      }		
		  //Finalizado correctamente
		    result = Activity.RESULT_OK;
		   }catch(Exception e){
		      e.printStackTrace();		
		   }finally{
		      if(stream != null){
			try{
		          stream.close();				
			}catch(IOException e){
			  e.printStackTrace();				
			}
		      }
		      if(fos != null){
			try{
		          fos.close();					
			}catch(IOException e){
		          e.printStackTrace();					
			}
		       }		
		   }
		   Bundle extras = intent.getExtras();
		   if(extras != null){
		      Messenger messenger = (Messenger)extras.get("MESSENGER");
		      Message msg = Message.obtain();
		      msg.arg1 = result;
		      msg.obj = output.getAbsolutePath();
		      try{
			 messenger.send(msg);			
		       }catch(android.os.RemoteException e1){
			   Log.w(getClass().getName(),"Excepción enviando mensaje", e1);			
		       }		
		    }
	        }
             }
            

 

El AndroidManifest.xml será el que sigue:

 

             <?xml version="1.0" encoding="utf-8"?>
             <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="es.androcode.android.intentservice.download"
              android:versionCode="1"
              android:versionName="1.0" >

             <uses-sdk android:minSdkVersion="15" />
             <uses-permission android:name="android.permission.INTERNET"/>
             <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
             <application
                android:icon="@drawable/ic_launcher"
                android:label="@string/app_name" >
                <activity
                   android:name=".MainActivity"
                   android:label="@string/app_name" >
                   <intent-filter>
                      <action android:name="android.intent.action.MAIN" />

                      <category android:name="android.intent.category.LAUNCHER" />
                   </intent-filter>
                </activity>

                <service android:name="DownloadService" >
                </service>
             </application>

          </manifest> 
          

 

Ahora cambiamos el layout main.xml al siguiente:
 

	   <?xml version="1.0" encoding="utf-8"?>
           <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" >

             <Button
                android:id="@+id/button1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="onClick"
                android:text="Boton" />
           </LinearLayout> 
           

 

Cambiamos la clase MainActivity a la siguiente:

 

            package es.androcode.android.intentservice.download;

            import android.app.Activity;
            import android.content.Intent;
            import android.net.Uri;
            import android.os.Bundle;
            import android.os.Handler;
            import android.os.Message;
            import android.os.Messenger;
            import android.view.View;
            import android.widget.Toast;

            public class MainActivity extends Activity {
               private Handler handler = new Handler() {
                  public void handleMessage(Message message) {
                         Object path = message.obj;
                         if (message.arg1 == RESULT_OK && path != null) {
                            Toast.makeText(MainActivity.this,
                                           "Descargado" + path.toString(), Toast.LENGTH_LONG).show();
                         } else {
                            Toast.makeText(MainActivity.this, "Fallo en la descarga.",
                                           Toast.LENGTH_LONG).show();
                         }

                  };
               }; 

               @Override
               public void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  setContentView(R.layout.main);
               } 

               public void onClick(View view) {
                  Intent intent = new Intent(this, DownloadService.class);
                  // Creamos un nuevo Messenger para devolver la comunicación
                  Messenger messenger = new Messenger(handler);
                  intent.putExtra("MESSENGER", messenger);
                  intent.setData(Uri.parse("http://www.androcode.es/index.html"));
                  intent.putExtra("urlpath", "http://www.androcode.es/index.html");
                  startService(intent);
               }
            } 
           

 

Si ejecutamos el ejemplo y presionamos el botón, la descarga debe ser realizada por el Service y una vez hecho la Activity debería mostrar un Toast con el nombre del archivo.

 
Hasta aquí esta primera parte de esta tercera parte… Ahora vamos a ver qué son NotificationManager y un ejemplo de uso.

 

Notification Manager

Android nos permite crear notificaciones en la barra de notificaciones sobre nuestra aplicación. El usuario puede expandir esta barra de notificación y por selección puede activar otra actividad.

 

Configuración de Notificaciones

Las notificaciones en Android están representadas por la clase Notification. Para crear notificaciones usamos la clase NotificationManager que puede ser recibida desde el Activity por medio del método getSystemService().

 

       NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
       

 

El Notification.Builder proporciona una interfaz constructora para crear un objeto Notification. Podemos usar un PendingIntent para especificar la acción que se debe realizar una vez que el usuario seleccione la notificación. Notification.Builder permite añadir hasta dos y tres botones con las acciones definidas en la notificación.

 

        //Preparamos el Intent que es lanzado si la notificación es seleccionada
        Intent intent = new Intent(this, NotificationReceiver.class);
        PendingIntent pIntent = PendingIntent.getActivity(this,0,intent,0);
        //Creamos la notificación
        //Las acciones son de "mentirijilla"
        Notification noti = new Notification.Builder(this)
                            .setContentTitle("Ejemplo de Notificación")
                            .setContentText("Asunto_notificación").setSmallIcon(R.drawable.small_icon)
                            .setContentIntent(pIntent)
                            .addAction(R.drawable.icon,"Llamada", pIntent)
                            .addAction(R.drawable.icon,"Más", pIntent)
                            .addAction(R.drawable.icon,"Mucho más", pIntent).build();

       NotificationManager notificationManager = 
                           (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
       //Se esconde la notificación tras ser seleccionada
       noti.flags |= Notification.FLAG_AUTO_CANCEL;
       notificationManager.notify(0, noti);
       

 

Podemos además ajustar la notificación para usar más espacio(hasta 256dp) ajustando el estilo.

 

        String longText = "...";
        Notification noti = new Notification.Builder(this).
        ...
        .setStyle(new Notification.BigTextStyle().bigText(longText))
       

 

Ejemplo de NotificationManager

Creamos un nuevo proyecto es.androcode.android.notificationmanager con la clase Activity llamada CreateNotificationActivity. Esta Activity debe usar el layout siguiente main.xml:

 

        <?xml version="1.0" encoding="utf-8"?>
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical" >

            <Button
                android:id="@+id/button1"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:onClick="createNotification"
                android:text="Create Notification" >
            </Button>

        </LinearLayout> 
       

 

Y creamos el siguiente layout llamado result.xml

 

       <?xml version="1.0" encoding="utf-8"?>
       <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <TextView
                android:id="@+id/textView1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="This is the result activity opened from the notification" >
            </TextView>

        </LinearLayout> 
       

 

Creamos una nueva Activity llamada NotificationReceiverActivity con el código siguiente, sin olvidar registrar la Activity en el AndroidManifest.xml

 

        import android.app.Activity;
        import android.os.Bundle;
        public class NotificationReceiverActivity extends Activity{
            protected void onCreate(Bundle savedInstanceState){
              super.onCreate(savedInstanceState);
              setContentView(R.layout.result);
            }
        }
       

 

Ahora cambiamos la clase CreateNotificationActivity al código siguiente:

 

        import android.app.Activity;
        import android.app.Notification;
        import android.app.NotificationManager;
        import android.app.PendingIntent;
        import android.content.Intent;
        import android.os.Bundle;
        import android.view.View;

        public class CreateNotificationActivity extends Activity {
        @Override
             public void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                    setContentView(R.layout.main);
             }

             public void createNotification(View view) {
             // Preparamos el intent que será lanzado si la notificación es seleccionada
            
                    Intent intent = new Intent(this, NotificationReceiverActivity.class);
                    PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);

            // Creamos la notificación. Las acciones son de "mentirijilla"
                    Notification noti = new Notification.Builder(this)
                                       .setContentTitle("New mail from " + "test@gmail.com")
                                       .setContentText("Subject").setSmallIcon(R.drawable.small_icon)
                                       .setContentIntent(pIntent)
                                       .addAction(R.drawable.icon, "Llamada", pIntent)
                                       .addAction(R.drawable.icon, "Más", pIntent)
                                       .addAction(R.drawable.icon, "Mucho Más", pIntent).build();
                    NotificationManager notificationManager =                              
                                        (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
                    // Ocultamos la notificación si ha sido ya seleccionada
                     noti.flags |= Notification.FLAG_AUTO_CANCEL;

                     notificationManager.notify(0, noti);

                     }
                } 
       

 

Ahora ejecutamos la aplicación y presionamos el botón. Una nueva notificación es creada. Si seleccionamos la segunda actividad será mostrada.
 

Hasta aquí el tercer post de esta tanda de tutoriales. Espero que os sirva y apliqueis lo aprendido :)
Me haría mucha ilusión que escribierais si os ha gustado y si no pues también.
Un saludo!

 
Fuente | http://www.vogella.com

 

Espero que os haya gustado y si es así no lo dudéis y ¡¡compartid lo aprendido!!

 

Acerca del autor

InmaculadaAlcon

Me entusiasma aprender y desarrollar software de calidad. Crear aplicaciones para Android es lo que más me gusta y por lo que me muevo cada día. Soy muy flamenca, enamorada de Sevilla y con el sueño de trabajar algún día en Silicon Valley. Me encanta el fútbol y soy del Betis manque pierda.

  • http://gravatar.com/gabrielpozo jackgris

    Muy bueno el post ;)

  • pakete
  • Miguel

    BOOT_COMPLETED no funciona para la versión de android 4.1
    como puedo hacer para que este ejemplo funcione perfectamente. Muchas gracias

  • gustavo alonso solano sibaja

    me gusto el post, espero ponerlo en practica, eh estado investigando unpoco de los servicios en segundo plano y este es de los post que mejor lo explican, saludos.