Je reçois un avertissement dans mon code qui dit:
Cette classe AsyncTask doit être statique ou des fuites peuvent se produire (Android.os.AsyncTask anonyme)
L'avertissement complet est:
Cette classe AsyncTask doit être statique ou des fuites peuvent se produire (Android.os.AsyncTask anonyme). Un champ statique entraîne la fuite des contextes. Les classes internes non statiques ont une référence implicite à leur classe externe. Si cette classe externe est par exemple un fragment ou une activité, cette référence signifie que le gestionnaire/chargeur/tâche de longue durée tiendra une référence à l'activité qui l'empêche de se faire ramasser. De même, les références de champ directes aux activités et aux fragments provenant de ces instances plus longues peuvent provoquer des fuites. Les classes ViewModel ne doivent jamais pointer sur des vues ou des contextes non applicatifs.
Ceci est mon code:
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... params) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
return null;
}
}.execute();
Comment puis-je corriger cela?
Les classes internes non statiques contiennent une référence à la classe qui le contient. Lorsque vous déclarez AsyncTask
en tant que classe interne, sa durée de vie peut être supérieure à celle de la classe contenant Activity
. Cela est dû à la référence implicite à la classe contenante. Cela empêchera l'activité d'être ramassée, d'où la fuite de mémoire.
Pour résoudre votre problème, utilisez la classe imbriquée statique au lieu de la classe anonyme, locale et interne ou la classe de niveau supérieur.
Pour éviter les fuites, vous pouvez rendre la classe interne statique. Le problème avec cela, cependant, est que vous n'avez plus accès aux vues de l'interface utilisateur de l'activité ou aux variables membres. Vous pouvez transmettre une référence à la variable Context
, mais vous courez le même risque de fuite de mémoire. (Android ne peut pas récupérer l’activité après sa fermeture si la classe AsyncTask en fait une référence forte.) La solution consiste à faire une référence faible à l’Activité (ou à ce que vous ayez besoin de Context
).
public class MyActivity extends AppCompatActivity {
int mSomeMemberVariable = 123;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// start the AsyncTask, passing the Activity context
// in to a custom constructor
new MyTask(this).execute();
}
private static class MyTask extends AsyncTask<Void, Void, String> {
private WeakReference<MyActivity> activityReference;
// only retain a weak reference to the activity
MyTask(MyActivity context) {
activityReference = new WeakReference<>(context);
}
@Override
protected String doInBackground(Void... params) {
// do some long running task...
return "task finished";
}
@Override
protected void onPostExecute(String result) {
// get a reference to the activity if it is still there
MyActivity activity = activityReference.get();
if (activity == null || activity.isFinishing()) return;
// modify the activity's UI
TextView textView = activity.findViewById(R.id.textview);
textView.setText(result);
// access Activity member variables
activity.mSomeMemberVariable = 321;
}
}
}
AsyncTask
disponibles ne sont toujours pas traités (voir ici , ici , ici , et ici ).AsyncTask
était une classe de niveau supérieur. Une classe interne statique est fondamentalement la même chose qu'une classe de niveau supérieur en Java.Si vous n'avez pas besoin de l'activité elle-même mais souhaitez quand même le contexte (par exemple, pour afficher un Toast
), vous pouvez transmettre une référence au contexte de l'application. Dans ce cas, le constructeur AsyncTask
ressemblerait à ceci:
private WeakReference<Application> appReference;
MyTask(Application context) {
appReference = new WeakReference<>(context);
}
En Kotlin, juste n'incluez pas le mot clé inner
pour la classe interne. Cela le rend statique par défaut.
Je ne suis pas encore trop bon chez Kotlin, corrigez donc le code ci-dessous s'il peut être amélioré:
class MyActivity : AppCompatActivity() {
internal var mSomeMemberVariable = 123
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// start the AsyncTask, passing the Activity context
// in to a custom constructor
MyTask(this).execute()
}
private class MyTask
internal constructor(context: MyActivity) : AsyncTask<Void, Void, String>() {
private val activityReference: WeakReference<MyActivity> = WeakReference(context)
override fun doInBackground(vararg params: Void): String {
// do some long running task...
return "task finished"
}
override fun onPostExecute(result: String) {
// get a reference to the activity if it is still there
val activity = activityReference.get()
if (activity == null || activity.isFinishing) return
// modify the activity's UI
val textView = activity.findViewById(R.id.textview)
textView.setText(result)
// access Activity member variables
activity.mSomeMemberVariable = 321
}
}
}
Cette classe AsyncTask
devrait être statique ou des fuites pourraient se produire car
Activity
est détruit, AsyncTask
(les deux static
ou non-static
) sont toujours en cours d'exécution.non-static
(AsyncTask
), elle fera référence à la classe externe (Activity
).Garbage Collected
le libérera. Si un objet est inutilisé et Garbage Collected
ne peut pas relâchez-le => fuite de mémoire=> Si AsyncTask
est non-static
, Activity
ne déclenchera pas l'événement, il est détruit => fuite
Solution pour mettre à jour l'interface utilisateur après make AsyncTask comme classe statique sans fuite
1) Utilisez WeakReference
comme @Suragch answer
2) Envoyer et supprimer Activity
référence à (de) AsyncTask
public class NoLeakAsyncTaskActivity extends AppCompatActivity {
private ExampleAsyncTask asyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// START AsyncTask
asyncTask = new ExampleAsyncTask();
asyncTask.setListener(new ExampleAsyncTask.ExampleAsyncTaskListener() {
@Override
public void onExampleAsyncTaskFinished(Integer value) {
// update UI in Activity here
}
});
asyncTask.execute();
}
@Override
protected void onDestroy() {
asyncTask.setListener(null); // PREVENT LEAK AFTER ACTIVITY DESTROYED
super.onDestroy();
}
static class ExampleAsyncTask extends AsyncTask<Void, Void, Integer> {
private ExampleAsyncTaskListener listener;
@Override
protected Integer doInBackground(Void... voids) {
...
return null;
}
@Override
protected void onPostExecute(Integer value) {
super.onPostExecute(value);
if (listener != null) {
listener.onExampleAsyncTaskFinished(value);
}
}
public void setListener(ExampleAsyncTaskListener listener) {
this.listener = listener;
}
public interface ExampleAsyncTaskListener {
void onExampleAsyncTaskFinished(Integer value);
}
}
}