Dans la programmation Android courante, Handler est souvent utilisé lors de l’exécution d’opérations asynchrones et du traitement des résultats renvoyés. Habituellement, notre code est implémenté comme ceci.
Copiez le code comme suit :
la classe publique SampleActivity étend l'activité {
Gestionnaire final privé mLeakyHandler = new Handler() {
@Outrepasser
public void handleMessage (Message msg) {
//...
}
}
}
Cependant, en fait, le code ci-dessus peut provoquer des fuites de mémoire. Lorsque vous utilisez l'outil Android Lint, vous recevrez un tel avertissement.
Copiez le code comme suit :
Sous Android, les classes de gestionnaire doivent être statiques, sinon des fuites peuvent survenir. Les messages mis en file d'attente dans MessageQueue du thread d'application conservent également leur gestionnaire cible. Si le gestionnaire est une classe interne, sa classe externe sera également conservée. déclarer le Handler comme une classe imbriquée statique avec un WeakReference à sa classe externe
En voyant cela, vous pouvez toujours être confus. Où dans le code peut provoquer une fuite de mémoire, et comment cela peut-il provoquer une fuite de mémoire ? Alors analysons-le lentement.
1. Lorsqu'une application Android démarre, une instance Looper est automatiquement créée pour être utilisée par le thread principal de l'application. La tâche principale de Looper consiste à traiter les objets de message dans la file d'attente de messages un par un. Sous Android, tous les événements du framework Android (tels que les appels de méthode du cycle de vie d'activité et les clics sur les boutons, etc.) sont placés dans des messages, puis ajoutés à la file d'attente des messages pour être traités par Looper, et Looper est responsable de leur traitement. par un. Le cycle de vie du Looper dans le thread principal est aussi long que l'application actuelle.
2. Lorsqu'un gestionnaire est initialisé dans le thread principal et que nous envoyons un message ciblant ce gestionnaire à la file d'attente des messages traitée par Looper, le message effectivement envoyé contient déjà une référence à une instance de Handler. Ce n'est que de cette manière que le traitement du Looper est effectué uniquement lorsque cela est le cas. message est reçu, Handler#handleMessage(Message) peut-il être appelé pour terminer le traitement correct du message.
3. En Java, les classes internes non statiques et les classes internes anonymes contiennent implicitement des références à leurs classes externes. Les classes internes statiques ne contiennent pas de références aux classes externes. Pour plus d'informations à ce sujet, veuillez consulter Chat Java : modificateur privé "Invalide"
Il est vrai que l'exemple de code ci-dessus est un peu difficile à détecter la fuite de mémoire, alors l'exemple suivant est très évident
Copiez le code comme suit :
la classe publique SampleActivity étend l'activité {
Gestionnaire final privé mLeakyHandler = new Handler() {
@Outrepasser
public void handleMessage (Message msg) {
//...
}
}
@Outrepasser
protected void onCreate (Bundle saveInstanceState) {
super.onCreate(savedInstanceState);
// Poste un message et retarde son exécution de 10 minutes.
mLeakyHandler.postDelayed(new Runnable() {
@Outrepasser
public void run() { /* ... */ }
}, 1000 * 60 * 10);
// Retour à l'activité précédente.
finition();
}
}
En analysant le code ci-dessus, lorsque nous exécutons la méthode finish de l'activité, le message retardé existera dans la file d'attente des messages du thread principal pendant 10 minutes avant d'être traité, et ce message contient une référence au gestionnaire, et le gestionnaire est une instance anonyme de la classe interne contient une référence à la SampleActivity externe, ce qui empêche le recyclage de la SampleActivity. Par conséquent, de nombreuses ressources détenues par la SampleActivity ne peuvent pas être recyclées. C'est ce que nous appelons souvent une fuite de mémoire.
Notez que le nouveau Runnable ci-dessus est également implémenté par une classe interne anonyme. Il contient également une référence à SampleActivity et empêche le recyclage de SampleActivity.
Pour résoudre ce problème, l'idée est de ne pas utiliser de classes internes non statiques. Lors de l'héritage de Handler, placez-le dans un fichier de classe séparé ou utilisez une classe interne statique. Étant donné que la classe interne statique ne contient pas de référence à la classe externe, elle ne provoquera pas de fuites de mémoire de l'instance de classe externe. Lorsque vous devez appeler une activité externe dans une classe interne statique, nous pouvons utiliser des références faibles pour la gérer. De plus, Runnable doit également être défini comme attribut de membre statique. Remarque : Une instance de classe interne anonyme statique ne contient pas de référence à la classe externe. Le code qui ne provoquera pas de fuite mémoire après modification est le suivant :
Copiez le code comme suit :
la classe publique SampleActivity étend l'activité {
/**
* Les instances de classes internes statiques ne détiennent pas de valeur implicite
* référence à leur classe externe.
*/
classe statique privée MyHandler étend Handler {
privé final WeakReference<SampleActivity> mActivity;
public MyHandler (activité SampleActivity) {
mActivity = new WeakReference<SampleActivity>(activité);
}
@Outrepasser
public void handleMessage (Message msg) {
Activité SampleActivity = mActivity.get();
si (activité != null) {
//...
}
}
}
privé final MyHandler mHandler = new MyHandler(this);
/**
* Les instances de classes anonymes ne détiennent pas de valeur implicite
* référence à leur classe externe lorsqu'ils sont "statiques".
*/
privé statique final Runnable sRunnable = new Runnable() {
@Outrepasser
public void run() { /* ... */ }
} ;
@Outrepasser
protected void onCreate (Bundle saveInstanceState) {
super.onCreate(savedInstanceState);
// Poste un message et retarde son exécution de 10 minutes.
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
// Retour à l'activité précédente.
finition();
}
}
En fait, de nombreuses fuites de mémoire dans Android sont causées par l'utilisation de classes internes non statiques dans Activity, comme mentionné dans cet article, nous devons donc prêter une attention particulière lors de l'utilisation de classes internes non statiques si son instance est le cycle de vie de. l'objet détenu est supérieur à son objet de classe externe, cela peut entraîner des fuites de mémoire. Personnellement, j'ai tendance à utiliser la classe statique de l'article et les méthodes de référence faibles pour résoudre ce problème.