Dans ce tutoriel, nous allons explorer les Un service
composant et sa super-classe, le IntentService
. Vous apprendrez quand et comment utiliser ce composant pour créer d'excellentes solutions d'accès simultané pour des opérations en arrière-plan de longue durée. Nous examinerons également rapidement IPC (Inter Process Communication) pour apprendre à communiquer avec des services s'exécutant sur différents processus..
Pour suivre ce tutoriel, vous devez comprendre un peu la concurrence sur Android. Si vous ne connaissez pas grand chose à ce sujet, vous pouvez lire d’abord certains de nos autres articles sur le sujet..
le Un service
Le composant est une partie très importante du framework de concurrence d'accès d'Android. Il répond à la nécessité d'effectuer une opération de longue durée au sein d'une application ou fournit certaines fonctionnalités à d'autres applications. Dans ce tutoriel, nous allons nous concentrer exclusivement sur Un service
la capacité de tâche de longue durée de, et comment utiliser ce pouvoir pour améliorer la concurrence.
UNE Un service
est un composant simple qui est instancié par le système pour effectuer un travail de longue durée qui ne dépend pas nécessairement de l'interaction de l'utilisateur. Il peut être indépendant du cycle de vie de l'activité et peut également s'exécuter sur un processus complètement différent.
Avant de plonger dans une discussion sur ce qu’un Un service
représente, il est important de souligner que même si les services sont couramment utilisés pour des opérations d'arrière-plan de longue durée et pour exécuter des tâches sur différents processus, une Un service
ne représente pas Fil
ou un processus. Il ne s'exécutera que dans un thread en arrière-plan ou sur un processus différent s'il est explicitement demandé de le faire..
UNE Un service
a deux caractéristiques principales:
Il y a beaucoup de confusion à propos des services et des threads. Lorsqu'un Un service
est déclaré, il ne contient pas Fil
. En fait, par défaut, il s'exécute directement sur le thread principal et tout travail effectué sur celui-ci peut potentiellement geler une application. (Sauf si c'est un IntentService
, une Un service
sous-classe déjà fournie avec un thread de travail configuré.)
Alors, comment les services offrent-ils une solution de simultanéité? Bien, un Un service
ne contient pas de thread par défaut, mais il peut être facilement configuré pour fonctionner avec son propre thread ou avec un pool de threads. Nous verrons plus à ce sujet ci-dessous.
Sans tenir compte de l’absence de thread intégré, un Un service
est une excellente solution pour les problèmes de concurrence dans certaines situations. Les principales raisons de choisir un Un service
sur d'autres solutions de concurrence telles que AsyncTask
ou le cadre HaMeR sont:
Un service
peut être indépendant du cycle de vie de l'activité.Un service
convient aux longues opérations.Un service
peut être redémarré pour reprendre ses travaux.Il y a deux types de Un service
, commencé et lié.
UNE service commencé est lancé via Context.startService ()
. En règle générale, il exécute une seule opération. Il s'exécute indéfiniment jusqu'à la fin de l'opération, puis s'arrête. En règle générale, il ne renvoie aucun résultat à l'interface utilisateur.
le service lié est lancé via Context.bindService ()
, et il permet une communication à double sens entre le client et Un service
. Il peut également se connecter avec plusieurs clients. Il se détruit lorsqu'il n'y a pas de client connecté.
Pour choisir entre ces deux types, le Un service
doit implémenter des callbacks: onStartCommand ()
pour fonctionner comme un service démarré, et onBind ()
pour fonctionner en tant que service lié. UNE Un service
peut choisir de mettre en œuvre un seul de ces types, mais il peut également adopter les deux en même temps sans aucun problème.
Pour utiliser un service, étendez la Un service
classe et substituer ses méthodes de rappel, selon le type de Un service
. Comme mentionné précédemment, pour les services démarrés, le onStartCommand ()
méthode doit être mise en œuvre et pour les services liés, le onBind ()
méthode. En fait, le onBind ()
La méthode doit être déclarée pour l'un ou l'autre type de service, mais elle peut renvoyer null pour les services démarrés..
classe publique CustomService étend le service @Override public int onStartCommand (intention, int flags, int startId) // Exécuter vos opérations // le service ne sera pas terminé automatiquement retournera Service.START_NOT_STICKY; @Nullable @Override public IBinder onBind (Intentive) // Crée une connexion avec un client // à l'aide d'une interface implémentée sur IBinder return null;
onStartCommand ()
: lancé par Context.startService ()
. Ceci est généralement appelé à partir d'une activité. Une fois appelé, le service peut s'exécuter indéfiniment et c'est à vous de l'arrêter, en appelant stopSelf ()
ou aire d'autoroute()
.onBind ()
: appelé quand un composant veut se connecter au service. Appelé sur le système par Context.bindService ()
. Il retourne un IBinder
qui fournit une interface pour communiquer avec le client.Le cycle de vie du service est également important à prendre en compte. le onCreate ()
et onDestroy ()
des méthodes doivent être mises en œuvre pour initialiser et arrêter toutes les ressources ou opérations du service.
le Un service
composant doit être déclaré sur le manifeste avec le
élément. Dans cette déclaration, il est également possible, mais non obligatoire, de définir un processus différent pour Un service
courir dans.
… …
Pour lancer un service démarré, vous devez appeler Context.startService ()
méthode. le Intention
doit être créé avec le Le contexte
et le Un service
classe. Toute information ou donnée pertinente doit également être transmise dans cette Intention
.
Intention serviceIntent = new Intent (this, CustomService.class); // Transmission des données à traiter sur le paquet de services data = new Bundle (); data.putInt ("OperationType", 99); data.putString ("DownloadURL", "http://mydownloadurl.com"); serviceIntent.putExtras (data); // Démarrage du service startService (serviceIntent);
Dans ton Un service
classe, la méthode qui devrait vous préoccuper est la onStartCommand ()
. C'est sur cette méthode que vous devez appeler toute opération que vous souhaitez exécuter sur le service démarré. Vous allez traiter le Intention
capturer les informations envoyées par le client. le startId
représente un identifiant unique, créé automatiquement pour cette requête spécifique et le drapeaux
peut également contenir des informations supplémentaires à ce sujet.
@Override public int onStartCommand (Intention, Int, int flags, int startId) Bundle data = intent.getExtras (); if (data! = null) int operation = data.getInt (KEY_OPERATION); // Vérifie quelle opération effectuer et envoie un message si (opération == OP_DOWNLOAD) // effectue un téléchargement return START_STICKY;
le onStartCommand ()
renvoie une constante int
qui contrôle le comportement:
Service.START_STICKY
: Le service est redémarré s'il est terminé.Service.START_NOT_STICKY
: Le service n'est pas redémarré.Service.START_REDELIVER_INTENT
: Le service est redémarré après un crash et les intentions, puis le traitement sera redélivré.Comme mentionné précédemment, un service démarré doit être arrêté, sinon il fonctionnera indéfiniment. Cela peut être fait soit par le Un service
appel stopSelf ()
sur lui-même ou par un appel client aire d'autoroute()
dessus.
void someOperation () // effectue une opération longue // et arrête le service à la fin stopSelf ();
Les composants peuvent créer des connexions avec des services, établissant une communication bidirectionnelle avec eux. Le client doit appeler Context.bindService ()
, en passant un Intention
, une ServiceConnection
interface et un drapeau
comme paramètres. UNE Un service
peut être lié à plusieurs clients et il sera détruit s'il n'a plus de clients connectés.
void bindWithService () Intentity Intent = new Intent (this, PlayerService.class); // liaison avec le service bindService (intent, mConnection, Context.BIND_AUTO_CREATE);
Il est possible d'envoyer Message
objets aux services. Pour ce faire, vous devrez créer un Messager
côté client dans un ServiceConnection.onServiceConnected
mise en œuvre de l'interface et l'utiliser pour envoyer Message
objets à la Un service
.
private ServiceConnection mConnection = new ServiceConnection () @Override public void onServiceConnected (ComponentName nomClasse, service IBinder) // utilise l'IBinder reçu pour créer un messager mServiceMessenger = new Messenger (service); mBound = true; @Override public void onServiceDisconnected (ComponentName arg0) mBound = false; mServiceMessenger = null; ;
Il est également possible de passer une réponse Messager
au Un service
pour que le client reçoive des messages. Attention cependant, car le client peut ne plus être là pour recevoir le message du service. Vous pouvez aussi utiliser BroadcastReceiver
ou toute autre solution de diffusion.
gestionnaire privé mResponseHandler = nouveau gestionnaire () @Override public void handleMessage (Message msg) // gestion de la réponse de Service; Message msgReply = Message.obtain (); msgReply.replyTo = new Messenger (mResponseHandler); try mServiceMessenger.send (msgReply); catch (RemoteException e) e.printStackTrace ();
Il est important de dissocier le service lorsque le client est détruit..
@Override protected void onDestroy () super.onDestroy (); // se déconnecter du service if (mBound) unbindService (mConnection); mBound = false;
Sur le Un service
côté, vous devez implémenter le Service.onBind ()
méthode, fournissant un IBinder
fourni à partir d'un Messager
. Cela va relayer une réponse Gestionnaire
pour gérer le Message
objets reçus du client.
IncomingHandler (PlayerService playerService) mPlayerService = new WeakReference <> (playerService); @Override public void handleMessage (Message msg) // handle messages public IBinder onBind (Intentive) // passe un classeur à l'aide du messager créé. Return mMessenger.getBinder (); final Messenger mMessenger = new Messenger (nouveau IncomingHandler (this));
Enfin, il est temps de parler de la façon de résoudre les problèmes de simultanéité à l'aide de services. Comme mentionné précédemment, une norme Un service
ne contient pas de fils supplémentaires et il fonctionnera sur le principal Fil
par défaut. Pour surmonter ce problème, vous devez ajouter un ouvrier Fil
, un pool de threads ou exécuter le Un service
sur un processus différent. Vous pouvez également utiliser une sous-classe de Un service
appelé IntentService
qui contient déjà un Fil
.
Pour faire le Un service
exécuter sur un fond Fil
vous pouvez simplement créer un extra Fil
et exécuter le travail là-bas. Cependant, Android nous offre une meilleure solution. Une des façons de tirer le meilleur parti du système consiste à implémenter le cadre HaMeR dans le Un service
, par exemple en bouclant un Fil
avec une file de messages pouvant traiter des messages indéfiniment.
Il est important de comprendre que cette implémentation traitera les tâches de manière séquentielle. Si vous devez recevoir et traiter plusieurs tâches en même temps, vous devez utiliser un pool de threads. L'utilisation de pools de threads n'entre pas dans le cadre de ce didacticiel et nous n'en parlerons pas aujourd'hui.
Pour utiliser HaMeR, vous devez fournir le Un service
avec un Looper
, une Gestionnaire
et un HandlerThread
.
Looper privé mServiceLooper; serviceHandler privé mServiceHandler; // Gestionnaire pour recevoir des messages de la classe finale privée du client ServiceHandler extend Handler ServiceHandler (Looper Looper) super (Looper); @Override public void handleMessage (Message msg) super.handleMessage (msg); // gérer les messages // arrêter le service à l'aide de startId stopSelf (msg.arg1); @Override public void onCreate () HandlerThread thread = new HandlerThread ("ServiceThread", Process.THREAD_PRIORITY_BACKGROUND); thread.start (); mServiceLooper = thread.getLooper (); mServiceHandler = new ServiceHandler (mServiceLooper);
Si le cadre HaMeR vous est inconnu, lisez nos tutoriels sur l'accès simultané à HaMer pour Android.
S'il n'y a pas besoin de Un service
pour rester en vie pendant longtemps, vous pouvez utiliser IntentService
, une Un service
sous-classe prête à exécuter des tâches sur des threads d'arrière-plan. Intérieurement, IntentService
est un Un service
avec une implémentation très similaire à celle proposée ci-dessus.
Pour utiliser cette classe, tout ce que vous avez à faire est de l’étendre et de mettre en œuvre la onHandleIntent ()
, une méthode hook qui sera appelée chaque fois qu'un client appelle startService ()
sur ce Un service
. Il est important de garder à l'esprit que le IntentService
s'arrêtera dès que son travail sera terminé.
Classe publique MyIntentService étend IntentService public MyIntentService () super ("MyIntentService"); @Override protected void onHandleIntent (Intention Intention) // gérer les intentions envoyées par startService
UNE Un service
peut fonctionner sur un tout autre Processus
, indépendamment de toutes les tâches qui se produisent sur le processus principal. Un processus a sa propre allocation de mémoire, son groupe de threads et ses priorités de traitement. Cette approche peut être très utile lorsque vous devez travailler indépendamment du processus principal..
La communication entre différents processus s'appelle IPC (Inter Process Communication). Dans un Un service
il y a deux façons principales de faire IPC: utiliser un Messager
ou mettre en œuvre un AIDL
interface.
Nous avons appris à envoyer et recevoir des messages entre services. Tout ce que vous avez à faire est d’utiliser créer un Messager
en utilisant le IBinder
instance reçue pendant le processus de connexion et l'utiliser pour envoyer une réponse Messager
Retour à la Un service
.
gestionnaire privé mResponseHandler = nouveau gestionnaire () @Override public void handleMessage (Message msg) // gestion de la réponse de Service; private ServiceConnection mConnection = new ServiceConnection () @Override public void onServiceConnected (ComponentName nomClasse, service IBinder) // utilise l'IBinder reçu pour créer un messager mServiceMessenger = new Messenger (service); Message msgReply = Message.obtain (); msgReply.replyTo = new Messenger (mResponseHandler); try mServiceMessenger.send (msgReply); catch (RemoteException e) e.printStackTrace ();
le AIDL
L’interface est une solution très puissante qui permet d’appeler directement Un service
méthodes en cours d'exécution sur différents processus et il est approprié d'utiliser lorsque votre Un service
est vraiment complexe. toutefois, AIDL
est compliqué à mettre en œuvre et est rarement utilisé, son utilisation ne sera donc pas abordée dans ce tutoriel..
Les services peuvent être simples ou complexes. Cela dépend des besoins de votre application. J'ai essayé de couvrir autant de terrain que possible sur ce tutoriel, mais je me suis concentré uniquement sur l'utilisation de services à des fins de concurrence et il y a plus de possibilités pour ce composant. Si vous souhaitez en savoir plus, consultez la documentation et les guides Android.
À bientôt!