Выпускать ресурсы в onPause вместо onDestroy

Это примерно POST-сотовая (т.е. Android 3.0+), а приведенные ниже цитаты взяты из https://developer.android.com/reference/android/app/Activity.html

В соответствии с жизненным циклом onStop и onDestroy являются гибкими, это означает:

Обратите внимание на столбец "Killable" в приведенной выше таблице - для этих методов которые отмечены как подлежащие уничтожению, после того, как этот метод возвращает процесс хостинга активности может быть убит системой в любое время без выполнения другой строки своего кода

  • Другими словами, onStop (наряду с другими, которые происходят перед этим событием) гарантируется для вызова, но в тот момент, когда метод возвращается, процесс может умереть, поэтому onDestroy не гарантируется.

    Другая цитата гласит:

    Для тех методов, которые не отмечены как подлежащие уничтожению, процесс активности не будет уничтожен системой, начиная с время, когда метод вызывается и продолжается после его возвращения.

    Далее

    Таким образом, активность находится в состоянии killable, например, между onPause() в начало onResume().

  • Но это означает, что не соответствует тому, что было сказано выше, , если это соответствует только PRE-honeycomb. Это не относится к POST-сотовой, правильно? Таким образом, в принципе, как onPause, так и onStop гарантированно будут называться.

  • Предполагая, что я освобожу только ресурс в onDestroy, это может привести к возможной утечке, поскольку onDestroy не может быть вызван, правильно?

  • Однако, может ли этот сценарий (т.е. onDestroy не вызываться) встречаться помимо, когда процесс уничтожается самим андроидом? Существуют ли другие сценарии, которые вызывают onDestroy не называться, , тем самым утечка ресурса.

  • Правда ли, что, когда Android уничтожит процесс, который будет уничтожен ресурсами, и утечка не может произойти (даже если мы явно не выпустили ресурс?).

Просьба представить подробную информацию о том, являются ли эти утверждения (1) (2) (3) (4) (5) правильными или нет.

Ответ 1

Прежде всего, дайте понять, что происходит с цитируемой вами документацией.

Следующие команды показывают вывод git blame файла Activity.java в AOSP:

$ cd $AOSP/frameworks/base
$ git blame ./core/java/android/app/Activity.java

Соответствующая часть вывода:

9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  363)  * <p>Note the "Killable" column in the above table -- for those methods that
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  364)  * are marked as being killable, after that method returns the process hosting the
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  365)  * activity may killed by the system <em>at any time</em> without another line
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  366)  * of its code being executed.  Because of this, you should use the
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  367)  * {@link #onPause} method to write any persistent data (such as user edits)
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  368)  * to storage.  In addition, the method
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  369)  * {@link #onSaveInstanceState(Bundle)} is called before placing the activity
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  370)  * in such a background state, allowing you to save away any dynamic instance
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  371)  * state in your activity into the given Bundle, to be later received in
550116576 (RoboErik                        2014-07-09 15:05:53 -0700  372)  * {@link #onCreate} if the activity needs to be re-created.
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  373)  * See the <a href="#ProcessLifecycle">Process Lifecycle</a>
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  374)  * section for more information on how the lifecycle of a process is tied
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  375)  * to the activities it is hosting.  Note that it is important to save
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  376)  * persistent data in {@link #onPause} instead of {@link #onSaveInstanceState}
5c40f3fcc (Daisuke Miyakawa                2011-02-15 13:24:36 -0800  377)  * because the latter is not part of the lifecycle callbacks, so will not
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  378)  * be called in every situation as described in its documentation.</p>
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  379)  *
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  380)  * <p class="note">Be aware that these semantics will change slightly between
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  381)  * applications targeting platforms starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB}
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  382)  * vs. those targeting prior platforms.  Starting with Honeycomb, an application
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  383)  * is not in the killable state until its {@link #onStop} has returned.  This
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  384)  * impacts when {@link #onSaveInstanceState(Bundle)} may be called (it may be
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  385)  * safely called after {@link #onPause()} and allows and application to safely
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  386)  * wait until {@link #onStop()} to save persistent state.</p>
0aae2d4e0 (Dianne Hackborn                 2010-12-07 23:51:29 -0800  387)  *
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  388)  * <p>For those methods that are not marked as being killable, the activity's
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  389)  * process will not be killed by the system starting from the time the method
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  390)  * is called and continuing after it returns.  Thus an activity is in the killable
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  391)  * state, for example, between after <code>onPause()</code> to the start of
9066cfe98 (The Android Open Source Project 2009-03-03 19:31:44 -0800  392)  * <code>onResume()</code>.</p>

Обратите внимание, что абзац, который обсуждает поведение после сотовой связи, был добавлен Dianne Hackborn в 2010-12-07, тогда как прилагаемые абзацы датируются 2009-03-03.

То, что он говорит нам, заключается в том, что Дайан добавила новый абзац без обновления остальной части javadoc, поэтому противоречие. К сожалению, это не редкость в Android.

На ваши вопросы:

1) На постсоветных версиях Android обе версии будут onResume() и onStop() (как указано Dianne Hackborn в ее дополнении к Activity javadoc).

2) Только на пре-Honeycomb будет вызван только onPause() (как указано в более ранней версии Activity javadoc)

3,4,5) onDestroy() не будет вызываться, только если процесс, на котором запущено все приложение, будет убит. Когда процесс убит, все ресурсы, выделенные ему, освобождаются, поэтому в этом случае нет риска утечки памяти.

Важное примечание: поскольку освобождение ресурсов в onDestroy() не приведет к утечке памяти, может показаться хорошей идеей поставить весь "освобождающий" код там. Однако он редко является оптимальным подходом. Зачем? Читайте ниже.

Когда Activity переходит в фоновый режим, он останавливается, но не уничтожается (обычно). Activity может оставаться в этом "остановленном" состоянии довольно долгое время и снова будет запущен, если пользователь вернется в приложение. Если вы отпускаете ресурсы в onDestroy(), который не вызывается по умолчанию, когда Activity переходит в фоновый режим, Activity будет удерживать эти ресурсы в состоянии остановки, тем самым увеличивая количество ресурсов, потребляемых вашим приложением фоновое состояние.

Когда у Android заканчивается память, он начинает убивать процессы, чтобы освободить память, потребляемую ими. Одним из наиболее важных соображений, принимаемых во внимание при выборе того, какие процессы убивать, является их потребление ресурсов. Таким образом, если ваше приложение хранится в ресурсах, а в фоновом режиме остановлено, у него будет больше шансов быть убитым Android.

Кроме того, мы, разработчики, должны убедиться, что мы создаем лучшие приложения для наших пользователей. Приложение, которое потребляет не минимальное количество пользовательских ресурсов телефона и аккумулятора, находясь в фоновом режиме, не является хорошим приложением. И пользователи узнают об этом!

Поэтому я настоятельно рекомендую высвободить все ресурсы в методе onStop(). Обычно я не переписываю методы onDestroy() в Activities и Fragments.

Следствие: Как отметил @Juan в своем комментарии, вышеупомянутое важное примечание имеет не менее важное, но не столь очевидное следствие: onStart() должен быть единственным методом, в котором находятся ресурсы выделены. Независимо от вашего определения "ресурсов", ни onCreate(), ни onResume() не должны выделять эти ресурсы.

Ответ 2

Я думаю, Василий дал отличный ответ. Тем не менее есть один небольшой, но важный момент, который отсутствует там, связанный с

  1. Правда ли, что, когда Android убивает процесс, что ресурсы будут уничтожены, и утечка не может произойти (даже если мы явно не выпустили ресурс?).

Ответ зависит от того, чего именно вы боитесь. Как отметил Василий, Android (основанный на Linux) - это современная безопасная ОС, которая гарантирует, что когда процесс будет убит, вся память будет выпущена, причем все открытые файлы, сетевые соединения и т.д. Также будут правильно закрыты. Таким образом, утечка ресурсов в обычном смысле не будет.

Тем не менее существует один потенциально проблематичный сценарий. Предположим, что у вас есть какая-то логика, которую вы выполняете, когда вы выпускаете какой-то ресурс, то есть что-то, что вы поместили бы в деструктор на С++ или в реализацию Closeable.close в Java. Например, у вас есть кеш в памяти, который задерживает запись в файл с пакетной загрузкой. Обычно вы скрываете этот кеш в памяти в onDestroy, и все работает нормально. Но когда процесс принудительно убит Android, onDestroy может не вызываться, и ваша логика очистки не выполняется, и вы можете потерять некоторые данные и/или иметь недопустимое состояние в своем постоянном хранилище. Поэтому, если у вас есть что-то подобное, вы должны обеспечить согласованность, выполнив такую ​​логику в более надежном месте: onPause или onStop.