Область действия участника и Asynctask
Я инициализирую переменную-член в классе Activity
private String test = new String("A");
то я использую его для записи в журнал долгое время цикла в doInBackground()
метод анонимного AsyncTask, запущенного из Activity
new AsyncTask<Void, Void, Void>() {
@Override
protected void onPreExecute() {
}
@Override
protected void onPostExecute(Void result) {
}
@Override
protected Void doInBackground(Void... params) {
for (int j = 10; j >= 0; j--) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i("DOINBACKGROUND ", test);
}
}.execute();
Вопрос:
Когда я покидаю Activity, пока Asynctask
все еще выполняется, а после действия onDestroy()
выполняется, я вижу в журнале, что переменная-член все еще жива и не уничтожена. Может кто-нибудь объяснить мне, как это возможно?
ВОСЬМОЙ ВОПРОС:
переменная-член все еще жива, потому что даже после onDestroy()
она еще не сбрасывается из-за критериев gc и приоритета gc. Это нормально.
Но я сомневаюсь, что если
- переменная-член "test" (и контекст активности) не будет мусорной, пока ссылочная асинтекса не закончит свой материал, поэтому асинтеза может завершить ее
doInBackground()
всегда и, конечно, без сбоев (хотя и с временным потреблением памяти)
или вместо этого
- переменная-член "test" будет изнашиваться рано или поздно, независимо от того, запущена ли асинтеза, возможно, приведет к сбою asysnctask
Ответы
Ответ 1
Не путайте сбор мусора и жизненный цикл деятельности.
Объект может быть собран в мусор, как только все ссылки, отсылаемые к нему из объектов GC, исчезнут.
onDestroy()
является частью жизненного цикла активности. По сути, каркас выполняется с помощью активности и отказывается от каких-либо ссылок, которые он мог бы использовать для деятельности и связанных с ней ресурсов.
Когда вы создаете анонимный внутренний класс, он получает неявную ссылку на родительский объект. Другими словами, внутренний класс anon всегда является нестационарным внутренним классом. Эта родительская ссылка является ссылкой на ваш Activity
. Затем вы передаете объект задачи асинхронного вызова исполнителю с вызовом execute()
, и исполнитель сохраняет ссылку на асинхронную задачу до тех пор, пока это необходимо, также предотвращая сбор данных, на которые ссылается активность.
Таким образом, asynctask в моем примере с фрагментами завершит свою doInBackground() всегда и, конечно, без сбоев из-за NPE?
Да. Но учтите следующее:
-
Сделайте свои внутренние классы static
, если они специально не нуждаются в доступе к родительскому объекту. Поскольку внутренние классы anon всегда не static
, сделайте их не анонимными.
-
Смешивание асинхронных операций с объектами с отдельным жизненным циклом (например, действия или фрагменты) является хрупким, и его лучше избегать. Проблемы включают, например, аннулирование, подача результатов на ушедший объект и сохранение ссылок, предотвращающих GC, на дорогостоящие объекты, такие как действия.
Ответ 2
Прежде всего, onDestroy() происходит непосредственно перед уничтожением активности и просит диспетчера активности освободить все ресурсы, привязанные к этой активности. Это означает, что все ресурсы активности будут кандидатами на удаление gc. Однако это не заставляет gc удалять ресурсы из памяти, они просто кандидаты. Этот кандидат будет оцениваться gc на основе их размера, возраста, продолжительности, типа и т.д., И всякий раз, когда системе требуется больше места, она просит gc удалить кандидатов, и это делается на основе их оценок. Кандидат с более высоким счетом, скорее всего, будет удален первым.
Это заметно, когда вы видите крушение из ниоткуда, даже после выхода из приложения.
Вы можете увидеть этот сбой, если вы создадите другое действие и вызовите System.gc() на нем.
Приветствия
А.
Ответ 3
Членная переменная test
не будет возвращена сборщиком мусора, пока экземпляр Activity не будет собран.
Экземпляр Activity не будет возвращен сборщиком мусора до тех пор, пока AsyncTask не будет завершен, потому что AsyncTask содержит ссылку на экземпляр Activity.
Экземпляр AsyncTask не будет собираться мусором, пока он не завершит свою работу.
AsyncTask завершит метод doInBackground()
без сбоев. Конечно.
Ответ 4
AsyncTask не привязан к жизненному циклу Activity, который содержит его. Например, если вы запускаете AsyncTask внутри Activity и пользователь поворачивает устройство, активность будет уничтожена (и будет создан новый экземпляр Activity), но AsyncTask не умрет, а продолжит жизнь до завершения.
Затем, когда AsyncTask завершает работу, а не обновляет пользовательский интерфейс нового Activity, он обновляет прежний экземпляр Activity (то есть тот, в котором он был создан, но который больше не отображается!). Это может привести к исключению (типа java.lang.IllegalArgumentException: View не подключен к оконному менеджеру, если вы используете, например, findViewById, чтобы получить представление внутри Activity).
Также существует вероятность того, что это приведет к утечке памяти, так как AsyncTask поддерживает ссылку на Activity, что предотвращает сбор объектов Activity, пока AsyncTask остается в живых.
По этим причинам использование AsyncTasks для длительных фоновых задач - это, как правило, плохая идея. Скорее, для длительных фоновых задач должен использоваться другой механизм (например, служба).
Ответ 5
Я предполагаю, что задача async не является статической и содержит ссылку на включенную активность, что предотвращает сбор мусора.
Если вы хотите знать, как анонимный класс может вызвать утечку активности, обратитесь сюда - ссылка
Также вы можете попробовать повернуть устройства на несколько устройств и проверить, есть ли несколько экземпляров одного и того же действия, используя следующую команду и проверяя отсутствие действий.
adb shell dumpsys meminfo your.app.packagename
Applications Memory Usage (kB):
Uptime: 40343748 Realtime: 164852669
** MEMINFO in pid 16561 [samsung.svl.com.graph] **
Pss Private Private Swapped Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 5708 5664 16 2380 20480 8849 11630
Dalvik Heap 1163 972 136 27080 37459 29598 7861
Dalvik Other 604 604 0 4
Stack 288 288 0 0
Other dev 4 0 4 0
.so mmap 3569 992 72 2120
.apk mmap 39 0 0 0
.ttf mmap 0 0 0 0
.oat mmap 539 0 4 0
.art mmap 747 524 4 704
Other mmap 5 4 0 0
GL mtrack 10951 10951 0 0
Unknown 2260 2260 0 92
TOTAL 25877 22259 236 32380 57939 38447 19491
Objects
Views: 17 ViewRootImpl: 1
AppContexts: 3 **Activities: 1**
Assets: 3 AssetManagers: 3
Local Binders: 8 Proxy Binders: 23
Parcel memory: 3 Parcel count: 12
Death Recipients: 0 OpenSSL Sockets: 0
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0