0. Introduction▲
Cet article explique comment mettre en place des Threads de background de manière appropriée sous Android. Il explique comment effectuer des traitements dans vos applications. Le discours se centre sur les activités ayant à effectuer des traitements, mais ces principes s'appliquent aussi bien aux services.
La première partie de cet article explique le fonctionnement des Handlers et des AsyncTasks de manière générale. Elle montre comment déclarer une Thread de traitement et la faire communiquer avec votre Thread d'IHM.
La seconde partie se concentre sur la problématique des fuites mémoires qui apparaissent si la Thread n'est pas liée au cycle de vie de l'activité (du service). Et oui, je ne vais considérer les fuites mémoires que dans le cadre de la synchronisation du cycle de vie des Threads et des activités. Cet article ne traite pas des fuites mémoires dans leur généralité.
0-1. Les Threads▲
Il faut toujours garder en tête quand on fait de la programmation Android que si une activité réagit en plus de 5 secondes, elle sera tuée par l'ActivityManager qui la considèrera comme morte.
Les IHMs de l'activité sont dans une Thread qui lui est propre (comme en swing). C'est cette Thread qui est chargée de l'interaction avec l'utilisateur. Ainsi pour effectuer un traitement, il faut lancer une autre Thread (dites de background, de traitement ou d'arrière-plan) qui effectue le traitement. Plusieurs façons d'interagir entre les Threads en arrière-plan et la Thread d'IHM sont possibles.
Mais surtout il faut garder toujours en tête qu'aucun traitement ne doit être effectué dans la Thread d'IHM !
Deux objets dédiés à ce pattern sont disponibles nativement sur Android, les Handlers et les AsyncTasks.
1. Les Handlers▲
Le Handler est associé à l'activité qui le déclare et travaille au sein de la Thread d'IHM. Ce qui signifie que tout traitement effectué par le Handler gèle l'IHM le temps qu'il soit effectué. Il faut donc considérer le Handler comme celui qui met à jour l'IHM, la Thread qui appelle le Handler a la charge du traitement. Le Handler ne doit que mettre à jour l'IHM, tout autre comportement est une erreur de conception.
Une Thread communique avec un Handler au moyen de messages (de l'objet Message pour être exact). Pour cela :
- La Thread récupère l'objet Message du pool du Handler par handler.obtainedMessage. Cette méthode peut être surchargée de manière à envoyer plus d'informations à la Thread (en lui passant d'autres paramètres au moyen d'un Bundle).
-
La Thread envoie le message au Handler en utilisant l'une des
méthodes suivantes :
- sendMessage (envoie le message et le place à la fin de la queue)
- sendMessageAtFrontOfQueue (envoie le message et le place au début de la queue)
- sendMessageAtTime (envoie le message au moment donné en paramètre et le place à la fin de la queue)
- sendMessageDelayed (envoie le message après un temps d'attente passé en paramètre et le place à la fin de la queue)
- Le Handler doit surcharger sa méthode handleMessage pour répondre aux messages qui lui sont envoyés. Il a à charge de mettre à jour l'IHM en fonction de ces données.

L'exemple suivant met à jour une ProgressBar:
<
LinearLayout
xmlns
:
android
=
"
http://schemas.android.com/apk/res/android
"
android
:
orientation
=
"
vertical
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
fill_parent
"
>
<
ProgressBar
android
:
id
=
"
@+id/progress
"
style
=
"
?android:attr/progressBarStyleHorizontal
"
android
:
layout_width
=
"
fill_parent
"
android
:
layout_height
=
"
wrap_content
"
/
>
<
/
LinearLayout
>
Le code java :
/**
*
@author
Android2ee
*
@goals
Cette
classe
a
pour
but
de
montrer
comment
utiliser
les
Handlers
et
les
Threads
*/
public
class
HandlerTuto extends
Activity {
/**
*
La
ProgressBar
à
mettre
à
jour
*/
ProgressBar bar;
/**
*
La
clef
dans
le
bundle
pour
incrémenter
la
progressBar
*/
private
final
String PROGRESS_BAR_INCREMENT=
"
ProgreesBarIncrementId
"
;
/**
*
Le
Handler
à
charge
de
la
communication
entre
la
Thread
de
background
et
celle
de
l
'
IHM
*/
Handler handler =
new
Handler
(
) {
@
Override
public
void
handleMessage
(
Message msg) {
int
progress=
msg.getData
(
).getInt
(
PROGRESS_BAR_INCREMENT);
//
Incrémenter
la
ProgressBar,
on
est
bien
dans
la
Thread
de
l'IHM
bar.incrementProgressBy
(
progress);
//
On
peut
faire
toute
action
qui
met
à
jour
l'IHM
}
}
;
/**
*
L
'
AtomicBoolean
qui
gère
la
destruction
de
la
Thread
de
background
*/
AtomicBoolean isRunning =
new
AtomicBoolean
(
false
);
/**
*
L
'
AtomicBoolean
qui
gère
la
mise
en
pause
de
la
Thread
de
background
*/
AtomicBoolean isPausing =
new
AtomicBoolean
(
false
);
//
Création
de
l'activité
@
Override
public
void
onCreate
(
Bundle icicle) {
super
.onCreate
(
icicle);
setContentView
(
R.layout.main);
//
Définition
de
la
ProgressBar
bar =
(
ProgressBar) findViewById
(
R.id.progress);
bar.setMax
(
210
);
}
/**
Méthode
du
cycle
de
vie
de
l
'
activité
(
lancement
de
celle
-
ci
)
*/
public
void
onStart
(
) {
super
.onStart
(
);
//
initialisation
de
la
ProgressBar
bar.setProgress
(
0
);
//
Définition
de
la
Thread
(peut
être
effectuée
dans
une
classe
externe
ou
interne)
Thread background =
new
Thread
(
new
Runnable
(
) {
/**
*
Le
Bundle
qui
porte
les
données
du
Message
et
sera
transmis
au
Handler
*/
Bundle messageBundle=
new
Bundle
(
);
/**
*
Le
message
échangé
entre
la
Thread
et
le
Handler
*/
Message myMessage;
//
Surcharge
de
la
méthode
run
public
void
run
(
) {
try
{
//
Si
isRunning
est
à
false,
la
méthode
run
doit
s'arrêter
for
(
int
i =
0
; i <
20
&
&
isRunning.get
(
); i+
+
) {
//
Si
l'activité
est
en
pause
mais
pas
morte
while
(
isPausing.get
(
) &
&
(
isRunning.get
(
))) {
//
Faire
une
pause
ou
un
truc
qui
soulage
le
CPU
(dépend
du
traitement)
Thread.sleep
(
2000
);
}
//
Effectuer
le
traitement,
pour
l'exemple
je
dors
une
seconde
Thread.sleep
(
1000
);
//
Envoyer
le
message
au
Handler
(la
méthode
handler.obtainMessage
est
plus
efficace
//
que
créer
un
message
à
partir
de
rien,
optimisation
du
pool
de
message
du
Handler)
//
Instanciation
du
message
(la
bonne
méthode):
myMessage=
handler.obtainMessage
(
);
//
Ajouter
des
données
à
transmettre
au
Handler
via
le
Bundle
messageBundle.putInt
(
PROGRESS_BAR_INCREMENT, 1
);
//
Ajouter
le
Bundle
au
message
myMessage.setData
(
messageBundle);
//
Envoyer
le
message
handler.sendMessage
(
myMessage);
}
}
catch
(
Throwable t) {
//
gérer
l'exception
et
arrêter
le
traitement
}
}
}
);
//
Initialisation
des
AtomicBooleans
isRunning.set
(
true
);
isPausing.set
(
false
);
//
Lancement
de
la
Thread
background.start
(
);
}
/**
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/**
Gestion
du
cycle
de
vie
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/**
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
//
Méthode
appelée
quand
l'activité
s'arrête
public
void
onStop
(
) {
super
.onStop
(
);
//
Mise-à-jour
du
booléen
pour
détruire
la
Thread
de
background
isRunning.set
(
false
);
}
/*
(non-Javadoc)
*
@see
android.app.Activity#onPause()
*/
@
Override
protected
void
onPause
(
) {
super
.onPause
(
);
//
Mise-à-jour
du
booléen
pour
mettre
en
pause
la
Thread
de
background
isPausing.set
(
true
);
}
/*
(non-Javadoc)
*
@see
android.app.Activity#onResume()
*/
@
Override
protected
void
onResume
(
) {
super
.onResume
(
);
//
Mise-à-jour
du
booléen
pour
relancer
la
Thread
de
background
isPausing.set
(
false
);
}
}
Cet exemple utilise des objets de type AtomicBoolean, l'explication est donnée dans le paragraphe traitant des fuites mémoires.
1-1. Suis-je dans la Thread de l'IHM ou pas ?▲
La classe Activity possède la méthode runOnUIThread pour lancer un traitement dans la Thread de l'IHM.
2. La désynchronisation avec l'AsyncTask▲
Depuis la version 1.5 d'Android, l'AsyncTask permet une nouvelle façon d'effectuer des tâches en arrière-plan.
Pour cela :
- Créer une sous-classe d'AsyncTask (elle peut être interne et privée à l'activité).
- Redéfinir une ou plusieurs de ces méthodes pour spécifier son travail.
- La lancer au moyen de sa méthode execute
La dérivation d'une classe AsyncTask n'est pas triviale, elle est générique et à paramètres variables. Pour la généricité elle attend 3 paramètres :
- Le type de l'information qui est nécessaire au traitement (dans l'exemple URL )
- Le type de l'information qui est passé à sa tâche pour indiquer sa progression (dans l'exemple : Integer )
- Le type de l'information passé au code lorsque la tâche est finie (dans l'exemple Long ).
Une dernière chose d'importance est que la classe AsyncTask (et donc vos classes filles) s'exécute dans deux Threads distinctes ; la Thread d'IHM et la Thread de traitement.
Un exemple (toujours celui qui met à jour une ProgressBar):
private
class
DownloadFilesTask extends
AsyncTask<
URL, Integer, Long>
{
protected
Long doInBackground
(
URL... urls) {
int
count =
urls.length;
long
totalSize =
0
;
for
(
int
i =
0
; i <
count; i+
+
) {
totalSize +
=
Downloader.downloadFile
(
urls[i]);
publishProgress
(
(
int
) (
(
i /
(
float
) count) *
100
));
}
return
totalSize;
}
protected
void
onProgressUpdate
(
Integer... progress) {
setProgressPercent
(
progress[0
]);
}
protected
void
onPostExecute
(
Long result) {
showDialog
(
"
Downloaded
"
+
result +
"
bytes
"
);
}
}
Les étapes d'AsynchTask :
doInBackground est la méthode qui s'exécute dans une autre Thread. Elle reçoit un tableau d'objets lui permettant ainsi d'effectuer un traitement en série sur ces objets. Seule cette méthode est exécutée dans une Thread à part, les autres méthodes s'exécutent dans la Thread de l'IHM .
onPreExecute est appelée par la Thread de l'IHM avant l'appel à doInBackground , elle permet de pré-initialiser les éléments de l'IHM.
onPostExecute est appelée lorsque la méthode doInBackground est terminée.
onProgressUpdate est appelée par la méthode publishProgress à l'intérieur de la méthode doInBackground.

Exemple :
/**
*
@author
Android2ee
*
@goals
Cette
classe
montre
un
usage
simple
du
Pattern
AsyncTask
.
Attention
,
elle
génère
des
fuites
mémoires
*
si
elle
est
utilisée
telle
quelle
.
*/
public
class
AsyncTuto extends
Activity {
/**
*
La
ProgressBar
à
mettre
à
jour
*/
ProgressBar bar;
@
Override
public
void
onCreate
(
Bundle savedInstanceState) {
super
.onCreate
(
savedInstanceState);
setContentView
(
R.layout.main);
//
Définition
de
la
ProgressBar
bar =
(
ProgressBar) findViewById
(
R.id.progress);
bar.setMax
(
210
);
//
Lancement
de
l'AsynchTask
new
MyAsyncTask
(
).execute
(
);
}
/**
*
Cette
classe
montre
une
simple
dérivation
de
la
classe
AsyncTask
*/
class
MyAsyncTask extends
AsyncTask<
Void, Integer, String>
{
/**
*
Un
compteur
*/
Integer count =
0
;
//
Surcharge
de
la
méthode
doInBackground
(Celle
qui
s'exécute
dans
une
Thread
à
part)
@
Override
protected
String doInBackground
(
Void... unused) {
try
{
while
(
count <
20
) {
//
incrémente
le
compteur
count+
+
;
//
Faire
une
pause
Thread.sleep
(
1000
);
//
Donne
son
avancement
en
appelant
onProgressUpdate
publishProgress
(
count);
}
}
catch
(
InterruptedException t) {
//
Gérer
l'exception
et
terminer
le
traitement
return
(
"
The
sleep
operation
failed
"
);
}
return
(
"
return
object
when
task
is
finished
"
);
}
//
Surcharge
de
la
méthode
onProgressUpdate
(s'exécute
dans
la
Thread
de
l'IHM)
@
Override
protected
void
onProgressUpdate
(
Integer... diff) {
//
Mettre
à
jour
l'IHM
bar.incrementProgressBy
(
diff[0
]);
}
//
Surcharge
de
la
méthode
onPostExecute
(s'exécute
dans
la
Thread
de
l'IHM)
@
Override
protected
void
onPostExecute
(
String message) {
//
Mettre
à
jour
l'IHM
Toast.makeText
(
getApplicationContext
(
), message, Toast.LENGTH_SHORT).show
(
);
}
}
}
3. AsyncTask ou Handler▲
Ces deux méthodes ont pour but d'effectuer des traitements liés aux IHMs dans des Threads indépendantes de celle de l'IHM. La question est de savoir laquelle choisir dans quelles circonstances.
L'idée est que si vous faites un traitement lourd, spécifique, qui
n'a besoin que de donner son avancement et sa fin (typiquement
charger des données), le mieux est l'utilisation de l'AsyncTask.
A contrario, si vous avez besoin d'établir une communication avec
l'IHM, il est plus pertinent d'utiliser un Handler ( par exemple la
communication Bluetooth entre deux appareils utilise ce pattern).
Ainsi, si vous avez un traitement qui correspond à effectuer la même
action sur un ensemble d'éléments donnés et connus avant le début du
traitement, l'utilisation de l'AsyncTask est préconisée. Sinon, il y
a de grandes chances qu'il vous faille utiliser le pattern du
Handler.
En conclusion: un traitement par lots s'effectuera avec une
AsyncTask et un traitement nécessitant une communication dynamique
entre la Thread de traitement et l'IHM utilisera le pattern du
Handler.
4. Threads et fuites mémoires▲
La question qui se pose est « Que se passe-t-il lorsque l'activité suit son cycle de vie et est détruite puis recréée ? Typiquement une rotation de l'écran? Que devient la Thread de traitement ? »
Dans la plupart des cas, le développeur ne fait pas attention et l'ancienne Thread n'est pas détruite, une nouvelle Thread est mise en place. Le traitement peut rester cohérent (ou pas).
La même chose se produit avec les AsynchTask bien que seuls les Handlers soient cités dans la suite de l'article. Par contre, les tutoriels disponibles traitent tous les cas.
Le schéma mémoire qui se met en place est alors le suivant :

On s'aperçoit que la destruction de l'activité ne détruit pas la Thread, celle-ci reste « en vie ». Pire, elle continue de pointer vers le Handler et l'activité, qui bien que considérés détruits par Android ne le sont pas (vous ne pouvez pas y accéder, ni afficher l'activité.). Le GarbageCollector détecte que ces ressources sont utilisées (en effet la Thread pointe sur ces espaces mémoires) et ne les collecte pas.
Pire encore, quand l'activité est recréée (typiquement un changement d'orientation), elle recrée aussi une nouvelle Thread et la lance.
On se retrouve au final (suite à un changement d'orientation par exemple) avec :
- deux threads qui effectuent le même traitement mais n'en sont pas au même point,
- Une Activity et un Handler actifs,
- Une activité et un Handler fantômes, ni vraiment morts ni vraiment vivants.
Alors quelle est la parade ?
Avant de trouver la solution, il faut tout d'abord se poser les questions suivantes :
- La Thread doit-elle être détruite lorsque l'activité se termine ?
- La Thread doit-elle se terminer lorsque l'activité est détruite puis recréée ?
- L'utilisateur a-t-il son mot à dire ?
Ces questions sont fondamentales pour le Design Pattern que vous allez implémenter pour votre traitement.
Clairement si la fin de l'activité ne termine pas la Thread, il est fort à parier que vous auriez du mettre un service entre l'activité et la Thread. Celui-ci ayant à charge le traitement est lancé par l'activité et continue indépendamment du cycle de vie de l'activité.
Dans les cas où le cycle de vie de votre activité doit être lié à celui de votre Thread, deux cas s'offrent à vous : l'utilisation de deux booléens « synchronized » appelés AtomicBoolean ou l'utilisation en plus de ces deux éléments de la méthode onRetainNonConfigurationInstance.
Deux projets Eclipse démontrant cette fuite mémoire sont disponible ici : page des tutoriels sur les Handlers .
L'un démontre cette fuite avec un Handler, l'autre la démontre avec un AsyncTask.
4-1. Utilisation des AtomicBooleans pour lier le cycle de vie de la Thread à celui de l'activité.▲
Les AtomicBooleans sont des objets de type Boolean qui sont thread safe. En effet les méthodes d'accès aux valeurs du booléens sont « synchronized ». Ainsi on accède à la valeur de ce type d'objet par appel à la méthode get() et on change sa valeur en utilisant la méthode set.
public
class
HandlerTutoActivity extends
Activity {
/**
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/**
Gérer
le
Handler
et
la
Thread
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/**
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/**
*
Le
Handler
*/
private
Handler handler;
/**
*
Le
AtomicBoolean
pour
lancer
et
stopper
la
Thread
*/
private
AtomicBoolean isThreadRunnning =
new
AtomicBoolean
(
);
/**
*
Le
AtomicBoolean
pour
mettre
en
pause
et
relancer
la
Thread
*/
private
AtomicBoolean isThreadPausing =
new
AtomicBoolean
(
);
/**
*
La
Thread
*/
Thread backgroundThread;
/**
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/**
Gérer
l
'
activité
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/**
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/**
Appelé
à
la
création
de
l
'
activité
.
*/
@
Override
public
void
onCreate
(
Bundle savedInstanceState) {
super
.onCreate
(
savedInstanceState);
setContentView
(
R.layout.main);
//
Instancier
la
ProgressBar
progressBar =
(
ProgressBar) findViewById
(
R.id.progressbar);
progressBar.setMax
(
100
);
//
définition
du
Handler
handler =
new
Handler
(
) {
/*
*
(non-Javadoc)
*
*
@see
android.os.Handler#handleMessage(android.os.Message)
*/
@
Override
public
void
handleMessage
(
Message msg) {
super
.handleMessage
(
msg);
Log.d
(
TAG, "
handle
message
called
"
);
//
s'assurer
que
la
Thread
est
bien
en
mode
Run
avant
de
faire
quelque
chose
if
(
isThreadRunnning.get
(
)) {
Log.w
(
TAG, "
handle
message
calls
updateProgress
"
);
updateProgress
(
);
}
}
}
;
//
Définition
de
la
Thread
et
liaison
avec
le
Handler
backgroundThread =
new
Thread
(
new
Runnable
(
) {
/**
*
Le
message
échangé
entre
la
Thread
et
le
Handler
*/
Message myMessage;
/**
(
non
-
Javadoc
)
*
*
@see
java
.
lang
.
Runnable
#
run
(
)
*/
public
void
run
(
) {
try
{
while
(
isThreadRunnning.get
(
)) {
if
(
isThreadPausing.get
(
)) {
//
Faire
une
pause
cohérente
avec
le
traitement,
soulager
le
CPU
Thread.sleep
(
2000
);
}
else
{
//
Faire
un
traitement
Thread.sleep
(
100
);
//
Obtenir
le
Message
myMessage =
handler.obtainMessage
(
);
//
envoyer
le
message
au
Hanlder
handler.sendMessage
(
myMessage);
}
}
}
catch
(
Throwable t) {
//
Termine
la
Thread
}
}
}
);
//
Initialiser
le
booléen
isThreadRunning
isThreadRunnning.set
(
true
);
//
Lancer
la
Thread
backgroundThread.start
(
);
}
/*
*
(non-Javadoc)
*
*
@see
android.app.Activity#onDestroy()
*/
protected
void
onDestroy
(
) {
//
Tuer
la
Thread
isThreadRunnning.set
(
false
);
super
.onDestroy
(
);
}
/*
*
(non-Javadoc)
*
*
@see
android.app.Activity#onPause()
*/
protected
void
onPause
(
) {
//
Mettre
la
Thread
en
pause
isThreadPausing.set
(
true
);
super
.onPause
(
);
}
/*
*
(non-Javadoc)
*
*
@see
android.app.Activity#onResume()
*/
protected
void
onResume
(
) {
//
Relancer
la
Thread
isThreadPausing.set
(
false
);
super
.onResume
(
);
}
Ce pattern (qui est le même que celui présenté en début d'article) permet d'obtenir le schéma mémoire suivant lors du passage de l'activité par les méthodes onDestroy, onCreate :

Ce schéma mémoire a le mérite d'être sans fuite mémoire, a contrario, il réinitialise le traitement de la Thread (en fait il en créé un nouvelle) à chaque redémarrage de l'activité. Il pourrait être utile de sauvegarder l'état d'avancement de la Thread dans la méthode onDestroy puis de restaurer cet état dans la méthode onCreate.
Cette solution ne permet pas de pallier au problème du changement de configuration de l'appareil qui reconstruit l'activité immédiatement après sa destruction. Pour cela il faut utiliser la méthode onRetainNonConfigurationInstance.
Un tutoriel présentant cette solution est disponible ici : page des tutoriels sur les Handlers .
4-2. Utilisation de la méthode onRetainNonConfigurationInstance ▲
Attention : Pour garder un caractère compréhensible, ce paragraphe explique l'utilisation de cette méthode. Mais si vous utilisez le code décrit ci-dessous directement vous ne gérez plus la fuite mémoire générée par la destruction de l'activité. Il faut inclure dans la solution de ce paragraphe, l'utilisation des AtomicBoolean du paragraphe précédent qui permettent de n'avoir jamais de fuite mémoire. Le tutoriel HandlerActivityBindingThreadTuto disponible sur Android2ee vous présente la solution complète et propre, il se télécharge ici : page des tutoriels sur les Handlers .
Mais tout d'abord « Qu'es aquò la méthode OnRetainNonConfigurationInstance ? » comme dirait mon grand-père.
De manière générale les méthodes Object publicgetLastNonConfigurationInstance() et public Object onRetainNonConfigurationInstance() permettent de passer un objet entre deux instances d'une activité lorsque celle-ci est détruite pour être immédiatement recréée. Il faut donc toujours tester la valeur de retour (null ou pas) et être cohérent avec ce retour.
On renvoie l'objet (ou la liste d'objets) avec la méthode onRetainNonConfiguartionInstance et on le récupère avec getLastNonConfigurationInstance.
Nous utiliserons ces méthodes pour passer en paramètre la Thread ; ce qui donne :
/**
Appelée
à
la
première
creation
de
l
'
activité
*/
@
Override
public
void
onCreate
(
Bundle savedInstanceState) {
super
.onCreate
(
savedInstanceState);
//
Faire
quelque
chose
backgroundThread =
(
Thread) getLastNonConfigurationInstance
(
);
//
Tester
si
la
Thread
est
nulle
ou
pas
et
adapter
le
traitement
en
fonction
}
@
Override
public
Object onRetainNonConfigurationInstance
(
) {
//
Renvoyer
la
Thread
return
backgroundThread;
}
Ce DesignPattern permet d'obtenir le schéma mémoire suivant :

Remarquez que ce schéma mémoire a le bon goût lors de la destruction puis recréation (immédiate) de l'activité de conserver la même Thread de Traitement et d'éviter les fuites mémoires. Il est à noter que sans les AtomicBooleans lorsque l'activité s'arrête, vous générez une fuite mémoire, il faut donc impérativement mixer les deux approches.
Un tutoriel présentant cette solution est disponible ici : page des tutoriels sur les Handlers .
5. Conclusion▲
Suite à cet article vous êtes censés être capable d'effectuer des traitements dans vos applications qui ne gèlent pas vos IHMs et qui ne génèrent pas de fuite mémoire. J'espère que vous vous en souviendrez dans vos projets ou quand vous expliquerez à vos stagiaires comment coder proprement. En attendant, bonne continuation à vous.
6. Android2EE: La Formation et l'Expertise Android à votre service▲
![]() |
Android2EE
|
|
7. Récupération des tutoriels▲
Cet article est associé à un tutoriel que vous trouverez en libre téléchargement sur le site Android2ee. À l'adresse suivante : Page des tutoriels des Handlers-AsyncTask sur Android2ee
Liste des tutoriels disponibles :
Tutorial |
OrphanThreadDemo |
HandlerTuto |
HandlerActivityBindingThreadDemo |
OrphanAsyncTaskThreadTuto |
AsyncTaskTuto |
8. 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
9. 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
10. 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.
11. Remerciements▲
J'adresse ici tous mes remerciements à Feanorin pour son implication, son aide et sa sympathie et à Erielle pour l'excellence de ses corrections orthographiques.
Je remercie les correcteurs techniques MrDuChnok, David55 et Feanorin pour la pertinence de leurs remarques.
Je remercie spécialement Monsieur Adam Daniel, DRH de développez.com, pour ses mails nocturnes, ses encouragements et son aide qui m'est précieuse.