Como usar uma classe AsyncTask interna estática
Para evitar vazamentos, você pode tornar a classe interna estática. O problema disso, porém, é que você não tem mais acesso às visualizações da interface do usuário da atividade ou às variáveis de membro. Você pode passar uma referência ao, Context
mas então corre o mesmo risco de um vazamento de memória. (O Android não pode coletar a Atividade após o fechamento, se a classe AsyncTask tiver uma forte referência a ela.) A solução é fazer uma referência fraca à Atividade (ou o que Context
você precisar).
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
- Tanto quanto sei, esse tipo de perigo de vazamento de memória sempre foi verdadeiro, mas só comecei a ver o aviso no Android Studio 3.0. Muitos dos principais
AsyncTask
tutoriais por aí ainda não lidam com isso (veja aqui , aqui , aqui e aqui ).
- Você também seguiria um procedimento semelhante se
AsyncTask
fosse uma classe de nível superior. Uma classe interna estática é basicamente a mesma que uma classe de nível superior em Java.
Se você não precisa da Atividade em si, mas ainda deseja que o Contexto (por exemplo, exiba a Toast
), pode passar uma referência ao contexto do aplicativo. Nesse caso, o AsyncTask
construtor ficaria assim:
private WeakReference<Application> appReference;
MyTask(Application context) {
appReference = new WeakReference<>(context);
}
- Existem alguns argumentos para ignorar esse aviso e usar apenas a classe não estática. Afinal, o AsyncTask deve durar muito pouco (alguns segundos no máximo) e liberará sua referência à Atividade quando terminar de qualquer maneira. Veja isso e isso .
- Artigo excelente: Como vazar um contexto: manipuladores e classes internas
Kotlin
No Kotlin , não inclua a inner
palavra - chave da classe interna. Isso o torna estático por padrão.
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
}
}
}