Як використовувати статичний внутрішній клас AsyncTask
Щоб уникнути протікання, ви можете зробити внутрішній клас статичним. Проблема з цим полягає в тому, що у вас більше немає доступу до представлень інтерфейсу користувача або змінних учасників. Ви можете передати посилання на, Context
але тоді ви ризикуєте витік пам'яті. (Android не може зібрати сміття після його закриття, якщо клас AsyncTask має чіткі посилання на нього.) Рішення полягає в слабкому посиланні на Діяльність (або на все, що 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;
}
}
}
Примітки
- Наскільки я знаю, цей тип небезпеки витоку пам'яті завжди був вірним, але я почав бачити лише попередження в Android Studio 3.0. Багато головних
AsyncTask
навчальних посібників там досі не займаються цим (див. Тут , тут , тут і тут ).
- Ви б також дотримувались аналогічної процедури, якби ви
AsyncTask
були класом вищого рівня. Статичний внутрішній клас в основному такий же, як клас вищого рівня в Java.
Якщо вам не потрібна сама активність, але ви все ще хочете контекст (наприклад, для відображення a Toast
), ви можете передати посилання на контекст програми. У цьому випадку AsyncTask
конструктор виглядатиме так:
private WeakReference<Application> appReference;
MyTask(Application context) {
appReference = new WeakReference<>(context);
}
- Існує кілька аргументів для ігнорування цього попередження та просто використання нестатичного класу. Зрештою, AsyncTask призначений бути дуже короткочасним (найдовше пару секунд), і він звільнить своє посилання на Активність, коли все-таки закінчиться. Дивіться це і це .
- Відмінна стаття: Як просочити контекст: Обробники та внутрішні класи
Котлін
У Котліні просто не включайте inner
ключове слово для внутрішнього класу. Це робить його статичним за замовчуванням.
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
}
}
}