Connexion à un service
-
Un composant appelle Context.bindService(Intent service, ServiceConnection conn, int flags) pour se lier à un service
-
ServiceConnection est un listener avec les méthodes :
- onServiceConnected(ComponentName name, IBinder service) : appelé lors de la connexion au service
- onServiceDisconnected(ComponentName name) : appelé lorsque la connexion est perdue
-
flags (combinaison avec ou logique de constantes) :
- BIND_AUTO_CREATE (démarre le service automatiquement)
- BIND_NOT_FOREGROUND (n'accorde pas une priorité de 1er plan au service)
- BIND_ABOVE_CLIENT (priorité service > priorité client)
- BIND_WAIVE_PRIORITY (pas d'impact du contexte sur la priorité)
-
ServiceConnection est un listener avec les méthodes :
- La méthode onBind() du service est appelée : elle doit être redéfinie pour retourner un IBinder exposant les méthodes publiques.
- On utilise la méthode Context.unbindService(ServiceConnection conn) avec le ServiceConnection précédemment utilisé pour se déconnecter du service ; la déconnexion a également lieu lors de l'arrêt du composant appelant
- Lors de la déconnexion du service, sa méthode onUnbind() est appelée ; si cette méthode retourne true, une prochaine connexion appelera onRebind() et non onBind().
Cycle de vie d'un service
- Le service peut être tué par le système pour pénurie de ressources.
- Sa susceptibilité d'être tué dépend de sa priorité.
-
Par défaut un service hérite de la plus forte priorité des contextes qui sont connectés
- Il est possible de changer le comportement par défaut (drapeaux lors de bindService(), passage d'une activité en 1er plan avec notification
-
Les méthodes du service doivent avoir une exécution rapide ; pour les longs travaux, on utilise des threads séparées
- Il faut interrompre les threads de travail dans onDestroy()

AIDL
Utilisation de AIDL
- Android Interface Definition Language (AIDL) = langage de définition d'interface (IDL) pour évocation distante de méthodes (RPC)
-
Procédé
- Définition des méthodes d'un service à exposer dans un fichier AIDL
- Génération d'un proxy (client) pour l'appel des méthodes et d'un stub (serveur) pour l'implantation d'un service avec méthodes appelables
- A l'exécution, le proxy envoie un message de son binder vers celui du service ; le service lui répond ; les messages sont constitués de types primitifs sérialisés dans des paquets (Parcel).
Langage AIDL
-
Une interface AIDL ressemble à une interface Java à quelques exceptions près :
- seulement des méthodes, pas de champ statique, pas de modificateurs
- les types primitifs (ainsi que String, CharSequence, List<T> implanté comme une ArrayList<T>, Map non générique comme une HashMap) sont utilisables sans précaution particulière
- pas de support des exceptions
- pas de support d'héritage d'interface
- les classes Java utilisées comme type de retour ou arguments doivent implanter l'interface Parcelable ; les types doivent être préfixés par in, out ou inout selon la directionnalité de la communication des objets
- Une interface AIDL est compilable en classe Java fournissant une souche (Stub), i.e. une classe abstraite Java offrant un serveur RPC avec méthodes à définir ; il faut redéfinir cette classe et la retourner par la méthode onBind() du service.
Parcelable
- Parcelable : pour la sérialisation/désérialisation de classes Java par le mécanisme d'IPC
-
Une classe T implantant Parcelable doit :
- Implanter une méthode void writeToParcel(Parcel out) pour sérialiser ses données dans un Parcel
- Implanter une méthode int describeContents() retournant 0
-
Avoir un champ static final CREATOR de type Parcelable.Creator<T> avec les méthodes implantées :
- T createFromParcel(Parcel source) pour désérialiser une instance depuis un Parcel
- T[] newArray(int size) : pour créer un nouveau tableau de références nulles de taille size de type TInvocation de méthodes distances sur un service Android
- Un en-tête dans un fichier .aidl doit être présent pour chaque classe complexe référencée dans une interface AIDL
Une bibliothèque matricielle
Classe Matrix serialisable dans un Parcel
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License package fr.upem.matrixlibrary; import java.util.Arrays; import android.os.Parcel; import android.os.Parcelable; /* A matrix of integers */ public class Matrix implements Parcelable { private final int rows, cols; private final int[][] content; public Matrix(int rows, int cols) { this.rows = rows; this.cols = cols; this.content = new int[rows][]; // Initialize the matrix for (int i = 0; i < rows; i++) this.content[i] = new int[cols]; } public static Matrix createIdentity(int n) { Matrix m = new Matrix(n, n); for (int i = 0; i < n; i++) m.set(i, i, 1); return m; } public int getRows() { return rows; } public int getCols() { return cols; } public int get(int i, int j) { return content[i][j]; } public void set(int i, int j, int value) { content[i][j] = value; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(rows); dest.writeInt(cols); // Write the matrix row by row for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) dest.writeInt(content[i][j]); } public static final Creator<Matrix> CREATOR = new Creator<Matrix>() { @Override public Matrix createFromParcel(Parcel source) { int rows = source.readInt(), cols = source.readInt(); Matrix m = new Matrix(rows, cols); for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) m.set(i, j, source.readInt()); return m; } @Override public Matrix[] newArray(int size) { return new Matrix[size]; } }; @Override public String toString() { return Arrays.deepToString(content); } }
En-tête AIDL pour la classe Matrix
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License package fr.upem.matrixlibrary; import java.util.Arrays; import android.os.Parcel; import android.os.Parcelable; /* A matrix of integers */ public class Matrix implements Parcelable { private final int rows, cols; private final int[][] content; public Matrix(int rows, int cols) { this.rows = rows; this.cols = cols; this.content = new int[rows][]; // Initialize the matrix for (int i = 0; i < rows; i++) this.content[i] = new int[cols]; } public static Matrix createIdentity(int n) { Matrix m = new Matrix(n, n); for (int i = 0; i < n; i++) m.set(i, i, 1); return m; } public int getRows() { return rows; } public int getCols() { return cols; } public int get(int i, int j) { return content[i][j]; } public void set(int i, int j, int value) { content[i][j] = value; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(rows); dest.writeInt(cols); // Write the matrix row by row for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) dest.writeInt(content[i][j]); } public static final Creator<Matrix> CREATOR = new Creator<Matrix>() { @Override public Matrix createFromParcel(Parcel source) { int rows = source.readInt(), cols = source.readInt(); Matrix m = new Matrix(rows, cols); for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) m.set(i, j, source.readInt()); return m; } @Override public Matrix[] newArray(int size) { return new Matrix[size]; } }; @Override public String toString() { return Arrays.deepToString(content); } }
Définition AIDL pour un service de multiplication matricielle
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License package fr.upem.matrixlibrary; import java.util.Arrays; import android.os.Parcel; import android.os.Parcelable; /* A matrix of integers */ public class Matrix implements Parcelable { private final int rows, cols; private final int[][] content; public Matrix(int rows, int cols) { this.rows = rows; this.cols = cols; this.content = new int[rows][]; // Initialize the matrix for (int i = 0; i < rows; i++) this.content[i] = new int[cols]; } public static Matrix createIdentity(int n) { Matrix m = new Matrix(n, n); for (int i = 0; i < n; i++) m.set(i, i, 1); return m; } public int getRows() { return rows; } public int getCols() { return cols; } public int get(int i, int j) { return content[i][j]; } public void set(int i, int j, int value) { content[i][j] = value; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(rows); dest.writeInt(cols); // Write the matrix row by row for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) dest.writeInt(content[i][j]); } public static final Creator<Matrix> CREATOR = new Creator<Matrix>() { @Override public Matrix createFromParcel(Parcel source) { int rows = source.readInt(), cols = source.readInt(); Matrix m = new Matrix(rows, cols); for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) m.set(i, j, source.readInt()); return m; } @Override public Matrix[] newArray(int size) { return new Matrix[size]; } }; @Override public String toString() { return Arrays.deepToString(content); } }
Implantation du service
On implante la souche de IMatrixMultiplier. La souche a été auto-générée depuis l'interface AIDL.
// Code sample from Coursand [http://igm.univ-mlv.fr/~chilowi/], under the Apache 2.0 License package fr.upem.matriservice; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import fr.upem.matrixlibrary.IMatrixMultiplier; import fr.upem.matrixlibrary.Matrix; /** A service providing multiplications on matrices */ public class MatrixMultiplierService extends Service { private final IBinder multiplier = new IMatrixMultiplier.Stub() { @Override public Matrix pow(Matrix m, int power) throws RemoteException { if (power == 0) return Matrix.createIdentity(m.getRows()); if (power % 2 == 0) { Matrix m2 = pow(m, power / 2); return multiply(m2, m2); } else { // power % 2 == 1 return multiply(m, pow(m, power-1)); } } @Override public Matrix multiply(Matrix m1, Matrix m2) throws RemoteException { if (m1.getRows() != m2.getCols()) throw new IndexOutOfBoundsException("Dimension of matrices not compatible"); Matrix r = new Matrix(m1.getRows(), m2.getCols()); for (int i = 0; i < r.getRows(); i++) for (int j = 0; j < r.getCols(); j++) { int v = 0; for (int k = 0; k < m1.getCols(); k++) v += m1.get(i, k) * m2.get(k, j); r.set(i, j, v); } return r; } }; @Override public IBinder onBind(Intent arg0) { return multiplier; } }
Connexion au service
ServiceConnection conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { Toast.makeText(MatrixMultiplier.this, "Disconnected", Toast.LENGTH_LONG).show(); } @Override public void onServiceConnected(ComponentName name, IBinder service) { Toast.makeText(MyActivity.this, "Connected", Toast.LENGTH_LONG).show(); IMatrixMultiplier matmul = IMatrixMultiplier.Stub.asInterface(service) ; Matrix m = new Matrix(2,2); m.set(0,0, 1); m.set(0,1, 2); m.set(1,0, 3); m.set(1,1, 4); Matrix r = null; try { r = matmul.multiply(m, m); } catch (RemoteException e) { e.printStackTrace(); } if (r != null) t.setText(r.toString()); } }; boolean connected = bindService(new Intent().setClassName("fr.upemlv.matriservice", "fr.upemlv.matriservice.MatrixMultiplierService"), conn, Context.BIND_AUTO_CREATE);
Service local
Inutile de mettre en œuvre une interface AIDL si le service n'est utilisé que localement dans l'application.
public class LocalService extends Service { public class LocalBinder extends Binder { LocalService getService() { return LocalService.this ; } } private final IBinder binder = new LocalBinder() ; @Override public IBinder onBind(Intent intent) { return binder; } ... }
On peut ensuite récupérer le LocalBinder dès la connexion au service et récupérer avec la méthode getService() l'instance du LocalService (et ainsi potentiellement manipuler tous ses champs et méthodes publiques).