Предупреждение: этот класс AsyncTask должен быть статическим, иначе могут возникнуть утечки
Я получаю предупреждение в своем коде, в котором говорится:
Этот класс AsyncTask должен быть статическим, иначе могут возникнуть утечки (анонимный android.os.AsyncTask)
Полное предупреждение:
Этот класс AsyncTask должен быть статическим, иначе могут возникнуть утечки (анонимный android.os.AsyncTask). Статическое поле будет пропускать контексты. Нестатические внутренние классы имеют неявную ссылку на свой внешний класс. Если этот внешний класс является, например, Fragment или Activity, то эта ссылка означает, что долго выполняющийся обработчик / загрузчик / задача будет содержать ссылку на операцию, которая не позволяет собирать мусор. Точно так же прямые ссылки на поля на действия и фрагменты из этих более длительных экземпляров могут вызвать утечки. Классы ViewModel никогда не должны указывать на представления или контексты вне приложения.
Это мой код:
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... params) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
return null;
}
}.execute();
Как мне это исправить?
3 ответа
Нестатические внутренние классы содержат ссылку на содержащий класс. Когда вы объявляете AsyncTask
как внутренний класс, он может жить дольше, чем содержащий Activity
учебный класс. Это из-за неявной ссылки на содержащий класс. Это предотвратит сбор мусора, что приведет к утечке памяти.
Чтобы решить вашу проблему, используйте статический вложенный класс вместо анонимного, локального и внутреннего класса или класс верхнего уровня.
Как использовать статический внутренний класс 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. Если вам не нужна сама активность, но все еще требуется контекст (например, для отображения
Toast
), вы можете передать ссылку на контекст приложения. В этом случаеAsyncTask
Конструктор будет выглядеть так:private WeakReference<Application> appReference; MyTask(Application context) { appReference = new WeakReference<>(context); }
- Есть некоторые аргументы для игнорирования этого предупреждения и просто использования нестатического класса. В конце концов, AsyncTask предназначен для очень короткого срока службы (самое большее пару секунд), и он все равно выпустит свою ссылку на Activity, когда все равно завершится. Смотрите это и это.
- Отличная статья: Как извлечь контекст: обработчики и внутренние классы
это AsyncTask
класс должен быть статическим, иначе могут возникнуть утечки
- когда
Activity
уничтожен,AsyncTask
(и то и другоеstatic
или жеnon-static
) Все еще работает - Если внутренний класс
non-static
(AsyncTask
), он будет иметь ссылку на внешний класс (Activity
). - Если объект не имеет ссылок на него,
Garbage Collected
выпустит это. Если объект не используется иGarbage Collected
не может освободить его => утечка памяти
=> Если AsyncTask
является non-static
, Activity
не выпустит событие, оно уничтожено => утечка
Решение для обновления пользовательского интерфейса после создания AsyncTask как статический класс без утечки
1) Использование WeakReference
как ответ @Suragch
2) Отправить и удалить Activity
ссылка на (с) 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<Void, Void, Integer> {
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);
}
}
}