4 марта 2014 г.

Жизненный цикл приложения (Activity) в Android (часть 1)

Сразу приведу ссылки из альма-матер по этой очень важной теме:

http://developer.android.com/training/basics/activity-lifecycle/starting.html
http://developer.android.com/guide/components/activities.html
http://developer.android.com/guide/components/tasks-and-back-stack.html

Если что, можно все детально почитать по этим линкам

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

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

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

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

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

Процесс != Приложение (Процесс не равно Приложение)

Внутренне, каждый экран пользовательского интерфейса представляет собой класс Activity. Каждая Activity имеет свой собственный жизненный цикл. Приложение имеет одну или больше Activities плюс процесс Linux привязанный к нему. Замудрёно? Но это одна из фишек Андроид.

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

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

Как уже говорилось, приложения Android не могут контролировать свой жизненный цикл, ОС сама управляет всеми процессами и, как следствие, Активностями внутри них. При этом, состояние Активности помогает ОС определить приоритет родительского для этой Активности Приложения (Application). А приоритет Приложения влияет на то, с какой вероятности его работа (и работа дочерних Активностей) будет прервана системой.

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

И так, вводную часть по данной теме можно считать законченной.

А вот дальше, опять, начинается свистопляска. Везде в сети, по крайней мере в русскоязычном сегменте, говориться что жизненный цикл активности имеет четыре состояния:
  • Активное
  • Приостановленное
  • Остановленное
  • Некативное
В то время как на альма-матер в двух статьях упоминаются только ТРИ состояния жизненного цикла Активности:
  • Resumed (возобновленное) – в этом состоянии Активность (окно) находится на переднем плане и пользователь может взаимодействовать с ней (иногда это состояние называют как активное или рабочее – running state).
  • Paused (приостановленное) – в этом состоянии Активность, частично закрыта окном другой Активности. Такое состояние бывает когда полупрозрачные или плавающие диалоговые окна становятся активными и частично ее перекрывают. В приостановленном состоянии Активность не может принимать ввод данных от пользователя и так же не может выполнять любой код, но при этом она полностью жива (объект Активности остается в памяти и сохраняет всю информацию о своем состоянии а так же остается подключенным к Window Manager). В этом состоянии Активность может быть убита системой в случае острой нехватки памяти.
  • Stoped (остановленное) – в этом состоянии активность полностью скрыта, невидима пользователем и находится в фоновом режиме. Остановленная Активность, так же все еще жива (объект Активности остается в памяти и сохраняет всю информацию о своем состоянии, но уже не связан с Window Manager). В этом состоянии Активность может быть убита системой, как только возникнет необходимость в ресурсах.
Другие состояния (Created и Started) являются временными, и система быстро переходит от них к следующему состоянию, вызвав следующий этап жизненного цикла. То есть, как только вызывается метод onCreate(), сразу же после этого вызывается метод onStart(), после которого сразу же вызывается метод onResume().
Из схемы приведенной ниже хорошо видны все эти состояния и методы используемые для этого.

basic-lifecycle

От куда растут ноги у фольклора о четырех состояниях, я могу лишь предположить – это книжка “Android 2. Программирование приложений для планшетных компьютеров и смартфонов”, автор Рето Майер.

А дальше уже пошли перепечатки этого дела по всему рунету, и видимо мало кто удосужился почитать изначальные статьи: http://developer.android.com/guide/components/activities.html и http://developer.android.com/training/basics/activity-lifecycle/starting.html

Кроме того можно предположить, что под четвертым состоянием иногда считают метод onDestroy() – который полностью удаляет Активность из памяти. Но это уже не состояние жизни (LifeCycle), это, блин, состояние смерти.

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

Я изменил класс ActivityMain своего тестового приложения AP0001, добавив в него методы описанные выше, в которых вызывается класс Toast показывающий быстрые всплывающие сообщения. Собственно сам код:

package com.proandroid.ap0001;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;

public class ActivityMain extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.layout_main);
  Toast.makeText(this, R.string.strOnCreate, Toast.LENGTH_LONG).show();
 }

 @Override
 protected void onStart() {
  super.onStart();
  Toast.makeText(this, 0x7f050004, Toast.LENGTH_LONG).show();
 }

 @Override
 protected void onResume() {
  super.onResume();
  Toast.makeText(this, "onResume()", Toast.LENGTH_LONG).show();
 }

 @Override
 protected void onPause() {
  super.onPause();
  // Toast.makeText(this, R.string.strOnPause, Toast.LENGTH_LONG).show();
  Toast.makeText(this, "onPause()", Toast.LENGTH_LONG).show();
 }

 @Override
 protected void onRestart() {
  super.onRestart();
  Toast.makeText(this, "onRestart()", Toast.LENGTH_LONG).show();
 }

 @Override
 protected void onStop() {
  super.onStop();
  Toast.makeText(this, "onStop()", Toast.LENGTH_LONG).show();
 }

 @Override
 protected void onDestroy() {
  super.onDestroy();
  Toast.makeText(this, "onDestroy()", Toast.LENGTH_LONG).show();
 }

}

Чтобы использовать класс Toast его сперва необходимо импортировать в программу, строка 5.

В Eclipse можно сделать все необходимы импорты сочетанием клавишь Ctrl+Shift+O

Далее переопределяются методы для каждого состояния, строки 10, 12, 23, 29, 36, 42 и 48.

И в каждом методе выводится всплывающее сообщение о том какой метод используется, строки 13, 19, 25, 32, 38, 44 и 50. Строки 13 и 19 делают тоже самое, что и другие, просто я намеренно изменил их чтобы продемонстрировать, как можно обращаться к ресурсам приложения через класс R.java.

Строковые значения, которые выводятся классом Toast в строках 13 и 19, определены, в файле strings.xml. На них в свою очередь есть указатели в классе R.java. В строке 13 происходит обращение через полное имя переменной, а в строке 19 – через числовой идентификатор.

Act0001

Собственно на скрине и так все понятно, далее расписывать не зачем.

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

Запускаем приложение

Act0002

Act0003

Act0004

Теперь нажмем кнопочку Home

Act0005

Act0006

Затем снова щелкнем по иконке приложения

Act0007

Act0008

Act0009

Теория подтверждается практикой Улыбка

Теперь нажмем кнопку обратно

Act0005

Act0006


Act0010

Про Toast более подробно и понятно можно почитать здесь http://developer.alexanderklimov.ru/android/toast.php или же в альма-матер http://developer.android.com/reference/android/widget/Toast.html

Приведу в конце еще одну диаграмку жизненного цикла Активности, которая тоже не плохо поясняет как и чего:
Act0011

На этом эту часть пока закончим и двинемся дальше изучать эту очень важную тему.

3 комментария:

  1. Хорошая статья, но на последней картинке ошибка: onSaveInstanceState и onRestoreInstanceState нужно поменять местами.

    ОтветитьУдалить
    Ответы
    1. Да вы правы. Я эту картинку взял от куда-то из сети и не обратил на это внимания.

      Удалить
    2. Лень было рисовать самому. А надо бы.

      Удалить