Activity і життєвий цикл програми

Ключовим компонентом для створення візуального інтерфейсу в додатку 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()

onCreate — перший метод, з якого починається виконання activity. У цьому методі activity переходить у стан Created. Цей метод обов'язково має бути визначений у класі activity. У ньому відбувається початкова налаштування activity, включаючи створення об'єктів візуального інтерфейсу.

Метод отримує об'єкт Bundle, який містить попередній стан activity, якщо він був збережений. Якщо activity створюється заново, об'єкт Bundle має значення null. Якщо ж activity була раніше створена, але знаходилася в призупиненому стані, то Bundle містить зв'язану з activity інформацію.

Після того, як метод onCreate() завершує виконання, activity переходить у стан Started, і система викликає метод onStart().


onStart()

У методі onStart() здійснюється підготовка до виведення activity на екран пристрою. Як правило, цей метод не потребує переозначення, а всю роботу виконує вбудований код. Після завершення роботи методу activity відображається на екрані, викликається метод onResume, і activity переходить у стан Resumed.


onResume()

При виклику методу onResume activity переходить у стан Resumed і відображається на екрані пристрою. Користувач може взаємодіяти з нею. Activity залишається в цьому стані, поки не втратить фокус, наприклад, через перемикання на іншу activity або вимкнення екрану пристрою.


onPause()

Якщо користувач вирішить перейти до іншої activity, система викликає метод onPause, і activity переходить у стан Paused. У цьому методі можна звільнити використовувані ресурси, призупинити процеси (наприклад, відтворення аудіо, анімацій), зупинити роботу камери (якщо вона використовується) тощо, щоб зменшити навантаження на систему.

Однак слід пам'ятати, що в цьому стані activity все ще залишається видимою на екрані, і на виконання цього методу виділяється дуже мало часу. Тому не варто тут зберігати які-небудь дані, особливо якщо це вимагає звернення до мережі (наприклад, відправка даних через інтернет) або бази даних — ці дії краще виконувати в методі onStop().

Після виконання цього методу activity стає невидимою, не відображається на екрані, але залишається активною. Якщо користувач вирішить повернутися до цієї activity, система знову викликає метод onResume, і activity знову з'являється на екрані.

Примітка: У випадку, якщо система вирішить завершити роботу невидимих activity, вона може викликати метод onStop(), особливо якщо система потребує звільнення пам'яті.


onStop()

У методі onStop() activity переходить у стан Stopped. У цьому стані activity повністю невидима. Тут слід звільняти ресурси, які не потрібні користувачу, коли він не взаємодіє з activity. Також можна зберігати дані (наприклад, у базу даних).

Activity залишається в пам'яті пристрою, і зберігається стан усіх елементів інтерфейсу. Наприклад, якщо в текстове поле EditText був введений текст, то після відновлення роботи activity в стані Resumed цей текст залишатиметься.

Якщо після виклику методу onStop() користувач вирішить повернутися до попередньої activity, система викликає метод onRestart. Якщо ж activity завершила свою роботу, наприклад, через закриття програми, викликається метод onDestroy().


onDestroy()

Метод onDestroy() викликається при завершенні роботи activity. Це відбувається або тому, що система вирішує вбити 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: