Ключевым компонентом для создания визуального интерфейса в приложении Android является activity (активность). Нередко activity ассоциируется с отдельным экраном или окном приложения, а переключение между окнами будет происходить как перемещение от одной activity к другой. Приложение может иметь одну или несколько activity. Например, при создании проекта с пустой Activity в проект по умолчанию добавляется один класс Activity - MainActivity, с которого и начинается работа приложения:
public class MainActivity extends AppCompatActivity { // содержимое класса }
Все объекты activity представляют собой объекты класса android.app.Activity, которая содержит базовую функциональность для всех activity. В приложении из прошлой темы мы напрямую с этим классом не работали, а MainActivity наследовалась от класса AppCompatActivity. Однако сам класс AppCompatActivity, хоть и не напрямую, наследуется от базового класса Activity.
Все приложения Android имеют строго определенный системой жизненный цикл. При запуске пользователем приложения система дает этому приложению высокий приоритет. Каждое приложение запускается в виде отдельного процесса, что позволяет системе давать одним процессам более высокой приоритет, в отличие от других. Благодаря этому, например, при работе с одними приложениями Android позволяет не блокировать входящие звонки. После прекращения работы с приложением, система освобождает все связанные ресурсы и переводит приложение в разряд низкоприоритетного и закрывает его.
Все объекты activity, которые есть в приложении, управляются системой в виде стека activity, который называется back stack. При запуске новой activity она помещается поверх стека и выводится на экран устройства, пока не появится новая activity. Когда текущая activity заканчивает свою работу (например, пользователь уходит из приложения), то она удаляется из стека, и возобновляет работу та activity, которая ранее была второй в стеке.
После запуска activity проходит через ряд событий, которые обрабатываются системой и для обработки которых существует ряд обратных вызовов:
protected void onCreate(Bundle saveInstanceState); protected void onStart(); protected void onRestart(); protected void onResume(); protected void onPause(); protected void onStop(); protected void onDestroy();
Схематично взаимосвязь между всеми этими обратными вызовами можно представить следующим образом
onCreate - первый метод, с которого начинается выполнение activity. В этом методе activity переходит в состояние Created.
Этот метод обязательно должен быть определен в классе activity. В нем производится
первоначальная настройка activity. В частности, создаются объекты визуального интерфейса. Этот метод получает объект Bundle
,
который содержит прежнее состояние activity, если оно было сохранено. Если activity заново создается, то данный объект имеет значение null.
Если же activity уже ранее была создана, но находилась в приостановленном состоянии, то bundle содержит связанную с activity информацию.
После того, как метод onCreate() завершил выполнение, activity переходит в состояние Started
, и
и система вызывает метод onStart()
В методе onStart() осуществляется подготовка к выводу activity на экран устройства. Как правило, этот метод не требует переопределения, а всю работу производит встроенный код. После завершения работы метода activity отображается на экране, вызывается метод onResume, а activity переходит в состояние Resumed.
При вызове метода onResume
activity переходит в состояние Resumed
и отображается на экране устройства, и пользователь может с ней
взаимодействовать. И собственно activity остается в этом состоянии, пока она не потеряет фокус, например, вследствии переключения на другую activity или просто из-за
выключения экрана устройства.
Если пользователь решит перейти к другой activity, то система вызывает метод onPause, а activity переходит в состояние Paused
.
В этом методе можно освобождать используемые ресурсы, приостанавливать процессы,
например, воспроизведение аудио, анимаций, останавливать работу камеры (если она используется) и т.д., чтобы они меньше сказывались на производительность системы.
Но надо учитывать, что в этот состоянии activity по прежнему остается видимой на экране, и на работу данного метода отводится очень мало времени, поэтому не стоит здесь сохранять какие-то данные, особенно если при этом
требуется обращение к сети, например, отправка данных по интернету, или обращение к базе данных - подобные действия лучше выполнять в методе onStop()
.
После выполнения этого метода activity становится невидимой, не отображается на экране, но она все еще активна. И если пользователь решит вернуться
к этой activity, то система вызовет снова метод onResume
, и activity снова
появится на экране.
Другой вариант работы может возникнуть, если вдруг система видит, что для работы активных приложений необходимо больше памяти. И система может сама завершить полностью работу activity, которая невидима и находится в фоне. Либо пользователь может нажать на кнопку Back (Назад). В этом случае у activity вызывается метод onStop.
В этом методе activity переходит в состояние Stopped. В этом состоянии activity полностью невидима. В методе onStop следует особождать используемые ресурсы, которые не нужны пользователю, когда он не взаимодействует с activity. Здесь также можно сохранять данные, например, в базу данных.
При этом во время состояния Stopped activity остается в памяти устройства, сохраняется состояние всех элементов интерфейса. К примеру, если в текстовое поле EditText был введен какой-то текст, то после возобновления работы activity и перехода ее в состояние Resumed мы вновь увидим в текстовом поле ранее введенный текст.
Если после вызова метода onStop пользователь решит вернуться к прежней activity, тогда система вызовет метод onRestart. Если же activity вовсе завершила свою работу, например, из-за закрытия приложения, то вызывается метод onDestroy().
Ну и завершается работа activity вызовом метода onDestroy, который возникает либо, если система решит убить activity в силу конфигурационных причин (например, поворот экрана или при многоконном режиме), либо при вызове метода finish().
Также следует отметить, что при изменении ориентации экрана система завершает activity и затем создает ее заново, вызывая метод onCreate.
В целом переход между состояниями activity можно выразить следующей схемой:
Расмотрим несколько ситуаций. Если мы работаем с Activity и затем переключаемся на другое приложение, либо нажимаем на кнопку Home, то у Activity вызывается следующая цепочка методов: onPause -> onStop. Activity оказывается в состоянии Stopped. Если пользователь решит вернуться к Activity, то вызывается следующая цепочка методов: onRestart -> onStart -> onResume.
Другая ситуация, если пользователь нажимает на кнопку Back (Назад), то вызывается следующая цепочка onPause -> onStop -> onDestroy. В результате Activity уничтожается. Если мы вдруг захотим вернуться к Activity через диспетчер задач или заново открыв приложение, то activity будет заново пересоздаваться через методы onCreate -> onStart -> onResume
Мы можем управлять этими событиями жизненного цикла, переопределив соответствующие методы. Для этого возьмем из прошлой главы класс MainActivity и изменим его следующим образом:
package com.example.viewapp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; public class MainActivity extends AppCompatActivity { private final static String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "onCreate"); } @Override protected void onDestroy(){ super.onDestroy(); Log.d(TAG, "onDestroy"); } @Override protected void onStop(){ super.onStop(); Log.d(TAG, "onStop"); } @Override protected void onStart(){ super.onStart(); Log.d(TAG, "onStart"); } @Override protected void onPause(){ super.onPause(); Log.d(TAG, "onPause"); } @Override protected void onResume(){ super.onResume(); Log.d(TAG, "onResume"); } @Override protected void onRestart(){ super.onRestart(); Log.d(TAG, "onRestart"); } }
Для логгирования событий здесь используется класс android.util.Log.
В данном случае обрабатываются все ключевые методы жизненного цикла. Вся обработка сведена к вызову метода Log.d()
,
в который передается TAG - случайное строковое значение и строка, которая выводится в консоли Logcat в нижней части
Android Studio, выполняя роль отладочной информации. Если эта консоль по умолчанию скрыта, то мы можем перейти к ней через пункт меню View -> Tool Windows -> Logcat.
И при запуске приложения мы сможем увидеть в окне Logcat отладочную информацию, которая определяется в методах жизненного цикла activity: