Жизненный цикл активности,Жизненный цикл активности

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

В методах обратного вызова жизненного цикла вы можете объявить, как будет вести себя ваша активность, когда пользователь покидает ее и снова входит в нее. Например, если вы создаете проигрыватель потокового видео, вы можете приостановить воспроизведение видео и разорвать сетевое соединение, когда пользователь переключается на другое приложение. Когда пользователь вернется, вы можете повторно подключиться к сети и позволить пользователю возобновить воспроизведение видео с того же места.

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

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

В этом документе подробно объясняется жизненный цикл активности. Документ начинается с описания парадигмы жизненного цикла. Далее объясняется каждый из обратных вызовов: что происходит внутри во время их выполнения и что вам нужно реализовать во время них.

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

Сведения об управлении жизненными циклами, включая рекомендации по передовым практикам, см. в разделах «Обработка жизненных циклов с помощью компонентов, учитывающих жизненный цикл» , и «Сохранение состояний пользовательского интерфейса» . Чтобы узнать, как спроектировать надежное приложение производственного качества, используя действия в сочетании с компонентами архитектуры, см. Руководство по архитектуре приложения .

Концепции жизненного цикла деятельности

Для навигации по переходам между этапами жизненного цикла активности класс Activity предоставляет базовый набор из шести обратных вызовов: onCreate() , onStart() , onResume() , onPause() , onStop() и onDestroy() . Система вызывает каждый из этих обратных вызовов, когда действие переходит в новое состояние.

На рисунке 1 представлено визуальное представление этой парадигмы.

Рисунок 1. Упрощенная иллюстрация жизненного цикла активности.

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

Если пользователь возвращается к действию, оно возобновляется с того места, на котором он остановился. За некоторыми исключениями, приложениям запрещено запускать действия при работе в фоновом режиме .

Вероятность того, что система завершит данный процесс вместе с действиями в нем, зависит от состояния действия в данный момент. Дополнительную информацию о связи между состоянием и уязвимостью к изгнанию см. в разделе о состоянии активности и изгнании из памяти .

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

Обратные вызовы жизненного цикла

В этом разделе представлена ​​концептуальная информация и информация о реализации методов обратного вызова, используемых в жизненном цикле действия.

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

onCreate()

Вы должны реализовать этот обратный вызов, который срабатывает, когда система впервые создает действие. При создании действия действие переходит в состояние Создано . В методе onCreate() выполните базовую логику запуска приложения, которая происходит только один раз за весь срок действия активности.

Например, ваша реализация onCreate() может привязывать данные к спискам, связывать действие с ViewModel и создавать экземпляры некоторых переменных области класса. Этот метод получает параметр savedInstanceState , который представляет собой объект Bundle , содержащий ранее сохраненное состояние действия. Если действие никогда раньше не существовало, значение объекта Bundle равно нулю.

Если у вас есть компонент, учитывающий жизненный цикл, который подключен к жизненному циклу вашей активности, он получает событие ON_CREATE . Метод, помеченный @OnLifecycleEvent , вызывается, чтобы ваш компонент, учитывающий жизненный цикл, мог выполнить любой код настройки, необходимый для созданного состояния.

В следующем примере метода onCreate() показаны основные настройки действия, такие как объявление пользовательского интерфейса (определенного в файле макета XML), определение переменных-членов и настройка некоторых элементов пользовательского интерфейса. В этом примере файл макета XML передает идентификатор ресурса файла R.layout.main_activity в setContentView() .

Котлин

lateinit var textView: TextView

// Some transient state for the activity instance.
var gameState: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    // Call the superclass onCreate to complete the creation of
    // the activity, like the view hierarchy.
    super.onCreate(savedInstanceState)

    // Recover the instance state.
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)

    // Set the user interface layout for this activity.
    // The layout is defined in the project res/layout/main_activity.xml file.
    setContentView(R.layout.main_activity)

    // Initialize member TextView so it is available later.
    textView = findViewById(R.id.text_view)
}

// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

// Invoked when the activity might be temporarily destroyed; save the instance state here.
override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    // Call superclass to save any view hierarchy.
    super.onSaveInstanceState(outState)
}

Ява

TextView textView;

// Some transient state for the activity instance.
String gameState;

@Override
public void onCreate(Bundle savedInstanceState) {
    // Call the superclass onCreate to complete the creation of
    // the activity, like the view hierarchy.
    super.onCreate(savedInstanceState);

    // Recover the instance state.
    if (savedInstanceState != null) {
        gameState = savedInstanceState.getString(GAME_STATE_KEY);
    }

    // Set the user interface layout for this activity.
    // The layout is defined in the project res/layout/main_activity.xml file.
    setContentView(R.layout.main_activity);

    // Initialize member TextView so it is available later.
    textView = (TextView) findViewById(R.id.text_view);
}

// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    textView.setText(savedInstanceState.getString(TEXT_VIEW_KEY));
}

// Invoked when the activity might be temporarily destroyed; save the instance state here.
@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putString(GAME_STATE_KEY, gameState);
    outState.putString(TEXT_VIEW_KEY, textView.getText());

    // Call superclass to save any view hierarchy.
    super.onSaveInstanceState(outState);
}

В качестве альтернативы определению XML-файла и передаче его в setContentView() вы можете создавать новые объекты View в коде активности и строить иерархию представлений, вставляя новые объекты View в ViewGroup . Затем вы используете этот макет, передавая корневую ViewGroup в setContentView() . Дополнительные сведения о создании пользовательского интерфейса см. в документации по пользовательскому интерфейсу .

Ваша активность не остается в состоянии «Создано». После завершения выполнения метода onCreate() действие переходит в состояние Started , и система быстро последовательно вызывает методы onStart() и onResume() .

onStart()

Когда активность переходит в состояние «Запущено», система вызывает onStart() . Этот вызов делает действие видимым для пользователя, поскольку приложение готовится к тому, чтобы действие вышло на передний план и стало интерактивным. Например, в этом методе инициализируется код, поддерживающий пользовательский интерфейс.

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

Метод onStart() завершается быстро, и, как и в состоянии Created, действие не остается в состоянии Started. После завершения этого обратного вызова действие переходит в состояние возобновления , и система вызывает метод onResume() .

onResume()

Когда действие переходит в состояние «Возобновлено», оно выходит на передний план, и система вызывает обратный вызов onResume() . Это состояние, в котором приложение взаимодействует с пользователем. Приложение остается в этом состоянии до тех пор, пока что-то не отвлечет внимание от приложения, например, когда устройство принимает телефонный звонок, пользователь переходит к другому действию или выключается экран устройства.

Когда действие переходит в состояние «Возобновлено», любой компонент с поддержкой жизненного цикла, привязанный к жизненному циклу действия, получает событие ON_RESUME . Здесь компоненты жизненного цикла могут включать любые функции, которые необходимо запускать, пока компонент виден и на переднем плане, например, запуск предварительного просмотра камеры.

Когда происходит прерывающее событие, действие переходит в состояние «Приостановлено» , и система вызывает обратный вызов onPause() .

Если активность возвращается в состояние «Возобновлено» из состояния «Приостановлено», система еще раз вызывает метод onResume() . По этой причине реализуйте onResume() для инициализации компонентов, которые вы освобождаете во время onPause() , и для выполнения любых других инициализаций, которые должны происходить каждый раз, когда действие переходит в состояние Resumed.

Вот пример компонента с учетом жизненного цикла, который обращается к камере, когда компонент получает событие ON_RESUME :

Котлин

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }
    ...
}

Ява

public class CameraComponent implements LifecycleObserver {

    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void initializeCamera() {
        if (camera == null) {
            getCamera();
        }
    }
    ...
}

Приведенный выше код инициализирует камеру, как только LifecycleObserver получает событие ON_RESUME . Однако в многооконном режиме ваша активность может быть полностью видна, даже если она находится в состоянии паузы. Например, когда приложение находится в многооконном режиме и пользователь касается окна, которое не содержит ваших действий, ваши действия переходят в состояние «Приостановлено».

Если вы хотите, чтобы камера была активна только при возобновлении работы приложения (видна и активна на переднем плане), инициализируйте камеру после события ON_RESUME , показанного ранее. Если вы хотите, чтобы камера оставалась активной, пока действие приостановлено, но видимо, например, в многооконном режиме, инициализируйте камеру после события ON_START .

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

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

Независимо от того, в каком событии создания вы решите выполнить операцию инициализации, обязательно используйте соответствующее событие жизненного цикла для освобождения ресурса. Если вы инициализируете что-то после события ON_START , отпустите или завершите его после события ON_STOP . Если вы инициализируете после события ON_RESUME , отпустите после события ON_PAUSE .

В приведенном выше фрагменте кода код инициализации камеры помещается в компонент, учитывающий жизненный цикл. Вместо этого вы можете поместить этот код непосредственно в обратные вызовы жизненного цикла активности, такие как onStart() и onStop() , но мы не рекомендуем этого делать. Добавление этой логики в независимый компонент, учитывающий жизненный цикл, позволяет повторно использовать компонент в нескольких действиях без необходимости дублировать код. Чтобы узнать, как создать компонент с учетом жизненного цикла, см. раздел «Обработка жизненных циклов с помощью компонентов с учетом жизненного цикла» .

onPause()

Система вызывает этот метод как первый признак того, что пользователь покидает вашу активность, хотя это не всегда означает, что активность уничтожается. Это указывает на то, что действие больше не находится на переднем плане, но оно все еще видно, если пользователь находится в многооконном режиме. Существует несколько причин, по которым действие может перейти в это состояние:

  • Событие, которое прерывает выполнение приложения, как описано в разделе об обратном вызове onResume() , приостанавливает текущую активность. Это самый распространенный случай.
  • В многооконном режиме фокус всегда находится только на одном приложении, а все остальные приложения система приостанавливает.
  • Открытие нового полупрозрачного действия, например диалога, приостанавливает действие, которое оно охватывает. Пока действие частично видно, но не в фокусе, оно остается приостановленным.

Когда действие переходит в состояние «Приостановлено», любой компонент с поддержкой жизненного цикла, привязанный к жизненному циклу действия, получает событие ON_PAUSE . Здесь компоненты жизненного цикла могут остановить любую функциональность, которую не нужно запускать, пока компонент не находится на переднем плане, например остановку предварительного просмотра камеры.

Используйте метод onPause() , чтобы приостановить или настроить операции, которые не могут продолжаться или могут продолжаться на модерации, пока Activity находится в состоянии паузы и которое вы ожидаете возобновить в ближайшее время.

Вы также можете использовать метод onPause() для освобождения системных ресурсов, дескрипторов датчиков (например, GPS) или любых ресурсов, влияющих на срок службы батареи, пока ваша деятельность приостановлена ​​и они не нужны пользователю.

Однако, как упоминалось в разделе о onResume() , приостановленное действие может по-прежнему быть полностью видимым, если приложение находится в многооконном режиме. Рассмотрите возможность использования onStop() вместо onPause() , чтобы полностью освободить или настроить ресурсы и операции, связанные с пользовательским интерфейсом, для лучшей поддержки многооконного режима.

Следующий пример реакции LifecycleObserver на событие ON_PAUSE является аналогом предыдущего примера события ON_RESUME , освобождая камеру, которая инициализируется после получения события ON_RESUME :

Котлин

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun releaseCamera() {
        camera?.release()
        camera = null
    }
    ...
}

Ява

public class JavaCameraComponent implements LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void releaseCamera() {
        if (camera != null) {
            camera.release();
            camera = null;
        }
    }
    ...
}

В этом примере код выпуска камеры размещается после получения события ON_PAUSE LifecycleObserver .

Выполнение onPause() очень короткое и не обязательно дает достаточно времени для выполнения операций сохранения. По этой причине не используйте onPause() для сохранения данных приложения или пользователя, выполнения сетевых вызовов или выполнения транзакций базы данных. Такая работа может не завершиться до завершения метода.

Вместо этого выполняйте операции отключения при большой нагрузке во время onStop() . Дополнительную информацию о подходящих операциях для выполнения во время onStop() см. в следующем разделе. Подробнее о сохранении данных смотрите в разделе о сохранении и восстановлении состояния .

Завершение метода onPause() не означает, что действие выходит из состояния Paused. Скорее, активность остается в этом состоянии до тех пор, пока она не возобновится или не станет полностью невидимой для пользователя. Если активность возобновляется, система еще раз вызывает обратный вызов onResume() .

Если действие возвращается из состояния «Приостановлено» в состояние «Возобновлено», система сохраняет экземпляр Activity в памяти, вызывая этот экземпляр, когда система вызывает onResume() . В этом сценарии вам не нужно повторно инициализировать компоненты, созданные во время любого из методов обратного вызова, ведущих к состоянию Resumed. Если активность становится полностью невидимой, система вызывает onStop() .

onStop()

Когда ваша активность больше не видна пользователю, она переходит в состояние «Остановлено» , и система вызывает обратный вызов onStop() . Это может произойти, когда недавно запущенное действие занимает весь экран. Система также вызывает onStop() , когда действие завершается и приближается к завершению.

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

В методе onStop() освободите или настройте ненужные ресурсы, пока приложение не видно пользователю. Например, ваше приложение может приостанавливать анимацию или переключаться с детального на крупномасштабное обновление местоположений. Использование onStop() вместо onPause() означает, что работа, связанная с пользовательским интерфейсом, продолжается, даже когда пользователь просматривает вашу активность в многооконном режиме.

Кроме того, используйте onStop() для выполнения относительно ресурсоемких операций выключения процессора. Например, если вы не можете найти лучшее время для сохранения информации в базе данных, вы можете сделать это во время onStop() . В следующем примере показана реализация onStop() , которая сохраняет содержимое черновой заметки в постоянном хранилище:

Котлин

override fun onStop() {
    // Call the superclass method first.
    super.onStop()

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    val values = ContentValues().apply {
        put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
        put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
    }

    // Do this update in background on an AsyncQueryHandler or equivalent.
    asyncQueryHandler.startUpdate(
            token,     // int token to correlate calls
            null,      // cookie, not used here
            uri,       // The URI for the note to update.
            values,    // The map of column names and new values to apply to them.
            null,      // No SELECT criteria are used.
            null       // No WHERE columns are used.
    )
}

Ява

@Override
protected void onStop() {
    // Call the superclass method first.
    super.onStop();

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());

    // Do this update in background on an AsyncQueryHandler or equivalent.
    asyncQueryHandler.startUpdate (
            mToken,  // int token to correlate calls
            null,    // cookie, not used here
            uri,    // The URI for the note to update.
            values,  // The map of column names and new values to apply to them.
            null,    // No SELECT criteria are used.
            null     // No WHERE columns are used.
    );
}

В предыдущем примере кода напрямую используется SQLite. Однако мы рекомендуем использовать Room — библиотеку персистентности, которая обеспечивает уровень абстракции поверх SQLite. Чтобы узнать больше о преимуществах использования Room и о том, как реализовать Room в своем приложении, см. руководство по библиотеке Room Persistence .

Когда ваша активность переходит в состояние «Остановлено», объект Activity остается резидентным в памяти: он сохраняет всю информацию о состоянии и членах, но не присоединяется к оконному менеджеру. Когда деятельность возобновляется, он вспоминает эту информацию.

Вам не нужно повторно инициализировать компоненты, созданные во время любого из методов обратного вызова, ведущих к состоянию Resumed. Система также отслеживает текущее состояние каждого объекта View в макете, поэтому, если пользователь вводит текст в виджет EditText , это содержимое сохраняется, поэтому вам не нужно его сохранять и восстанавливать.

Примечание. Если ваша активность остановлена, система может уничтожить процесс, содержащий это действие, если системе необходимо восстановить память. Даже если система уничтожает процесс, пока действие остановлено, система все равно сохраняет состояние объектов View , таких как текст в виджете EditText , в Bundle — блоке пар ключ-значение — и восстанавливает их, если пользователь возвращается к действию. Подробнее о восстановлении активности, к которой возвращается пользователь, смотрите в разделе о сохранении и восстановлении состояния .

Из состояния «Остановлено» действие либо возвращается для взаимодействия с пользователем, либо действие завершается и исчезает. Если активность возвращается, система вызывает onRestart() . Если Activity завершено, система вызывает onDestroy() .

onDestroy()

onDestroy() вызывается перед уничтожением активности. Система вызывает этот обратный вызов по одной из двух причин:

  1. Действие завершается из-за того, что пользователь полностью закрыл действие или из-за вызова finish() для действия.
  2. Система временно прекращает активность из-за изменения конфигурации, например поворота устройства или перехода в многооконный режим.

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

Вместо того, чтобы помещать логику в свою Activity , чтобы определить, почему она уничтожается, используйте объект ViewModel , чтобы содержать соответствующие данные представления для вашей Activity . Если Activity создается заново из-за изменения конфигурации, ViewModel не нужно ничего делать, поскольку он сохраняется и передается следующему экземпляру Activity .

Если Activity не воссоздается заново, то у ViewModel вызывается метод onCleared() , с помощью которого он может очистить любые необходимые данные перед их уничтожением. Вы можете отличить эти два сценария с помощью метода isFinishing() .

Если действие завершается, onDestroy() — это последний обратный вызов жизненного цикла, который получает действие. Если onDestroy() вызывается в результате изменения конфигурации, система немедленно создает новый экземпляр действия, а затем вызывает onCreate() для этого нового экземпляра в новой конфигурации.

Обратный вызов onDestroy() освобождает все ресурсы, не освобожденные предыдущими обратными вызовами, такими как onStop() .

Состояние активности и выброс из памяти

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

Вероятность быть убитым Состояние процесса Конечное состояние активности
Самый низкий Передний план (имеется или собирается сфокусироваться) Возобновлено
Низкий Видимый (без фокуса) Начато/Приостановлено
Выше Фон (невидимый) Остановлено
Самый высокий Пустой Разрушен

Таблица 1. Связь между жизненным циклом процесса и состоянием активности.

Система никогда не прекращает действие напрямую, чтобы освободить память. Вместо этого он убивает процесс, в котором выполняется действие, уничтожая не только действие, но и все остальное, выполняющееся в этом процессе. Чтобы узнать, как сохранить и восстановить состояние пользовательского интерфейса вашего действия в случае прекращения процесса, инициированного системой, см. раздел о сохранении и восстановлении состояния .

Пользователь также может завершить процесс, используя Диспетчер приложений в разделе «Настройки», чтобы закрыть соответствующее приложение.

Дополнительные сведения о процессах см. в разделе Обзор процессов и потоков .

Сохранение и восстановление временного состояния пользовательского интерфейса.

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

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

Когда системные ограничения уничтожают действие, сохраните временное состояние пользовательского интерфейса, используя комбинацию ViewModel , onSaveInstanceState() и/или локального хранилища. Чтобы узнать больше об ожиданиях пользователей по сравнению с поведением системы и о том, как наилучшим образом сохранить сложные данные о состоянии пользовательского интерфейса при инициированной системой активности и прекращении процесса, см. раздел Сохранение состояний пользовательского интерфейса .

В этом разделе описывается, что такое состояние экземпляра и как реализовать метод onSaveInstance() , который является обратным вызовом самого действия. Если ваши данные пользовательского интерфейса легкие, вы можете использовать только onSaveInstance() для сохранения состояния пользовательского интерфейса как при изменении конфигурации, так и при завершении процесса, инициируемом системой. Но поскольку onSaveInstance() требует затрат на сериализацию/десериализацию, в большинстве случаев вы используете и ViewModel , и onSaveInstance() , как описано в разделе «Сохранение состояний пользовательского интерфейса» .

Примечание. Чтобы узнать больше об изменениях конфигурации, о том, как при необходимости ограничить воссоздание активности и как реагировать на эти изменения конфигурации из системы View и Jetpack Compose, посетите страницу «Обработка изменений конфигурации» .

Состояние экземпляра

Существует несколько сценариев, в которых ваша активность уничтожается из-за нормального поведения приложения, например, когда пользователь нажимает кнопку «Назад» или когда ваша активность сигнализирует о своем собственном уничтожении, вызывая метод finish() .

Когда ваша активность уничтожается из-за того, что пользователь нажимает кнопку «Назад» или активность завершается сама по себе, концепция этого экземпляра Activity как для системы, так и для пользователя исчезает навсегда. В этих сценариях ожидания пользователя соответствуют поведению системы, и вам не нужно выполнять никакой дополнительной работы.

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

Сохраненные данные, которые система использует для восстановления предыдущего состояния, называются состоянием экземпляра . Это коллекция пар ключ-значение, хранящаяся в объекте Bundle . По умолчанию система использует состояние экземпляра Bundle для сохранения информации о каждом объекте View в макете активности, например текстовое значение, введенное в виджет EditText .

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

Примечание. Чтобы система Android могла восстановить состояние представлений в вашей активности, каждое представление должно иметь уникальный идентификатор, предоставляемый атрибутом android:id .

Объект Bundle не подходит для хранения большего количества данных, чем тривиальный, поскольку он требует сериализации в основном потоке и потребляет память системного процесса. Чтобы сохранить больше, чем очень небольшой объем данных, используйте комбинированный подход к сохранению данных, используя постоянное локальное хранилище, метод onSaveInstanceState() и класс ViewModel , как описано в разделе «Сохранение состояний пользовательского интерфейса» .

Сохраните простое и легкое состояние пользовательского интерфейса с помощью onSaveInstanceState().

Когда ваша активность начинает останавливаться, система вызывает метод onSaveInstanceState() , чтобы ваша активность могла сохранить информацию о состоянии в пакете состояний экземпляра. Реализация этого метода по умолчанию сохраняет временную информацию о состоянии иерархии представления действия, например текст в виджете EditText или положение прокрутки виджета ListView .

Чтобы сохранить дополнительную информацию о состоянии экземпляра для вашего действия, переопределите onSaveInstanceState() и добавьте пары ключ-значение в объект Bundle , который сохраняется на случай неожиданного уничтожения вашего действия. Когда вы переопределяете onSaveInstanceState() , вам нужно вызвать реализацию суперкласса, если вы хотите, чтобы реализация по умолчанию сохраняла состояние иерархии представлений. Это показано в следующем примере:

Котлин

override fun onSaveInstanceState(outState: Bundle?) {
    // Save the user's current game state.
    outState?.run {
        putInt(STATE_SCORE, currentScore)
        putInt(STATE_LEVEL, currentLevel)
    }

    // Always call the superclass so it can save the view hierarchy state.
    super.onSaveInstanceState(outState)
}

companion object {
    val STATE_SCORE = "playerScore"
    val STATE_LEVEL = "playerLevel"
}

Ява

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
// ...


@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state.
    savedInstanceState.putInt(STATE_SCORE, currentScore);
    savedInstanceState.putInt(STATE_LEVEL, currentLevel);

    // Always call the superclass so it can save the view hierarchy state.
    super.onSaveInstanceState(savedInstanceState);
}

Примечание. onSaveInstanceState() не вызывается, когда пользователь явно закрывает действие или в других случаях, когда вызывается finish() .

Чтобы сохранить постоянные данные, такие как пользовательские настройки или данные для базы данных, используйте соответствующие возможности, когда ваша деятельность находится на переднем плане. Если такой возможности нет, сохраните постоянные данные во время метода onStop() .

Восстановить состояние пользовательского интерфейса активности, используя сохраненное состояние экземпляра

Когда ваша активность воссоздается после того, как она была ранее уничтожена, вы можете восстановить сохраненное состояние экземпляра из Bundle , который система передает вашей активности. Оба метода обратного вызова onCreate() и onRestoreInstanceState() получают один и тот же Bundle , содержащий информацию о состоянии экземпляра.

Поскольку метод onCreate() вызывается независимо от того, создает ли система новый экземпляр вашей активности или воссоздает предыдущий, вам необходимо проверить, имеет ли состояние Bundle значение null, прежде чем пытаться его прочитать. Если оно равно нулю, система создает новый экземпляр действия вместо восстановления предыдущего, который был уничтожен.

Следующий фрагмент кода показывает, как можно восстановить некоторые данные состояния в onCreate() :

Котлин

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState) // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance.
    if (savedInstanceState != null) {
        with(savedInstanceState) {
            // Restore value of members from saved state.
            currentScore = getInt(STATE_SCORE)
            currentLevel = getInt(STATE_LEVEL)
        }
    } else {
        // Probably initialize members with default values for a new instance.
    }
    // ...
}

Ява

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance.
    if (savedInstanceState != null) {
        // Restore value of members from saved state.
        currentScore = savedInstanceState.getInt(STATE_SCORE);
        currentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance.
    }
    // ...
}

Вместо восстановления состояния во время onCreate() вы можете реализовать onRestoreInstanceState() , который система вызывает после метода onStart() . Система вызывает onRestoreInstanceState() только в том случае, если существует сохраненное состояние для восстановления, поэтому вам не нужно проверять, является ли Bundle нулевым.

Котлин

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    // Always call the superclass so it can restore the view hierarchy.
    super.onRestoreInstanceState(savedInstanceState)

    // Restore state members from saved instance.
    savedInstanceState?.run {
        currentScore = getInt(STATE_SCORE)
        currentLevel = getInt(STATE_LEVEL)
    }
}

Ява

public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy.
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from saved instance.
    currentScore = savedInstanceState.getInt(STATE_SCORE);
    currentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

Внимание: всегда вызывайте реализацию onRestoreInstanceState() суперкласса, чтобы реализация по умолчанию могла восстановить состояние иерархии представлений.

Навигация между действиями

Приложение, скорее всего, будет входить в действие и выходить из него, возможно, много раз в течение срока службы приложения, например, когда пользователь нажимает кнопку «Назад» на устройстве или действие запускает другое действие.

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

Начало одного действия из другого

Часто в какой-то момент одному действию необходимо запустить другое действие. Такая необходимость возникает, например, когда приложению необходимо перейти с текущего экрана на новый.

В зависимости от того, хочет ли ваша активность получить результат от новой активности, которую она собирается запустить, вы запускаете новую активность, используя либо метод startActivity() , либо метод startActivityForResult() . В любом случае вы передаете объект Intent .

Объект Intent указывает либо конкретное действие, которое вы хотите начать, либо описывает тип действия, которое вы хотите выполнить. Система подберет для вас подходящее занятие, которое может быть даже из другого приложения. Объект Intent также может содержать небольшие объемы данных, которые будут использоваться запущенным действием. Дополнительные сведения о классе Intent см. в разделе Intents и Intent Filters .

стартактивность()

Если вновь запущенному действию не требуется возвращать результат, текущее действие может запустить его, вызвав метод startActivity() .

При работе в собственном приложении часто требуется просто запустить известное действие. Например, в следующем фрагменте кода показано, как запустить действие под названием SignInActivity .

Котлин

val intent = Intent(this, SignInActivity::class.java)
startActivity(intent)

Ява

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

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

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

Котлин

val intent = Intent(Intent.ACTION_SEND).apply {
    putExtra(Intent.EXTRA_EMAIL, recipientArray)
}
startActivity(intent)

Ява

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

Дополнительный EXTRA_EMAIL , добавленный к намерению, представляет собой строковый массив адресов электронной почты, на которые должно быть отправлено электронное письмо. Когда приложение электронной почты отвечает на это намерение, оно считывает массив строк, предоставленный в дополнительном файле, и помещает адреса в поле «Кому» формы составления электронного письма. В этой ситуации начинается активность почтового приложения, а когда пользователь заканчивает работу, ваша активность возобновляется.

НачатьАктивитиФорРезультат()

Иногда вам нужно получить результат от действия после его завершения. Например, вы можете запустить действие, которое позволит пользователю выбрать человека в списке контактов. Когда он заканчивается, он возвращает человека, который был выбран. Для этого вы вызываете метод startActivityForResult(Intent, int) , где целочисленный параметр идентифицирует вызов.

Этот идентификатор предназначен для различения нескольких вызовов startActivityForResult(Intent, int) из одного и того же действия. Это не глобальный идентификатор, и он не может конфликтовать с другими приложениями или действиями. Результат возвращается через ваш метод onActivityResult(int, int, Intent) .

Когда дочернее действие завершается, оно может вызвать setResult(int) для возврата данных родительскому элементу. Дочернее действие должно предоставить код результата, который может быть стандартными результатами RESULT_CANCELED , RESULT_OK или любыми пользовательскими значениями, начинающимися с RESULT_FIRST_USER .

Кроме того, дочернее действие может при желании вернуть объект Intent , содержащий любые дополнительные данные, которые ему нужны. Родительское действие использует метод onActivityResult(int, int, Intent) вместе с целочисленным идентификатором, первоначально предоставленным родительским действием, для получения информации.

Если дочернее действие завершается сбоем по какой-либо причине, например, из-за сбоя, родительское действие получает результат с кодом RESULT_CANCELED .

Котлин

class MyActivity : Activity() {
    // ...

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
            // When the user center presses, let them pick a contact.
            startActivityForResult(
                    Intent(Intent.ACTION_PICK,Uri.parse("content://contacts")),
                    PICK_CONTACT_REQUEST)
            return true
        }
        return false
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
        when (requestCode) {
            PICK_CONTACT_REQUEST ->
                if (resultCode == RESULT_OK) {
                    // A contact was picked. Display it to the user.
                    startActivity(Intent(Intent.ACTION_VIEW, intent?.data))
                }
        }
    }

    companion object {
        internal val PICK_CONTACT_REQUEST = 0
    }
}

Ява

public class MyActivity extends Activity {
     // ...

     static final int PICK_CONTACT_REQUEST = 0;

     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             // When the user center presses, let them pick a contact.
             startActivityForResult(
                 new Intent(Intent.ACTION_PICK,
                 new Uri("content://contacts")),
                 PICK_CONTACT_REQUEST);
            return true;
         }
         return false;
     }

     protected void onActivityResult(int requestCode, int resultCode,
             Intent data) {
         if (requestCode == PICK_CONTACT_REQUEST) {
             if (resultCode == RESULT_OK) {
                 // A contact was picked. Display it to the user.
                 startActivity(new Intent(Intent.ACTION_VIEW, data));
             }
         }
     }
 }

Координационная деятельность

Когда одно действие запускает другое, в обоих жизненных циклах происходят переходы. Первое действие прекращает работу и переходит в состояние «Приостановлено» или «Остановлено», а другое действие создается. В случае, если эти действия совместно используют данные, сохраненные на диске или в другом месте, важно понимать, что первое действие не останавливается полностью до создания второго. Скорее, процесс запуска второго перекрывается с процессом остановки первого.

Порядок обратных вызовов жизненного цикла четко определен, особенно когда два действия находятся в одном процессе (другими словами, в одном и том же приложении) и одно запускает другое. Вот порядок операций, которые происходят, когда действие A запускает действие B:

  1. Выполняется метод onPause() действия A.
  2. Методы onCreate() , onStart() и onResume() действия B выполняются последовательно. Действие B теперь ориентировано на пользователя.
  3. Если действие A больше не отображается на экране, выполняется его метод onStop() .

Эта последовательность обратных вызовов жизненного цикла позволяет управлять переходом информации от одного действия к другому.