Как использовать статический внутренний класс AsyncTask
Чтобы предотвратить утечки, вы можете сделать внутренний класс статическим. Однако проблема в том, что у вас больше нет доступа к представлениям пользовательского интерфейса Activity или переменным-членам. Вы можете передать ссылку на, 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 предназначен для очень короткого срока действия (самое большее пару секунд), и он все равно выпустит ссылку на Activity, когда все равно завершится. Смотрите это и это .
- Отличная статья: Как вывести контекст: обработчики и внутренние классы
Котлин
В Kotlin просто не включайте 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
}
}
}