LabsAndroid

Laboratoire 5
Services, notifications et récetpeurs d'événements

Objectifs d’apprentissage (test pour Victor)

Cycle de vie d’un service

Un service est un composant applicatif qui ne possède pas d’interface graphique et qui s’exécute en arrière-plan. Son utilisation peut être limitée à l’application où il est défini ou proposée à d’autres applications. Ce type d’information est précisé dans le fichier manisfest lors de la déclaration du service. Un service peut être lancé de deux façons :

Les deux méthodes peuvent être utilisées en même temps.

Cycle de vie

Important : Un service s’exécute dans le thread principal de l’application. Il est fortement conseillé de créer des threads pour vos services afin de dédier le thread principal aux interactions avec l’utilisateur.

Lancement et arrêt d’un service

activity_main

public class TestService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("TestService", "onCreate");
    }

    public int onStartCommand(Intent intent, int flags, int startId) {
        // L'exécution de cette méthode se fait dans le thread principal.
        // Pour ne pas bloquer l'application, il est vivement recommandé
        // d'exécuter tout traitement long dans un thread secondaire
        Log.i("TestService", "onStartCommand: startId=" + startId);
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
final Intent intent = new Intent(this,TestService.class);
startService(intent);
<service android:name=".TestService"></service>
public void onDestroy() {
    Log.i("TestService", "onDestroy");
    super.onDestroy();
}
final Intent intent = new Intent(this,TestService.class);
stopService(intent);
public class TestService extends Service {
    public static final String CLE_STOP = "stop";
    public int onStartCommand(Intent intent, int flags, int startId) {
        final boolean stop = intent.getBooleanExtra(CLE_STOP, false);

        if (stop) {
            Log.i("TestService", "stopSelf");
            stopSelf();
        }
    }
}
final Intent intent = new Intent(this, Testservice.class);
intent.putExtra(TestService.CLE_STOP, true);
startService(intent);

Connexion et déconnexion d’un service

// Cette classe est une spécialisation de la classe Binder.
// elle retourne une instance du service TestService
public class MonServiceBinder extends Binder {
    public TestService getService() {
        return TestService.this;
    }
}

// créer une référence vers l'instance du service TestService
private final IBinder mBinder = new MonServiceBinder();

// remplacer la méthode `**`onBind()`**` par :
public IBinder onBind(Intent intent) {
    Log.i("TestService", "onBind");
    return mBinder;
}

@Override
public boolean onUnbind(Intent intent) {
    Log.i("TestService", "onUnbind");
    return true;
}
private TestService mService;

// définition de la connexion
private final ServiceConnection mConnexion = new ServiceConnection() {
    public void onServiceConnected(ComponentName arg0, IBinder arg1) {
        Log.i("MainActivity", "onServiceConnected");
        mService = ((TestService.MonServiceBinder) arg1).getService();
        btnConnectDeconnect.setText(R.string.service_bouton_Deconnect);
    }

    public void onServiceDisconnected(ComponentName arg0) {
        Log.i("MainActivity", "onServiceDisconnected");
        mService = null;
        btnConnectDeconnect.setText(R.string.service_bouton_Connect);
    }
};

// dans le clic du bouton Connecter/deconnecter
if (mService == null) {
    final Intent intent = new Intent(this, Testservice.class);
    bindService(intent, mConnexion, Context.BIND_AUTO_CREATE);
} else {
    unbindService(mConnexion);
    mService = null;
    btnConnectDeconnect.setText(R.String.service_bouton_Connect);
}

IMPORTANT: ne pas oublier de déclarer les chaines de caractères dans le fichier ressource strings.

Notifications

La barre de notification ou barre de statut permet aux applications tournant en tâche de fond d’avertir l’utilisateur sans perturber l’utilisation courante de l’appareil. Cette barre reçoit et stocke les notifications qui lui sont envoyées par les applications.

Création et envoi d’une notification

Notifications

Activité Notifications

Bitmap mLogo = BitmapFactory.decodeResource(getResources(), R.drawable.logogg);
final long[] patternVibrations = { 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,50 };

// création de la notification. Noter cette nouvelle façon de créer un objet final
Notification.Builder notificationBuilder = new Notification.Builder(this)
    .setLargeIcon(mLogo)
    .setAutoCancel(true) // Notification disparaît quand l'utilisateur la touche.
    .setSmallIcon(R.drawable.ic_launcher)
    .setContentTitle(getString(R.string.notification_titre))
    .setContentText(getString(R.string.notification_text))
    .setNumber(1)
    .setVibrate(patternVibrations);

// définition de l’activité qui sera lancée par la notification
final Intent notificationIntent = new Intent(this, NotificationActivity.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

final PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notificationBuilder.setContentIntent(pendingIntent);

// noter l’utilisation d’un PendingIntent. (Intention en attente)
// envoi de la notification
final Notification notification = notificationBuilder.getNotification();
Toast.makeText(this, (notification == null) ? R.string.notification_erreur : R.string.notification_ajoutee, Toast.LENGTH_LONG).show();
if (notification != null) {
    final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(0, notification);
}
<uses-permission android:name="android.permission.VIBRATE" />

Cette balise est dans la balise manifest mais en dehors de la balise application

Utilisation d’une notification dans le cas d’un service

Nous allons compléter la classe TestService pour permettre l’affichage d’une notification informant que le service est bien démarré. Contrairement à la notification créée en 2.1, celle-ci sera persistante. Elle ne pourra donc pas être supprimée par l’utilisateur. Il faudra s’assurer que l’application la supprime quand le service sera détruit.

private void afficherNotification(final int nb) {
    Bitmap mLogo = BitmapFactory.decodeResource(getResources(), R.drawable.logogg);
    final long[] patternVibrations = { 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,50 };

    final Notification.Builder notificationBuilder = new Notification.Builder(this)
        .setLargeIcon(mLogo)
        .setOngoing(true) //persistante. ne peut être supprimée par l'utilisateur
        .setSmallIcon(R.drawable.ic_launcher)
        .setContentTitle(getString(R.string.notification_service_titre))
        .setContentText(getString(R.string.notification_service_text))
        .setNumber(1)
        .setVibrate(patternVibrations);

    final Notification notification = notificationBuilder.getNotification();

    if (notification != null) {
        final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(0, notification);
    }
}
afficherNotification(startId);
final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(0);
afficherNotification(0);

Récepteurs d’évènements

Un récepteur d’évènements est un composant applicatif dont le rôle consiste uniquement à réagir à des évènements auxquels il est abonné (modèle de conception Observer). Exple: arrivée d’un SMS, batterie faible. Comme le service, il ne possède pas d’interface graphique. Lorsqu’il reçoit un évènement, s’il désire informer l’utilisateur, il doit le faire en utilisant la barre de notification ou en lançant une activité. L’exécution d’un récepteur d’évènements s’opère dans le thread principal du processus de l’application où il est défini. Il ne devrait pas bloquer le thread principal plus de dix secondes. Les évènements sont produits soit par le système, soit par des applications. Ils sont envoyés aux récepteurs d’évènements susceptibles de recevoir l’évènement donné. Les évènements sont des objets de type Intent décrivant une action qui vient d’être réalisée ou un évènement qui vient de se produire. L’envoi de l’intent se fait de manière asynchrone pour ne pas bloquer le composant émetteur de l’évènement.

public class RecepteurEvenements extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, R.string.reception_evenement, Toast.LENGTH_SHORT).show();
    }
}
final Intent intent = new Intent("votreEspaceDeNom.laboratoire4.EVENEMENT_1");
sendBroadcast(intent);
<receiver android:name=".RecepteurEvenements">
    <intent-filter>
        <action android:name=" votreEspaceDeNom.laboratoire4.EVENEMENT_1" />
    </intent-filter>
</receiver>

Remarque

Avant de recevoir son premier évènement, le composant est inactif. Il devient actif dès qu’il reçoit un évènement en paramètre de sa méthode onReceive() Dès la sortie de cette méthode, le composant redevient inactif. Attention au cas où le récepteur d’évènement lance un thread. Le système pourrait tuer le processus de l’application, donc du thread. Pour éviter ce problème, il est conseillé de lancer le thread depuis un service. Le système détectera le service comme actif et ne tuera donc pas le processus, sauf dans des cas extrêmes de besoin de ressources.