I. Description▲
Tout d'abord les IntentService sont un type de service Android qui permet de gérer les requêtes asynchrones. Ici, le mot requête doit être considéré comme une action ou une demande de traitement.
Ces requêtes sont exécutées dans un « worker », par conséquent les traitements sont exécutés les uns après les autres au sein de ce thread (qui n'est pas le Thread U.I.). Les IntentService vous permettent de décharger le Thread UI de traitements lourds tout en ayant les avantages des services qui sont indépendants du cycle de vie des activités, ou bien lancer ces traitements qui pourront être exécutés dans un autre processus (c'est la notion de RemoteService).
Les IntentService s'arrêtent d'eux-mêmes à la fin des traitements demandés. Pour rajouter un nouveau traitement, il vous suffit de le rajouter dans la liste d'attente de l'IntentService.
Par contre pour récupérer les résultats du traitement, il vous faudra transférer le résultat par message (intent, broadcast, handler, etc.) au(x) destinataire(s) voulu(s). C'est l'inconvénient de l'IntentService qui n'a pas accès directement au Thread UI, contrairement aux AsyncTask.
Si nous résumons rapidement, les IntentService sont utiles pour gérer des requêtes multiples de manière asynchrone (bien que leur exécution soit synchrone dans le worker). Ainsi, nous avons l'avantage du cycle de vie des Services, de l'arrêt automatique de celui-ci quand l'action est terminée et de l'exécution du traitement en tâche de fond dans un seul concept. L'inconvénient est l'absence d'accès direct à l'interface graphique.
II. Implémentation d'un IntentService▲
Prenons l'exemple d'implémentation fourni par la documentation de Google :
public
class
HelloIntentService extends
IntentService {
/**
*
Un
constructeur
est
requis
,
et
doit
appeler
la
méthode
*
superIntentService
(
String
)
*
constructeur
avec
un
nom
pour
le
«
worker
thread
»
*/
public
HelloIntentService
(
) {
super
(
"
HelloIntentService
"
);
}
/**
*
L
'
IntentService
appelle
la
méthode
par
défaut
du
«
worker
thread
»
avec
*
l
'
intent
passé
en
paramètre
de
la
méthode
.
Quand
cette
méthode
est
terminée
*
le
service
s
'
arrête
de
lui
-
même
*/
@
Override
protected
void
onHandleIntent
(
Intent intent) {
//
Normalement
le
traitement
lourd
se
fait
ici,
comme
télécharger
un
fichier
//
À
ne
pas
faire
mais
dans
cet
exemple
une
attente
de
5
secondes
est
//
faite,
ceci
ne
bloque
pas
le
« thread
UI »
puisque
nous
sommes
dans
le
//
« worker
thread »
long
endTime =
System.currentTimeMillis
(
) +
5
*
1000
;
while
(
System.currentTimeMillis
(
) <
endTime) {
synchronized
(
this
) {
try
{
wait
(
endTime -
System.currentTimeMillis
(
));
}
catch
(
Exception e) {
}
}
}
}
}
On a un peu de mal à savoir comment le Service fonctionne ainsi. Où se trouve notre file d'attente d'intention ? Comment le « worker thread » est lancé ?
Regardons alors directement dans le code des IntentService :
private
final
class
ServiceHandler extends
Handler {
public
ServiceHandler
(
Looper looper) {
super
(
looper) ;
}
@
Override
public
void
handleMessage
(
Message msg) {
onHandleIntent
(
(
Intent)msg.obj) ;
stopSelf
(
) ; //
arrêt
du
service
}
}
Premièrement, une classe interne « ServiceHandler » est créée avec comme paramètre de son constructeur un Looper (qui est une gestion de queue de message). Cette classe hérite d'un Handler qui permet de recevoir des messages et les traiter. Lors de la réception d'un de ces messages (dans la fonction handleMessage), nous pouvons voir que la fonction onHandleIntent est invoquée, le traitement se fera au sein de cette méthode. L'appel à la fonction stopSelf arrête ensuite le service (StopSelf est une fonction de la classe Service Android).
On comprend comment le Service s'arrête de lui-même. Mais qui lui passe ce Looper et comment gère-t-il sa file d'attente ? Regardons dans la fonction onCreate de la classe IntentService.
public
abstract
class
IntentService extends
Service {
//
blabla
@
Override
public
void
onCreate
(
) {
super
.onCreate
(
);
HandlerThread thread =
new
HandlerThread
(
HandlerThread
(
"
IntentService[
"
+
mName +
"
]
"
);
thread.start
(
); //
notre
« worker
thread »
mServiceLooper =
thread.getLooper
(
);//
son
Looper
mServiceHandler =
new
ServiceHandler
(
mServiceLooper);//
passage
du
Looper
au
ServiceHandler
}
}
Un nouveau thread est créé à partir de la classe HandlerThread et son Looper est passé à la classe ServiceHandler que nous venons de voir. Nous avons trouvé notre « worker thread ».
Depuis la fonction onStart, le service gère la file d'attente via les intentions par l'envoi de messages à notre « worker thread ».
public
abstract
class
IntentService extends
Service {
//
blabla
@
Override
public
void
onStart
(
Intent intent, int
startId) {
Message msg =
mServiceHandler.obtainMessage
(
);//
Créer
un
message
prérempli
msg.arg1 =
startId;//
affecte
un
identifiant
au
message
(utilisé
pour
arrêter
ou
pas
le
service)
msg.obj =
intent;//
l'action
associée
au
message
mServiceHandler.sendMessage
(
msg); //
ajoute
le
message
dans
la
file
d'attente
du
Looper
}
//
Start
Service
@
Override
public
int
onStartCommand
(
Intent intent, int
flags, int
startId) {
onStart
(
intent, startId);
return
mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
}
La méthode onStart est appelée depuis la méthode onStartCommand. Cette fonction est invoquée automatiquement à chaque appel lors du démarrage d'un service par la méthode startService (cf. le cycle de vie des Services Android). Par conséquent, pour ajouter une nouvelle demande, il vous suffit d'appeler la fonction startService appartenant à la classe Context en lui passant l'Intent qui porte les paramètres du traitement.
L'envoi du message au ServiceHandler est effectué au sein de la méthode onStart de l'IntentService par l'appel à la fonction sendMessage. Dans ce message nous trouvons l'Intent passé en paramètre et le startId, qui identifiera ce service lors de l'appel du stopSelf.
Une option permet de redémarrer le service lorsque celui-ci a été détruit par le système pour des questions de place mémoire. Ce redémarrage (cette restitution de l'Intent d'où la notion de Redelivery) renverra seulement l'Intent le plus récent. Il suffit d'appeler la fonction setIntentRedelivery(true) dans le onCreate ou le constructeur de l'IntentService. Cette option peut être utile si vous avez une seule action à réaliser. Il est tout de même plus pertinent de connaître l'état des actions non effectuées pour pouvoir les relancer, plutôt que de s'appuyer sur ce flag.
Dans la fonction onDestroy, on nettoie le Looper ce qui permet de vider et d'arrêter la queue de messages en cours pour terminer le thread proprement.
public
abstract
class
IntentService extends
Service {
//
blabla
@
Override
public
void
onDestroy
(
) {
mServiceLooper.quit
(
); //
nettoie
la
queue
de
messages
et
stoppe
le
Thread
}
}
Pour résumer, les IntentService fournissent :
- un « worker thread » qui exécute tous les Intent passés à la fonction onStartCommand (via la méthode Context.StartService(Intent intent)) ;
- une file d'attente sur les Intent passés, qui est gérée par la fonction sendMessage (du ServiceHandler) ;
- le traitement des Intent qui se fait depuis la fonction onHandleIntent de votre classe qui hérite d'IntentService. Cette méthode s'exécute dans le « worker thread », pas de souci de multithreading (synchronised et autre mutex) ;
- l'arrêt du service quand le traitement est terminé ;
- le retour de la valeur null sur la fonction onBind par défaut (par défaut on n'a pas besoin de s'abonner au Service).
Nous avons vu comment les IntentService fonctionnent, mais pourrait-on avoir un exemple concret ?
III. Exemple d'un IntentService▲
Essayons de mettre en place un IntentService permettant de pouvoir récupérer le contenu d'une page web (code source) et l'afficher depuis une TextView prévue à cet effet.
Pour la communication entre votre IntentService et l'activité nous passerons par un BroadcastReceiver. Ce principe est la manière naturelle sous Android pour effectuer ce dialogue et permettra de s'adapter aux envois multiples.
Voici l'IntentService :
package
com.android2ee.tutorial.intentservice.;
import
java.io.BufferedReader;
import
java.io.IOException;
import
java.io.InputStream;
import
java.io.InputStreamReader;
import
java.net.URLConnection;
import
java.net.URL;
import
com.android2ee.tutorial.intentservice.MainActivity.MyReceiver;
import
android.app.IntentService;
import
android.content.Intent;
public
class
DownloadSourceService extends
IntentService {
public
static
final
String URL =
"
urlpath
"
;
public
static
final
String SOURCE_URL =
"
destination_source
"
;
public
DownloadSourceService
(
) {
super
(
"
DownloadService
"
);
}
@
Override
protected
void
onHandleIntent
(
Intent intent) {
String urlPath =
intent.getStringExtra
(
URL);
InputStream is =
null
;
BufferedReader r =
null
;
StringBuilder result =
null
;
//
on
récupère
les
données
depuis
l'url
try
{
URL aURL =
new
URL
(
urlPath);
URLConnection conn =
aURL.openConnection
(
);
conn.connect
(
);
is =
conn.getInputStream
(
);
r =
new
BufferedReader
(
new
InputStreamReader
(
is));
result =
new
StringBuilder
(
);
String line;
while
(
(
line =
r.readLine
(
)) !
=
null
) {
result.append
(
line);
}
}
catch
(
IOException e) {
//
message
d'erreur
}
finally
{
//
on
ferme
bien
tous
les
flux
if
(
r !
=
null
) {
try
{
r.close
(
);
}
catch
(
IOException e) {
//
message
d'erreur
}
}
}
//
maintenant
on
transmet
le
résultat
//
on
pourrait
avoir
un
Handler,
BroadCast,
Notification,
etc.
Intent broadcastIntent =
new
Intent
(
);
broadcastIntent.setAction
(
MyReceiver.ACTION_RESP);
broadcastIntent.addCategory
(
Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra
(
SOURCE_URL, result.toString
(
));
sendBroadcast
(
broadcastIntent);
}
}
Finalement, l'implémentation est très simple. Il suffit de développer son traitement depuis la fonction onHandleIntent et ensuite de choisir une méthode de diffusion du résultat à la fin de celle-ci.
Il existe plusieurs méthodes de diffusion, parmi celles-ci vous pouvez avoir :
- un broadcast : permet d'avoir une diffusion au sens large ;
- un handler : permet d'avoir une diffusion vers un correspondant unique ;
- une notification : permet d'avoir une diffusion vers le centre de notification du téléphone ;
- un intent : permet de lancer une action en fin de traitement ;
- un listener : permet d'avoir une diffusion sur l'ensemble des personnes abonnées ;
- etc.
Voici l'Activité qui lancera le service, récupérera le message répondu par celui-ci et l'affichera sur sa vue.
package
com.android2ee.tutorial.intentservice.;
import
android.os.Bundle;
import
android.app.Activity;
import
android.content.BroadcastReceiver;
import
android.content.Context;
import
android.content.Intent;
import
android.content.IntentFilter;
import
android.widget.TextView;
public
class
IntentServiceActivity extends
Activity {
public
class
MyReceiver extends
BroadcastReceiver {
public
static
final
String ACTION_RESP =
"
com.myapp.intent.action.TEXT_TO_DISPLAY
"
;
@
Override
public
void
onReceive
(
Context context, Intent intent) {
String text =
intent.getStringExtra
(
DownloadSourceService.SOURCE_URL);
//
send
text
to
display
TextView result =
(
TextView) findViewById
(
R.id.text_result);
result.setText
(
text);
}
}
private
MyReceiver receiver;
@
Override
public
void
onCreate
(
Bundle savedInstanceState) {
super
.onCreate
(
savedInstanceState);
setContentView
(
R.layout.activity_main);
//
on
initialise
notre
broadcast
receiver =
new
MyReceiver
(
);
//
on
lance
le
service
Intent msgIntent =
new
Intent
(
this
, DownloadSourceService.class
);
msgIntent.putExtra
(
DownloadSourceService.URL,"
http://api.openweathermap.org/data/2.5/weather?q=London&mode=xml
"
);
startService
(
msgIntent);
}
@
Override
protected
void
onResume
(
) {
super
.onResume
(
);
//
on
déclare
notre
Broadcast
Receiver
IntentFilter filter =
new
IntentFilter
(
MyReceiver.ACTION_RESP);
filter.addCategory
(
Intent.CATEGORY_DEFAULT);
registerReceiver
(
receiver, filter);
}
@
Override
protected
void
onPause
(
) {
super
.onPause (
);
//
on
désenregistre
notre
broadcast
unregisterReceiver
(
receiver);
}
Ne pas oublier de déclarer l'IntentService dans le manifest comme un Service classique.
<?
xml
version="1.0"
encoding="utf-8"?
>
<
manifest
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
package
=
"
com.android2ee.tutorial.intentservice.
"
android
:
versionCode
=
"
1
"
android
:
versionName
=
"
1.0
"
>
<
uses-sdk
android
:
minSdkVersion
=
"
8
"
android
:
targetSdkVersion
=
"
19
"
/
>
<!--
Internet
permission,
as
we
are
accessing
to
url
-->
<
uses-permission
android
:
name
=
"
android.permission.INTERNET
"
/
>
<
application
android
:
allowBackup
=
"
true
"
android
:
icon
=
"
@drawable/ic_launcher
"
android
:
label
=
"
@string/app_name
"
android
:
theme
=
"
@style/AppTheme
"
>
<
activity
android
:
name
=
"
com.android2ee.tutorial.intentservice.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
>
<!--
Declaring
Service
in
Manifest
-->
<
service
android
:
name
=
"
.DownloadSourceService
"
android
:
exported
=
"
false
"
/
>
<
/
application
>
<
/
manifest
>
Enfin le fichier activity_main qui reste très simple :
<
RelativeLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
xmlns
:
tools
=
"
http://schemas.android.com/tools
"
android
:
layout_width
=
"
match_parent
"
android
:
layout_height
=
"
match_parent
"
android
:
paddingBottom
=
"
@dimen/activity_vertical_margin
"
android
:
paddingLeft
=
"
@dimen/activity_horizontal_margin
"
android
:
paddingRight
=
"
@dimen/activity_horizontal_margin
"
android
:
paddingTop
=
"
@dimen/activity_vertical_margin
"
tools
:
context
=
"
.MainActivity
"
>
<
TextView
android
:
id
=
"
@+id/text_result
"
android
:
layout_width
=
"
wrap_content
"
android
:
layout_height
=
"
wrap_content
"
/
>
<
/
RelativeLayout
>
IV. Conclusion▲
Les IntentService sont intéressants lorsque vous avez une tâche de fond régulière au sein de la vie de votre application. Par exemple :
- flux RSS ;
- mise à jour de la base de données ;
- téléchargement d'images à partir d'un ensemble d'URL ;
- etc.
Avec les IntentService, le développement de ces tâches sera simple comparé aux autres moyens que nous avons, dû à la simplicité de ce pattern. Cette méthode vous donne les avantages des services Android qui peuvent être disponibles depuis votre application (ou depuis les autres applications, si celui-ci a été exporté depuis le manifest).
Utilisez réellement les IntentService quand vous avez des requêtes multiples, ceux-ci vous simplifieront vraiment la tâche sur le cycle de vie de ce traitement ainsi que sur la gestion de sa file d'attente. Vous pouvez passer des paramètres par les Intent pour différencier le fonctionnement à réaliser du traitement en tâche de fond.
Mais surtout, ce DesignPattern est excellent pour mettre en place une architecture simple qui vous permet d'enchaîner des traitements dans un autre Thread que le ThreadUI. Cela vous permet d'éviter de mettre en place une architecture complexe (un PoolExecutor, des Services, des Threads de traitements) pour un traitement qui ne l'est pas. À noter que si vous effectuez une application complexe, vous ne pourrez pas vous appuyer exclusivement sur ce pattern et vous devrez mettre en place une véritable architecture robuste.
V. Approfondissons le sujet▲
Et si nous prenions du temps à regarder réellement l'enchaînement des actions de bas niveau, pour comprendre au mieux ce pattern ?
Voici le diagramme de séquence que l'on obtiendrait :
Le fonctionnement de l'IntentService est basé sur l'envoi et la réception de message en nous permettant d'avoir un traitement asynchrone. Tout traitement de fond se réalise dans le « worker thread » (HandlerThread), donc dans le même thread. Par conséquent nous aurons une exécution séquentielle des traitements.
Pour comprendre le fonctionnement, voici un schéma sur les traitements par Thread :
Note : le ThreadService est le Thread UI pour ceux qui ne le savent pas !
Le traitement est réalisé depuis le « worker thread », la gestion asynchrone des messages est réalisée par le biais de la « message queue » et du Looper qui ira lire périodiquement les messages présents dans la « message queue ».
Le service ne s'arrêtera de lui-même que si aucune intention n'est en cours (startId).
VI. Android2EE: La Formation et l'Expertise Android à votre service▲
Android2EE
|
|
|
VII. Récupération des tutoriels▲
Cet article est associé à des tutoriels que vous trouverez en libre téléchargement sur le site Android2ee. À l'adresse suivante : Page des tutoriels des capteurs Android sur Android2ee
Liste des tutoriels disponibles :
Tutoriels |
SensorAccelerationTuto |
SensorGyroscopeTuto |
SensorLightTuto |
SensorMagneticFieldTuto |
SensorOrientationAMTuto |
SensorOrientationTuto |
SensorProximityTuto |
SensorRotationVectorTuto |
SensorList |
VIII. Articles, conférences et autres tutoriaux made in Android2EE▲
Vous pouvez trouver d'autres articles sur developpez.com rédigés par Android2EE.
Vous pouvez trouver les projets Android open sources d'Android2EE sur GitHub.
Android2EE sur Github |
GDirectionsApiUtils pour vous simplifiez l'utilisation du service REST Google Api Direction (les itinéraires entre deux points). Le MythicServiceHelper est un projet simplifiant la communication entre les services et les activités. Il est mieux d'utiliser, à l'heure d'aujourd'hui, EventBus ou Otto. Vous pouvez aller le voir quand même par curiosité. Le Bluetooth avec Android expliqué. Ce projet est un chat bluetooth qui vous montre, par l'exemple, comment mettre en place une communication BlueTooth entre deux appareils Android. Le projet ForecastYahooRest a pour objectif de vous expliquer comment mettre une architecture saine pour vos projets Android pour effectuer des appels REST. Ce projet est lié à la conférence "An Android Journey" que vous pouvez retrouver sur Android2ee. Le projet SignInWithGoogleTutorialvous explique comment mettre l'authentification Google au sein de vos applications. Il est lié à la conférence Google Sign In que vous pouvez retrouver sur Android2EE. |
Vous pouvez trouver les supports des conférences d'Android2EE sur SlideShared.
Android2EE sur SlideShare |
Retrouvez la liste des tutoriaux Android2EE de la formation complète. C'est pas ma meilleure conférence :) An Android Journey La conférence 2014 d'Android2EE qui vous donne toutes les astuces, les bonnes pratiques et les bons conseils à mettre en place dans vos applications pour en faire des applications exceptionnelles. ActionBarCompat Cette conférence vous explique comment mettre en place l'ActionBar dans vos applications pour toutes les versions du système en utilisant l'ActionBarCompat. GoogleSignIn L'authentification Google vous est expliquée dans cette conférence. Android ProTips Cette conférence a été donnée à la DroidCon Paris 2013 pour vous présenter les meilleurs conseils sur le développement Android donnée aux GoogleIo. Ce sont mes bonnes pratiques favorites. Architecture Android Une peu d'architecture, ça vous dit? Cette conférence est dédiée à l'architecture, quels sont les problèmes rencontrés et les bonnes pratiques à mettre en place. Android, un nouveau futur s'ouvre à nous Cette conférence, donnée à Brazzaville, présente les enjeux de la mobilité et en particulier ma vision du futur relative à Android. Combining the power of Eclipse with Android Cette conférence, donnée à l'EclipseDay 2012, présente comment utiliser la DDMS et chasser les fuites mémoires au sein de vos applications Android. A Quick OverviewCette conférence, donnée au CocoHeads Toulouse en 2012, est une introduction à la programmation Android. Android A Quick Course @DevoxxFr Cette présentation est un extraie de la conférence "Android A Quick Course", donnée à DevoxxFrance, pour apprendre le développement Android. Les vidéos (3 heures tout de même sont disponibles sur le site Android2EE ou sur Parleys) |
Les vidéos de certaines de ces conférences sont disponibles à l'adresse suivante : Les vidéos
IX. Le site Android2ee, une référence pour le développement Android.▲
Le site Android2EE vous propose des tutoriels, des articles, des vidéos, des conférences, des eBooks en libre consultation pour monter en compétence sur la technologie Android.
Vous trouverez tout cela dans la partie « Open Resources ».
N'hésitez plus visitez-le ! Android2EE
X. Android2ee vous présente l'Ebook de programmation Android▲
Le nouveau système d'exploitation de Google pour les téléphones portables et les nouvelles tablettes est là. Sa réputation est solide, il envahit le monde de la téléphonie, il est ouvert et offre des outils de développement Java au monde des programmeurs. Il ouvre les portes du développement mobile à tous les développeurs objets avec un coût minime pour la montée en compétence. Une seule question se pose :
Êtes-vous prêts ?
L'objectif de ces livres est très clair : vous permettre en un temps record d'être autonome en programmation Android. Si vous êtes un programmeur Java (débutant ou confirmé), le but est que vous soyez autonome en moins de dix jours. C'est cet objectif qui est à l'origine de ce livre, permettre aux collaborateurs de mon entreprise de monter en compétence sur cette technologie avec rapidité et efficience. Vous serez alors à même de concevoir une application, de l'implémenter, de la tester, de l'internationaliser et de la livrer à votre client.
Lancez-vous dans la programmation Android et faites-vous plaisir !
Vous serez aussi capable de connaître et comprendre quelles sont les considérations à avoir lorsque l'on a à charge une application Android en tant que professionnel de l'informatique. Quelle est la stratégie de tests à utiliser ? Comment signer son application ? Comment la déployer ? Comment mettre en place la gestion du cycle de vie de l'application ? Comment implémenter l'intégration continue ?
Soyez efficient dans l'encadrement de vos projets Android d'entreprise.
L'achat d'un EBook vous donne accès à l'ensemble des tutoriaux Android2EE, plus de 50 projets Android qui vous apprendrons à mettre en place une notion spécifique (SlidingDrawer, Parsing d'un service REST, Mise en place d'un service, d'un ContentProvider...). Vous pouvez dès maintenant vous lancer dans la programmation Android, n'hésitez pas faîtes vous plaisir, il y a 75% de réduction sur les EBooks en ce moment:
Apprendre la programmation Android avec les EBooks Android2EE |
Android, A Complete Course, From Basics To Enterprise Edition (fr). Android, A Quick Course (fr). Android, An Entreprise Edition Vision (fr). Les eBooks sont disponibles en français et en anglais, à votre convenance. |
Nota Bene: l'ouvrage "Android, A Complete Course, From Basics To Enterprise Edition" réunit, au sein d'un même document, les livres "Android, A Quick Course" et "Android, An Enterprise Edition Vision" permettant au lecteur d'avoir dans un même document la totalité des préoccupations liées à la mise en place de projets Android, de la montée en compétence en tant que développeur à la gestion du cycle de vie du projet.