Операции

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

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

Когда операция останавливается по причине запуска новой операции, для уведомления об изменении ее состояния используются методы обратного вызова жизненного цикла операции. Существует несколько таких методов, которые может принимать операция вследствие изменения своего состояния — создание операции, ее остановка, возобновление или уничтожение системой; также каждый обратный вызов представляет возможность выполнить определенное действие, подходящее для соответствующего изменения состояния. Например, в случае остановки операция должна освободить любые крупные объекты, например, подключение к сети или базе данных. При возобновлении операции вы можете повторно получить необходимые ресурсы и возобновить выполнение прерванных действий. Такие изменения состояния являются частью жизненного цикла операции.

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

Создание операции

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

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

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

Реализация пользовательского интерфейса

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

В Android предусмотрен набор уже готовых представлений, которые можно использовать для создания дизайна макета и его организации. Виджеты — это представления с визуальными (и интерактивными) элементами, например, кнопками, текстовыми полями, чекбоксами или просто изображениями. Макеты — это представления, полученные из класса ViewGroup, обеспечивающие уникальную модель компоновки для своих дочерних представлений, таких как линейный макет, сетка или относительный макет. Также можно создать подкласс для классов View и ViewGroup (или воспользоваться существующими подклассами), чтобы создать собственные виджеты и макеты, и затем применить их к макету своей операции.

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

Подробные сведения о создании пользовательского интерфейса см. в статье Пользовательский интерфейс.

Объявление операции в манифесте

Чтобы операция стала доступна системе, ее необходимо объявить в файле манифеста. Для этого откройте файл манифеста и добавьте элемент <activity> в качестве дочернего для элемента <application>. Например:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

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

Дополнительные сведения об объявлении операции в манифесте см. в справке по элементу <activity>.

Использование фильтров намерений

Элемент <activity> также может задавать различные фильтры намерений — с помощью элемента <intent-filter> — для объявления того, как другие компоненты приложения могут активировать операцию.

При создании нового приложения с помощью инструментов Android SDK в заготовке операции, создаваемой автоматически, имеется фильтр намерений, который объявляет операцию. Эта операция реагирует на выполнение «основного» действия, и ее следует поместить в категорию переходсредства запуска. Фильтр намерений выглядит следующим образом.

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Элемент <action> указывает, что это «основная» точка входа в приложение. Элемент <category>указывает, что эту операцию следует указать в средстве запуска приложений системы (чтобы пользователи могли запускать эту операцию).

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

Однако, если вам необходимо, чтобы операция реагировала на неявные намерения, получаемые от других приложений (а также из вашего приложения), для операции необходимо определить дополнительные фильтры намерений. Для каждого типа намерения, на который необходимо реагировать, необходимо указать объект <intent-filter>, включающий элемент <action> и необязательный элемент <category> или <data> (или оба этих элемента). Эти элементы определяют тип намерения, на который может реагировать ваша операция.

Дополнительные сведения о том, как операции могут реагировать на намерения, приведены в статье Намерения и фильтры намерений.

Запуск операции

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

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

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

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

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

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

Запуск операции для получения результата

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

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

private void pickContact() {
    // Create an intent to "pick" a contact, as defined by the content provider URI
    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
    startActivityForResult(intent, PICK_CONTACT_REQUEST);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
        // Perform a query to the contact's content provider for the contact's name
        Cursor cursor = getContentResolver().query(data.getData(),
        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
        if (cursor.moveToFirst()) { // True if the cursor is not empty
            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
            String name = cursor.getString(columnIndex);
            // Do something with the selected contact's name...
        }
    }
}

В этом примере демонстрируется базовая логика, которой следует руководствоваться при использовании метода onActivityResult() для обработки результата выполнения операции. Первое условие проверяет, успешен ли запрос, и если он успешен, то результат для resultCode будет RESULT_OK; также проверяется, известен ли запрос, для которого получен этот результат, и в этом случае requestCode соответствует второму параметру, отправленному в метод startActivityForResult(). Здесь код обрабатывает результат выполнения операции путем запроса данных, возвращенных в Intent (параметр data).

При этом ContentResolver выполняет запрос к поставщику контента, который возвращает объект Cursor, обеспечивающий считывание запрошенных данных. Дополнительные сведения представлены в статье Поставщики контента.

Дополнительные сведения об использовании намерений см. в статье Намерения и фильтры намерений.

Завершение операции

Для завершения операции достаточно вызвать ее метод finish(). Также для завершения отдельной операции, запущенной ранее, можно вызвать метод finishActivity().

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

Управление жизненным циклом операций

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

Существует всего три состояния операции:

Возобновлена
Операция выполняется на переднем плане экрана и отображается для пользователя. (Это состояние также иногда называется «Выполняется».)
Приостановлена
На переднем фоне выполняется другая операция, которая отображается для пользователя, однако эта операция по-прежнему не скрыта. То есть поверх текущей операции отображается другая операция, частично прозрачная или не занимающая полностью весь экран. Приостановленная операция полностью активна (объект Activity по-прежнему находится в памяти, в нем сохраняются все сведения о состоянии и информация об элементах, и он также остается связанным с диспетчером окон), однако в случае острой нехватки памяти система может завершить ее.
Остановлена
Операция полностью перекрывается другой операцией (теперь она выполняется в фоновом режиме). Остановленная операция по-прежнему активна (объект Activity по-прежнему находится в памяти, в нем сохраняются все сведения о состоянии и информация об элементах, но объект больше не связан с диспетчером окон). Однако операция больше не видна пользователю, и в случае нехватки памяти система может завершить ее.

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

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

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

public class ExampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // The activity is being created.
    }
    @Override
    protected void onStart() {
        super.onStart();
        // The activity is about to become visible.
    }
    @Override
    protected void onResume() {
        super.onResume();
        // The activity has become visible (it is now "resumed").
    }
    @Override
    protected void onPause() {
        super.onPause();
        // Another activity is taking focus (this activity is about to be "paused").
    }
    @Override
    protected void onStop() {
        super.onStop();
        // The activity is no longer visible (it is now "stopped")
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // The activity is about to be destroyed.
    }
}

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

Вместе все эти методы определяют весь жизненный цикл операции. С помощью реализации этих методов можно отслеживать три вложенных цикла в жизненном цикле операции:

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

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

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

Рисунок 1. Жизненный цикл операции.

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

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

Метод Описание Завершаемый? Следующий
onCreate() Вызывается при первом создании операции. Здесь необходимо настроить все обычные статические элементы — создать представления, привязать данные и т. д. Этот метод передает объект Bundle, содержащий предыдущее состояние операции (если такое состояние было зафиксировано ранее; см. раздел Сохранение состояния операции).

За ним всегда следует метод onStart().

Нет onStart()
     onRestart() Вызывается после остановки операции непосредственно перед ее повторным запуском.

За ним всегда следует метод onStart().

Нет onStart()
onStart() Вызывается непосредственно перед тем, как операция становится видимой для пользователя.

За ним следует метод onResume(), если операция переходит на передний план, или метод onStop(), если она становится скрытой.

Нет onResume()
или
onStop()
     onResume() Вызывается непосредственно перед тем, как операция начинает взаимодействие с пользователем. На этом этапе операция находится в самом верху стека операций, и в нее поступают данные, вводимые пользователем.

За ним всегда следует метод onPause().

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

За ним следует либо метод onResume(), если операция возвращается на передний план, либо метод onStop(), если операция становится скрытой для пользователя.

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

За ним следует либо метод onRestart(), если операция возобновляет взаимодействие с пользователем, либо метод onDestroy(), если операция переходит в фоновый режим.

Да onRestart()
или
onDestroy()
onDestroy() Вызывается перед тем, как операция будет уничтожена. Это финальный вызов, который получает операция. Его можно вызвать либо по причине завершения операции (вызов метода finish()), либо ввиду временного уничтожения системой этого экземпляра операции с целью освободить место. Чтобы различить эти два сценария, используется метод isFinishing(). Да Ничего

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

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

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

Сохранение состояния операции

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

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

Прежде чем сделать операцию доступной для уничтожения, система вызывает метод onSaveInstanceState(). Система передает в этот метод объект Bundle, в котором можно сохранить информацию о состоянии операции в виде пар «имя-значение», используя для этого такие методы, как putString() и putInt(). Затем, если система завершает процесс вашего приложения и пользователь возвращается к вашей операции, система повторно создает операцию и передает объект Bundle в оба метода: onCreate() и onRestoreInstanceState(). С помощью любого из этих методов можно извлечь из объекта Bundle сохраненную информацию о состоянии операции и восстановить ее. Если такая информация отсутствует, то объект Bundle передается с нулевым значением (это происходит в случае, когда операция создается в первый раз).

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

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

Однако, даже если вы ничего не предпринимаете и не реализуете метод onSaveInstanceState(), часть состояния операции восстанавливается реализацией по умолчанию метода onSaveInstanceState() класса Activity. В частности, реализация по умолчанию вызывает соответствующий метод onSaveInstanceState() для каждого объекта View в макете, благодаря чему каждое представление может предоставлять ту информацию о себе, которую следует сохранить. Почти каждый виджет в платформе Android реализует этот метод необходимым для себя способом так, что любые видимые изменения в пользовательском интерфейсе автоматически сохраняются и восстанавливаются при повторном создании операции. Например, виджет EditText сохраняет любой текст, введенный пользователем, а виджет CheckBox сохраняет информацию о том, был ли установлен флажок. От вас требуется лишь указать уникальный идентификатор (с атрибутом android:id) для каждого виджета, состояние которого необходимо сохранить. Если виджету не присвоен идентификатор, то системе не удастся сохранить его состояние.

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

Поскольку реализация метода onSaveInstanceState() по умолчанию позволяет сохранить состояние пользовательского интерфейса, в случае , если вы переопределите метод с целью сохранить дополнительную информацию о состоянии, перед выполнением каких-либо действий вы всегда можете вызвать реализацию суперкласса для метода onSaveInstanceState(). Точно так же реализацию суперкласса onRestoreInstanceState() следует вызывать в случае ее переопределения, чтобы реализация по умолчанию могла сохранить состояния представлений.

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

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

Обработка изменений в конфигурации

Некоторые конфигурации устройств могут изменяться в режиме выполнения (например, ориентация экрана, доступность клавиатуры и язык). В таких случаях Android повторно создает выполняющуюся операцию (система сначала вызывает метод onDestroy(), а затем сразу же вызывает метод onCreate()). Такое поведение позволяет приложению учитывать новые конфигурации путем автоматической перезагрузки в приложение альтернативных ресурсов, которые вы предоставили (например, различные макеты для разных ориентаций и экранов разных размеров).

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

Лучший способ обработки такого перезапуска — сохранить и восстановить состояние операции с помощью методов onSaveInstanceState() и onRestoreInstanceState() (или onCreate()), как описано в предыдущем разделе.

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

Согласование операций

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

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

  1. Выполняется метод onPause() операции А.
  2. Последовательно выполняются методы onCreate(), onStart() и onResume() операции Б. (Теперь для пользователя отображается операция Б.)
  3. Затем, если операция A больше не отображается на экране, выполняется ее метод onStop().

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