Advertencia: esta clase AsyncTask debe ser estática o pueden producirse filtraciones

Recibo una advertencia en mi código que dice:

Esta clase AsyncTask debe ser estática o pueden producirse filtraciones (anonymous android.os.AsyncTask)

La advertencia completa es:

Esta clase AsyncTask debe ser estática o pueden producirse filtraciones (anonymous android.os.AsyncTask) Un campo estático tendrá una fuga de contextos. Las clases internas no estáticas tienen una referencia implícita a su clase externa. Si esa clase externa es, por ejemplo, un Fragmento o Actividad, entonces esta referencia significa que el manejador / cargador / tarea de larga ejecución mantendrá una referencia a la actividad que evita que obtenga basura recolectada. De manera similar, las referencias directas de campo a actividades y fragmentos de estas instancias de ejecución más larga pueden causar filtraciones. Las clases de ViewModel nunca deben apuntar a Vistas o Contextos que no sean de aplicación.

Este es mi código:

new AsyncTask(){ @Override protected Void doInBackground(Void... params) { runOnUiThread(new Runnable() { @Override public void run() { mAdapter.notifyDataSetChanged(); } }); return null; } }.execute(); 

¿Cómo puedo corregir esto?

Las clases internas no estáticas contienen una referencia a la clase contenedora. Cuando declara AsyncTask como una clase interna, puede vivir más tiempo que la clase de Activity que lo contiene. Esto se debe a la referencia implícita a la clase contenedora. Esto evitará que la actividad sea recogida de basura, de ahí la pérdida de memoria.

Para resolver su problema, use una clase anidada estática en lugar de una clase anónima, local e interna o use una clase de nivel superior.

Cómo usar una clase AsyncTask interna estática

Para evitar fugas, puede hacer que la clase interna estética. El problema con eso, sin embargo, es que ya no tiene acceso a las vistas de la interfaz de usuario de la actividad o las variables de miembro. Puede pasar una referencia al Context pero luego corre el mismo riesgo de una pérdida de memoria. (Android no puede recolectar basura de la actividad después de que se cierra si la clase AsyncTask tiene una referencia fuerte). La solución es hacer una referencia débil a la actividad (o cualquier Context que necesite).

 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 { private WeakReference 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; } } } 

Notas

  • Hasta donde yo sé, este tipo de peligro de pérdida de memoria siempre ha sido cierto, pero solo comencé a ver la advertencia en Android Studio 3.0. Muchos de los tutoriales principales de AsyncTask todavía no se ocupan de eso (ver aquí , aquí , aquí y aquí ).
  • También seguiría un procedimiento similar si su AsyncTask fuera una clase de nivel superior. Una clase interna estática es básicamente la misma que una clase de nivel superior en Java.
  • Si no necesita la Actividad en sí pero aún desea el Contexto (por ejemplo, para mostrar un Toast ), puede pasar una referencia al contexto de la aplicación. En este caso, el constructor AsyncTask se vería así:

     private WeakReference appReference; MyTask(Application context) { appReference = new WeakReference<>(context); } 
  • Hay algunos argumentos para ignorar esta advertencia y simplemente usar la clase no estática. Después de todo, el AsyncTask está pensado para ser de muy corta duración (un par de segundos como mucho), y dará a conocer su referencia a la Actividad cuando termine de todos modos. Mira esto y esto .
  • Excelente artículo: Cómo perder un contexto: Handlers y clases internas

Esta clase AsyncTask debe ser estática o pueden producirse filtraciones porque

  • Cuando se destruye la actividad, sigue funcionando AsyncTask (tanto static como non-static )
  • Si la clase interna es una clase non-static ( AsyncTask ), tendrá referencia a la clase externa ( Activity ).
  • Si un objeto no tiene referencias, Garbage Collected y Garbage Collected lo lanzará. Si un objeto no se utiliza y Garbage Collected no puede liberarlo => leak memory

=> Si AsyncTask non-static es non-static , Activity no lanzará el evento si se destruye => leak

Solución para la interfaz de usuario de actualización después de hacer AsyncTask como clase estática sin fugas

1) Use WeakReference como @Suragch answer
2) Enviar y eliminar referencia de Activity a (desde) 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 { 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); } } }