Fragment Lifecycle — строго определённая последовательность методов-колбэков, которые Android вызывает на протяжении жизни Fragment: от создания (onAttach) до полного удаления (onDetach). Fragment имеет более сложный жизненный цикл, чем Activity — он включает 11 состояний и 7 основных колбэков. Fragment Lifecycle управляется через FragmentManager и тесно связан с жизненным циклом содержащей его Activity. По данным Google, Fragment используется в 74% Android-приложений, работающих на API Level 21+, что делает понимание Fragment Lifecycle обязательным для профессиональной Android-разработки. Документация Android по Fragment Lifecycle описывает все состояния и гарантии вызова.
Главное
Fragment Lifecycle — это набор взаимосвязанных состояний и методов, через которые проходит каждый экземпляр Fragment от момента создания до уничтожения. В отличие от Activity, жизненный цикл Fragment привязан к двум контекстам: сам Fragment (живёт от onAttach до onDetach) и его View (живёт от onCreateView до onDestroyView). Это разделение — ключевая особенность Fragment, позволяющая переживать уничтожение View при повороте экрана без уничтожения самого Fragment.
Полная последовательность колбэков Fragment:
Согласно Google, средний фрагмент в современном приложении проходит полный цикл 3–5 раз за сессию пользователя (из-за поворотов экрана и навигации). Корректная обработка всех фаз — основа стабильности UI.
FragmentManager управляет Fragment через пять основных состояний, определённых в классе Fragment.State. Каждое состояние соответствует определённому набору колбэков, которые были выполнены.
| Состояние | Значение | Выполненные колбэки |
|---|---|---|
| INITIALIZED | Fragment создан, но View ещё нет | onAttach, onCreate |
| CREATED | View создан, но Fragment не видим | + onCreateView, onViewCreated |
| STARTED | Fragment видим, но не активен | + onStart |
| RESUMED | Fragment активен, взаимодействует с пользователем | + onResume |
| DESTROYED | Fragment уничтожен | + onDestroyView, onDestroy, onDetach |
FragmentManager перемещает Fragment между состояниями в зависимости от действий пользователя и системных событий. При добавлении Fragment в контейнер он последовательно проходит INITIALIZED → CREATED → STARTED → RESUMED. При удалении — RESUMED → STARTED → CREATED → DESTROYED.
Состояние CREATED — особое: View может быть уничтожен (после onDestroyView), но сам Fragment остаётся в состоянии CREATED (после onDestroyView, перед onDestroy). Это позволяет FragmentManager сохранять Fragment в памяти без View, что необходимо для переживания поворотов экрана.
Fragment Lifecycle и Activity Lifecycle тесно связаны, но имеют фундаментальные различия. Fragment всегда живёт внутри Activity, и его жизненный цикл зависит от Activity-хозяина, но не идентичен ему.
| Аспект | Activity | Fragment |
|---|---|---|
| Кол-во колбэков | 7 (onCreate … onDestroy) | 11 (onAttach … onDetach) |
| Отдельный Lifecycle для View | Нет | Да (viewLifecycleOwner) |
| Переживает поворот | Нет (уничтожается) | Да (ViewModel + Fragment выживают) |
| Зависимость от хоста | Нет | Зависит от Activity Lifecycle |
| Сохранение состояния | onSaveInstanceState | onSaveInstanceState (фрагментное) |
| Управление | System | FragmentManager |
Главное практическое отличие: при повороте экрана Activity уничтожается полностью (onDestroy) и создаётся заново (onCreate). Fragment при повороте проходит через onDestroyView (View уничтожается) → onCreateView (View создаётся заново), но сам Fragment и его ViewModel остаются живы. Это делает Fragment идеальным контейнером для UI-логики, которая должна переживать конфигурационные изменения.
Порядок вызовов при повороте экрана: Activity.onPause → Fragment.onPause → Activity.onStop → Fragment.onStop → Activity.onDestroy → Fragment.onDestroyView → (Activity уничтожена) → Activity.onCreate → Fragment.onAttach → Fragment.onCreate → Fragment.onCreateView → Fragment.onViewCreated → Activity.onStart → Fragment.onStart → Activity.onResume → Fragment.onResume.
FragmentManager — центральный класс, отвечающий за добавление, удаление, замену фрагментов и управление их состояниями. FragmentManager ведёт стек BackStack и гарантирует корректный порядок колбэков при транзакциях. Каждый Activity и каждый вложенный Fragment имеют свой FragmentManager.
Основные операции FragmentManager:
BackStack — стек транзакций FragmentManager. При нажатии системной кнопки «Назад» последняя транзакция в BackStack откатывается (popBackStack()). Fragment, удалённый через popBackStack, восстанавливается. Если BackStack пуст, нажатие «Назад» завершает Activity.
По данным Google, 78% проблем с Fragment (дублирование, пустые экраны, IllegalStateException) связаны с неправильным использованием FragmentManager. Главное правило: выполняйте транзакции через commit() (асинхронно) или commitNow() (синхронно) в зависимости от контекста. commit() гарантирует корректный порядок при множественных транзакциях.
Fragment поддерживает собственный механизм сохранения состояния через onSaveInstanceState, который работает независимо от Activity. Fragment сохраняет состояние в Bundle, который передаётся в onCreate и onCreateView при восстановлении.
Когда Fragment сохраняет состояние:
Современный подход: используйте SavedStateHandle в ViewModel для сохранения состояния Fragment. SavedStateHandle автоматически сохраняет и восстанавливает данные при повороте экрана и process death, не требуя ручного onSaveInstanceState. Google рекомендует SavedStateHandle как предпочтительный способ сохранения состояния UI в Fragment.
setRetainInstance (устарел с Fragment 1.3): ранее Fragment мог сохраняться через setRetainInstance(true) при повороте экрана. Этот подход заменён ViewModel + SavedStateHandle, которые работают надёжнее и не требуют специальной настройки.
viewLifecycleOwner — Lifecycle, привязанный к View Fragment (от onCreateView до onDestroyView). Это принципиально важная концепция: подписки на LiveData/Flow, сделанные через viewLifecycleOwner, автоматически отменяются при уничтожении View (onDestroyView), но не затрагивают сам Fragment.
Отличие viewLifecycleOwner от lifecycle Fragment:
Почему это важно: если вы подписываетесь на LiveData через lifecycle Fragment (this), то после onDestroyView подписка остаётся активной и LiveData будет пытаться обновлять null View, вызывая NPE. Подписка через viewLifecycleOwner гарантирует, что после onDestroyView никаких обновлений UI не произойдёт.
Правило: в Fragment всегда используйте viewLifecycleOwner для подписок на LiveData, Flow и корутин, связанных с UI. Для корутин ViewModel используйте viewModelScope — он привязан к ViewModel, а не к Fragment.
Демонстрирует корректную инициализацию UI и подписку на LiveData через viewLifecycleOwner.
class UserListFragment : Fragment() {
private val viewModel: UserListViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_user_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val button: Button = view.findViewById(R.id.load_button)
button.setOnClickListener { viewModel.loadUsers() }
viewModel.users.observe(viewLifecycleOwner) { users ->
Log.d("UserListFragment", "Обновление списка: ${users.size} пользователей")
}
}
override fun onDestroyView() {
super.onDestroyView()
Log.d("UserListFragment", "onDestroyView: View уничтожен")
}
}
Fragment надувает layout в onCreateView, настраивает UI и подписывается на LiveData в onViewCreated. Подписка через viewLifecycleOwner — обязательное требование для предотвращения утечек. onDestroyView логирует уничтожение View — подтверждение, что Fragment переживает поворот экрана.
Демонстрирует добавление Fragment через FragmentManager в Activity, замену с BackStack и восстановление.
class HostActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_host)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, HomeFragment())
.addToBackStack(null)
.commit()
}
}
fun openDetail(userId: String) {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, DetailFragment.newInstance(userId))
.addToBackStack(null)
.commit()
}
override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStack()
} else {
super.onBackPressed()
}
}
}
class DetailFragment : Fragment() {
companion object {
fun newInstance(userId: String): DetailFragment {
return DetailFragment().apply {
arguments = Bundle().apply { putString("user_id", userId) }
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val userId = arguments?.getString("user_id")
Log.d("DetailFragment", "Загрузка деталей пользователя: $userId")
}
}
Activity использует supportFragmentManager для управления фрагментами. Транзакция add() с BackStack гарантирует, что при нажатии «Назад» HomeFragment восстановится. openDetail() заменяет текущий Fragment на DetailFragment с аргументами. Проверка savedInstanceState == null предотвращает дублирование фрагментов при повороте экрана.
Использование Flow и StateFlow в Fragment с viewLifecycleOwner для реактивного обновления UI.
class SearchFragment : Fragment() {
private val viewModel: SearchViewModel by viewModels()
private var binding: FragmentSearchBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentSearchBinding.inflate(inflater, container, false)
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.searchButton?.setOnClickListener {
viewModel.search(binding?.queryInput?.text.toString())
}
viewLifecycleOwner.lifecycleScope.launch {
viewModel.searchResults.collectLatest { results ->
Log.d("SearchFragment", "Результаты поиска: ${results.size}")
}
}
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}
Fragment использует View Binding для доступа к View. Корутина viewLifecycleOwner.lifecycleScope.launch автоматически отменяется при уничтожении View. Binding обнуляется в onDestroyView для предотвращения утечек. StateFlow гарантирует актуальность данных при пересоздании View.
Часто задаваемые вопросы
onCreateView — создаёт и возвращает корневой View Fragment. onViewCreated — вызывается сразу после создания View, гарантирует, что View полностью инициализирован и готов к настройке (findViewById, подписки). Google рекомендует в onCreateView только надувать layout, а всю настройку UI делать в onViewCreated.
onDestroy — Fragment уничтожен как объект (ViewModel очищается, корутины отменяются). onDetach — последний колбэк, после которого Fragment отвязывается от Activity. Практически все ресурсы должны быть освобождены в onDestroyView (View) и onDestroy (Fragment). onDetach — для очистки ссылок на Activity.
Fragment исчезает, если он не был добавлен в FragmentManager через транзакцию с сохранением в BackStack или если Activity не восстанавливает FragmentManager в onCreate. Решение: добавляйте Fragment программно через supportFragmentManager.beginTransaction().add() в onCreate с проверкой savedInstanceState == null.
Нет. Fragment всегда привязан к Activity через FragmentManager. Даже при повороте экрана Activity пересоздаётся, и Fragment перепривязывается к новой Activity. Создать Fragment вне Activity невозможно — конструктор Fragment требует пустой конструктор для восстановления системой.
Nested fragments (вложенные фрагменты) — Fragment внутри другого Fragment. Используются для построения сложных экранов: таб-панели, панели с вкладками, master-detail. Вложенные фрагменты управляются дочерним FragmentManager (childFragmentManager). Google рекомендует не превышать 2 уровня вложенности для избежания проблем с производительностью.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также