Cómo usar una clase AsyncTask interna estática
Para evitar fugas, puede hacer que la clase interna sea estática. El problema con eso, sin embargo, es que ya no tiene acceso a las vistas de UI de la Actividad o las variables 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 una vez que se cierra si la clase AsyncTask tiene una fuerte referencia a ella). La solución es hacer una referencia débil a la Actividad (o lo Context
que sea 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<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;
}
}
}
Notas
- Hasta donde 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
AsyncTask
tutoriales principales aún no se tratan (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 lo mismo 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 a Toast
), puede pasar una referencia al contexto de la aplicación. En este caso, el AsyncTask
constructor se vería así:
private WeakReference<Application> appReference;
MyTask(Application context) {
appReference = new WeakReference<>(context);
}
- Existen algunos argumentos para ignorar esta advertencia y simplemente usar la clase no estática. Después de todo, AsyncTask está destinado a ser de muy corta duración (un par de segundos como máximo) y, de todos modos, liberará su referencia a la Actividad cuando termine. Ver esto y esto .
- Excelente artículo: Cómo filtrar un contexto: controladores y clases internas
Kotlin
En Kotlin simplemente no incluya la inner
palabra clave para la clase interna. Esto lo hace estático por defecto.
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
}
}
}