Життєвий цикл фрагментів
Кожен клас фрагмента успадковується від базового класу Fragment і має свій життєвий цикл, що складається з низки етапів:

Кожен етап життєвого циклу описується однією з констант перерахунку Lifecycle.State:
INITIALIZEDCREATEDSTARTEDRESUMEDDESTROYED
Варто зауважити, що подання фрагмента (його візуальний інтерфейс або View) має окремий життєвий цикл.
Під час створення фрагмент знаходиться в стані INITIALIZED. Щоб фрагмент пройшов усі інші етапи життєвого циклу, фрагмент необхідно передати в об'єкт FragmentManager, який далі визначає стан фрагмента і переводить фрагмент з одного стану в інший.
Методи життєвого циклу фрагмента
onCreate()— відбувається створення фрагмента. У цьому методі ми можемо отримати раніше збережений стан фрагмента через параметр методуBundle savedInstanceState. (Якщо фрагмент створюється вперше, то цей об'єкт має значенняnull) Цей метод викликається після виклику відповідного методуonCreate()уactivity.
public void onCreate(Bundle savedInstanceState)
onCreateView()— фрагмент створює подання (View або візуальний інтерфейс). У цьому методі ми можемо встановити, який саме візуальний інтерфейс буде використовувати фрагмент. Під час виконання цього методу подання фрагмента переходить у станINITIALIZED, а сам фрагмент все ще знаходиться в станіCREATED.
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
Перший параметр — об'єкт LayoutInflater дозволяє отримати вміст ресурсу layout і передати його у фрагмент.
Другий параметр — об'єкт ViewGroup представляє контейнер, у який буде завантажуватись фрагмент.
Третій параметр — об'єкт Bundle представляє стан фрагмента. (Якщо фрагмент завантажується вперше, то дорівнює null)
На виході метод повертає створене за допомогою LayoutInflater подання у вигляді об'єкта View — власне подання фрагмента.
onViewCreated()— викликається після створення подання фрагмента.
public void onViewCreated (View view, Bundle savedInstanceState)
Перший параметр — об'єкт View — подання фрагмента, яке було створене через метод onCreateView.
Другий параметр — об'єкт Bundle представляє стан фрагмента. (Якщо фрагмент завантажується вперше, то дорівнює null)
onViewStateRestored()— отримує стан подання фрагмента. Після виконання цього методу подання фрагмента переходить у станCREATED.
public void onViewStateRestored (Bundle savedInstanceState)
onStart()— викликається, коли фрагмент стає видимим і разом з поданням переходить у станSTARTED.
public void onStart ()
onResume()— викликається, коли фрагмент стає активним, і користувач може з ним взаємодіяти. При цьому фрагмент і його подання переходять у станRESUMED.
public void onResume ()
onPause()— фрагмент продовжує залишатися видимим, але вже не активний, і разом з поданням переходить у станSTARTED.
public void onPause ()
onStop()— фрагмент більше не є видимим і разом з поданням переходить у станCREATED.
public void onStop ()
На цьому етапі життєвого циклу ми можемо зберегти стан фрагмента за допомогою методу onSaveInstanceState(). Однак варто враховувати, що виклик цього методу залежить від версії API. До API 28 onSaveInstanceState() викликається до onStop(), а починаючи з API 28 після onStop().
onDestroyView()— знищується подання фрагмента. Подання переходить у станDESTROYED.onDestroy()— остаточне знищення фрагмента — він також переходить у станDESTROYED.
Додатково для фрагмента визначено два методи зворотного виклику, які пов'язані з приєднанням фрагмента до activity:
- Коли фрагмент додається в
FragmentManagerі приєднується до певного класуActivity, у фрагмента викликається методonAttach(). Цей метод викликається до всіх інших методів життєвого циклу. З цього моменту фрагмент стає активним, іFragmentManagerпочинає керувати його життєвим циклом. - Метод
onDetach()викликається, коли фрагмент видаляється зFragmentManagerі від'єднується від класуActivity. Цей метод викликається після всіх інших методів життєвого циклу.
У коді класу фрагмента ми можемо перевизначити всі або частину з цих методів. Наприклад, нехай у нас буде визначено такий проєкт:

У каталозі res/layout визначено файл layout для фрагмента - fragment_content.xml з такою розміткою:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/updateButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Обновить"
app:layout_constraintBottom_toTopOf="@+id/dateTextView"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/dateTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="26sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/updateButton" />
</androidx.constraintlayout.widget.ConstraintLayout>
Клас фрагмента використовує цей файл для встановлення подання, а також визначає методи для управління життєвим циклом:
package com.example.fragmentapp;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.util.Log;
import java.util.Date;
public class ContentFragment extends Fragment {
private final static String TAG = "ContentFragment";
public ContentFragment(){
Log.d(TAG, "Constructor");
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
Log.d(TAG, "onAttach");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
return inflater.inflate(R.layout.fragment_content, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button updateButton = view.findViewById(R.id.updateButton);
TextView updateBox = view.findViewById(R.id.dateTextView);
updateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String curDate = new Date().toString();
updateBox.setText(curDate);
}
});
Log.d(TAG, "onViewCreated");
}
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
Log.d(TAG, "onViewStateRestored");
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
@Override
public void onStop() {
super.onStop();
Log.d(TAG, "onStop");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, "onDestroyView");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
public void onDetach() {
super.onDetach();
Log.d(TAG, "onDetach");
}
}
На відміну від попередньої статті, де розглядалося створення фрагмента, тут фрагмент встановлює подання в методі onCreateView. Для цього в метод inflate() об'єкта LayoutInflater передається ідентифікатор ресурсу layout і контейнер — об'єкт ViewGroup, в який буде завантажуватись фрагмент. В результаті метод inflate() повертає створене подання.
return inflater.inflate(R.layout.fragment_content, container, false);
Під час виконання методу onViewCreated() подання вже створене, і воно передається як перший параметр — об'єкт View, через який за допомогою ідентифікаторів ми можемо отримати візуальні елементи — TextView і Button, які визначені в поданні.
Для решти методів життєвого циклу встановлено просте логування за допомогою методу Log.d().
Нехай у файлі activity_main.xml відбувається додавання фрагмента:
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.fragmentapp.ContentFragment" />
І клас MainActivity:
package com.example.fragmentapp;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Якщо ми запустимо проект, то на екрані пристрою ми побачимо візуальний інтерфейс, визначений для фрагмента.
А у вікні Logcat в Android Studio можна буде спостерігати логгування методів життєвого циклу

Lifecycle
Досить часто частина логіки програми зав'язана на життєвий цикл Activity. Ми вмикаємо що-небудь у методах onStart або onResume і вимикаємо в onPause або onStop.
Розглянемо приклад.
У нас є якийсь клас для роботи із сервером. Він має взаємодіяти із сервером, поки Activity відкрито. Відповідно, ми будемо підключати його до сервера під час показу Activity і відключати під час приховування Activity.
public class MyServer {
public void connect() {
// ...
}
public void disconnect() {
// ...
}
}
Метод connect використовується для підключення до сервера, disconnect - для відключення.
Викликаємо ці методи в onStart і onStop в Activity.
@Override
protected void onStart() {
super.onStart();
myServer.connect();
}
@Override
protected void onStop() {
super.onStop();
myServer.disconnect();
}
Тепер MyServer буде під'єднано, поки Activity мабуть на екрані.
Це цілком класична, часто використовувана схема. І в простому прикладі все виглядає непогано. Але в складних додатках зміст методів onStart, onStop тощо може складатися з декількох десятків рядків і бути досить заплутаним. Гугл рекомендує виносити цю логіку з Activity. Давайте подивимося, як це можна зробити.
У Activity є метод getLifecycle, який повертає об'єкт Lifecycle. На цей об'єкт можна підписати слухачів, які отримуватимуть сповіщення при зміні lifecycle-стану Activity.
Activity і фрагменти в Support Library, починаючи з версії 26.1.0 реалізують інтерфейс LifecycleOwner. Саме цей інтерфейс і додає їм метод getLifecycle.
Тобто у вас має бути такий рядок у build.gradle файлі модуля, у секції dependencies
implementation 'com.android.support:appcompat-v7:26.1.0'
Або використовуйте більш свіжу версію.
У нашому прикладі слухачем буде MyServer. Щоб мати можливість підписатися на Lifecycle, він повинен наслідувати інтерфейс LifecycleObserver.
public class MyServer implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void connect() {
// ...
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void disconnect() {
// ...
}
}
Зверніть увагу, що інтерфейс LifecycleObserver порожній. У ньому немає купи методів типу onStart, onStop тощо. Ми просто позначаємо в класі MyServer його ж власні методи анотацією OnLifecycleEvent і вказуємо, за якої lifecycle-події метод має бути викликаний.
У нашому прикладі ми вказуємо, що метод connect має викликатися в момент onStart, а метод disconnect - у момент onStop.
Залишилося підписати екземпляр MyServer на Lifecycle.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ...
getLifecycle().addObserver(myServer);
}
В Activity методом getLifecycle отримуємо Lifecycle, і методом addObserver підписуємо myServer.
А методи onStart і onStop в Activity нам більше не потрібні, їх можна видалити.
Тепер, під час переходу Activity зі стану CREATED у стан STARTED, його об'єкт Lifecycle викличе метод myServer.connect. А при переході зі STARTED у CREATED - Lifecycle викличе myServer disconnect.
При цьому в Acivity це вимагало від нас мінімум коду - тільки підписати myServer на Lifecycle. Все інше вирішує сам MyServer.
На схемі нижче ви можете побачити, які стани проходить Activity і які події при цьому викликаються.
Нічого нового тут для нас немає, все це ази Android. Тут ви можете бачити стани і події. Вони пов'язані дуже просто - при переході між станами відбуваються події.
Ці події ми вказували в анотаціях OnLifecycleEvent до методів об'єкта MyServer.
Повний список подій можна подивитися в документації.
Відписатися від Lifecycle можна методом removeObserver (документація).
Any
Ви можете використовувати подію ON_ANY для отримання всіх подій в одному методі
@OnLifecycleEvent(ON_ANY)
void onAny(LifecycleOwner source, Lifecycle.Event event) {
// ...
}
У цьому випадку всі події будуть викликати цей метод.
Використовуйте вхідний параметр event, щоб визначити, яка саме подія відбулася
Стан
Якщо ви хочете дізнатися поточний стан Activity, то у його об'єкта Lifecycle є метод getCurrentState:
if (getLifecycle().getCurrentState() == Lifecycle.State.RESUMED) {
// ...
}
Повний список станів можна подивитися в документації.
Також ви можете перевірити, що поточний стан Activity не нижчий за певний стан.
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
// ...
}
Метод isAtLeast тут перевіряє, що стан Activity не нижчий, ніж STARTED. Тобто або STARTED, або RESUMED.