onPause — это метод жизненного цикла Android, который вызывается, когда Activity теряет фокус ввода, но остаётся частично видимой на экране. Система вызывает onPause перед тем, как новое Activity выходит на передний план, при открытии диалогового окна, при нажатии кнопки «Недавние приложения» или при входящем звонке. Этот метод — последняя гарантированная точка для сохранения пользовательских данных, поскольку после onStop и onDestroy система может завершить процесс без дополнительных вызовов. Внутри onPause разработчик сохраняет черновики, приостанавливает анимации, освобождает камеру и записывает текущее состояние UI в SharedPreferences. Подробнее о полном жизненном цикле Activity читайте в статье Activity Lifecycle.
Главное
onPause — четвёртый метод жизненного цикла Activity, который вызывается, когда экран теряет фокус ввода, но остаётся частично видимым для пользователя. Это «переходное» состояние между активной работой приложения и его скрытием. Система вызывает onPause в следующих сценариях: открытие другого Activity (новый экран перекрывает текущий), появление диалогового окна (Dialog, PopupWindow, Snackbar не вызывают onPause, а DialogFragment — вызывает), нажатие кнопки «Недавние приложения», входящий звонок, нажатие кнопки «Питание» для блокировки экрана.
Основная задача onPause — подготовить приложение к тому, что оно может быть скрыто или уничтожено. Это последняя точка в жизненном цикле, где разработчик может быть уверен, что его код выполнится, прежде чем система продолжит переход к другому компоненту. После onPause система вызывает onStop (если Activity полностью скрывается), после которого уничтожение процесса может произойти в любой момент без дополнительных уведомлений.
Согласно документации Android Developers (2025), onPause должен быть максимально лёгким и быстрым. Пока onPause не вернёт управление, система не может запустить следующее Activity — это означает, что пользователь видит задержку перехода между экранами. Google рекомендует завершать onPause за менее чем 100 миллисекунд, а все длительные операции (сохранение в базу данных, запись на диск) выполнять асинхронно через корутины или apply().
В Activity метод onPause вызывается каждый раз, когда экран перестаёт быть активным, но может продолжать отображаться частично. Типичный пример: пользователь открывает приложение «Карты», нажимает «Поделиться местоположением», и поверх Карт открывается системный диалог выбора приложения. Activity Карт получает onPause, но остаётся видимым под диалогом. Когда диалог закрывается, Карты получают onResume без вызова onStart (экран не был скрыт полностью).
class NoteEditorActivity : AppCompatActivity() {
private var binding: ActivityNoteEditorBinding? = null
private val prefs by lazy {
getSharedPreferences("note_drafts", Context.MODE_PRIVATE)
}
override fun onPause() {
super.onPause()
// Сохраняем черновик заметки — асинхронно
prefs.edit()
.putString("draft_title", binding?.titleInput?.text.toString())
.putString("draft_body", binding?.bodyInput?.text.toString())
.putLong("draft_timestamp", System.currentTimeMillis())
.apply()
// Приостанавливаем видео
binding?.videoPlayer?.pause()
// Освобождаем эксклюзивные ресурсы
releaseCamera()
releaseAudioFocus()
}
override fun onResume() {
super.onResume()
// Восстанавливаем черновик
binding?.titleInput?.setText(prefs.getString("draft_title", ""))
binding?.bodyInput?.setText(prefs.getString("draft_body", ""))
acquireCamera()
acquireAudioFocus()
}
}
Пример NoteEditorActivity демонстрирует правильную работу с onPause: сохранение черновика в SharedPreferences через apply(), приостановка видеофайла, освобождение камеры и аудиофокуса. Каждый вызов — лёгкий и быстрый, не блокирующий UI-поток на время, достаточное для ANR. Обратите внимание на порядок: super.onPause() вызывается в первой строке — это гарантирует, что системная логика выполнится даже при исключении в пользовательском коде.
onPause — последняя точка, где разработчик может гарантированно сохранить данные пользователя перед тем, как приложение будет скрыто или убито системой. После onStop система может уничтожить процесс при нехватке памяти без вызова onDestroy. Метод onSaveInstanceState() вызывается после onPause, но его Bundle не предназначен для долговременного хранения — он живёт только до следующего onCreate.
SharedPreferences с асинхронным apply() — оптимальный способ сохранения небольших объёмов данных в onPause. В отличие от commit(), который синхронно записывает данные на диск и возвращает boolean, apply() немедленно сохраняет данные в памяти и планирует асинхронную запись на диск. Это занимает менее 1 миллисекунды в UI-потоке против 10–100 миллисекунд у commit().
override fun onPause() {
super.onPause()
// ❌ Плохо: синхронная запись блокирует поток
// prefs.edit().putInt("score", score).commit()
// ✅ Хорошо: асинхронная запись
prefs.edit().putInt("score", score).apply()
// Для сложных объектов — кэширование в ViewModel
viewModel.saveState()
}
Для структурированных данных (SQLite через Room) в onPause используют корутины с lifecycleScope. ViewModelScope автоматически отменяет корутину при уничтожении ViewModel, что предотвращает запись в закрытую базу данных. Запись через Room с корутинами занимает 5–15 миллисекунд и не блокирует UI-поток.
// В ViewModel:
fun saveDraft(title: String, body: String) {
viewModelScope.launch(Dispatchers.IO) {
noteDao.insert(NoteDraft(title = title, body = body))
}
}
// В Activity.onPause:
viewModel.saveDraft(
binding?.titleInput?.text.toString(),
binding?.bodyInput?.text.toString()
)
onPause во Fragment вызывается, когда Fragment перестаёт быть активным, но может оставаться видимым. Это происходит, когда: Fragment заменяется другим Fragment через FragmentTransaction; Fragment перестаёт быть текущей страницей в ViewPager; Activity, содержащая Fragment, получает onPause. Взаимодействие между onPause Activity и onPause Fragment строго иерархично: сначала onPause получает Activity, затем все её Fragment.
class MapFragment : Fragment() {
private var mapController: MapController? = null
override fun onPause() {
super.onPause()
mapController?.stopFollowMode()
binding?.mapContainer?.alpha = 0.7f
}
override fun onResume() {
super.onResume()
binding?.mapContainer?.alpha = 1.0f
if (isVisible) {
mapController?.startFollowMode()
}
}
}
Специфика работы с картами в onPause: Google Maps и Yandex Maps потребляют значительные ресурсы GPU при активном режиме слежения (follow mode). При потере фокуса имеет смысл отключать анимацию карты и снижать частоту обновления маркеров, а при возврате фокуса — восстанавливать полную функциональность. Это улучшает производительность и снижает энергопотребление при переключении между экранами.
Одна из самых частых путаниц у начинающих Android-разработчиков — непонимание разницы между onPause и onStop. Рассмотрим каждый сценарий и определим правильный метод.
| Сценарий | onPause | onStop |
|---|---|---|
| Открытие диалогового окна | Вызывается | Не вызывается |
| Открытие нового Activity (не прозрачного) | Вызывается | Вызывается |
| Нажатие кнопки «Домой» | Вызывается | Вызывается |
| Блокировка экрана | Вызывается | Вызывается |
| Входящий звонок | Вызывается | Вызывается |
| Прозрачное Activity поверх текущего | Вызывается | Не вызывается |
| Split Screen (половина экрана) | Вызывается | Не вызывается |
| PiP (Picture-in-Picture) | Вызывается | Не вызывается |
Главное правило: onPause вызывается при любой потере фокуса, onStop — только при полной потери видимости. Если Activity остаётся видимой (даже частично), onStop не вызывается. Это критически важно для режимов Split Screen, PiP и прозрачных Activity — здесь onPause/onResume работают, а onStart/onStop — нет.
onPause — самый критичный по времени метод жизненного цикла, поскольку он блокирует отрисовку следующего Activity. Система ожидает завершения onPause текущего Activity, прежде чем показать новое. Если onPause выполняется дольше 100 миллисекунд, пользователь замечает задержку перехода; если дольше 5 секунд — система показывает ANR.
Google Android Performance Guide (2025) даёт следующие рекомендации для onPause: не выполняйте сетевые запросы — они должны быть отменены или перенесены в WorkManager; не записывайте большие файлы на диск — используйте BufferedWriter в фоновом потоке; не выполняйте сложные SQL-запросы — Room-операции должны быть асинхронными через корутины; избегайте создания новых объектов — сборка мусора в onPause усугубляет задержку; используйте apply() вместо commit() для SharedPreferences.
override fun onPause() {
super.onPause()
// ❌ Плохо: HTTP-запрос блокирует UI
// val response = api.syncSave(data).execute()
// ❌ Плохо: синхронная запись в файл
// FileOutputStream(file).write(data)
// ✅ Хорошо: асинхронное сохранение
lifecycleScope.launch {
withContext(Dispatchers.IO) {
api.saveData(data)
fileDao.write(data)
}
}
// ✅ Хорошо: лёгкая запись в SharedPreferences
prefs.edit().putString("key", value).apply()
}
Профилирование onPause через Android Studio Profiler (CPU-графа) показывает точное время выполнения. Если onPause занимает более 100 мс, Profiler подсвечивает метод жёлтым, а более 500 мс — красным. В коммерческих проектах IT Sectr мы используем Macrobenchmark-тесты, которые автоматически проверяют время перехода между Activity и сигнализируют о регрессии производительности в CI-пайплайне.
Опытные разработчики тоже допускают ошибки в onPause. Рассмотрим пять типовых проблем и их решения.
Вызов Room DAO с синхронным запросом (.executeAsObservable() без корутин) в onPause блокирует UI-поток на 10–50 мс. Если в этот момент происходит GC или конкуренция за запись в БД, задержка может достигать 200–500 мс. Решение: использовать корутины с Dispatchers.IO или apply() для SharedPreferences.
onPause — не место для регистрации слушателей. Если зарегистрировать BroadcastReceiver в onPause, он останется активным, когда Activity уже не видна. Регистрация должна быть только в onStart/onResume, а в onPause/onStop — только отписка. Исключение — Intent-driven API, которые требуют регистрации перед вызовом.
Если в onPause возникает необработанное исключение, система не вызывает onStop и onDestroy. Activity зависает в неопределённом состоянии, а onResume при возврате может не восстановить корректно освобождённые ресурсы. Решение: оборачивать критичные операции в try/catch с логированием через Log.e().
Не нужно сохранять в onPause данные, которые легко восстановить. Например, результаты API-запросов кэшируют в Room или DataStore в момент получения, а не в onPause. Сохраняйте только то, что пользователь ввёл вручную и не сможет восстановить автоматически — текст в полях, выбранные элементы, позицию скролла.
super.onPause() должен вызываться, но, в отличие от onCreate, его отсутствие не вызывает немедленного краша. Система «прощает» пропуск super в onPause, но внутренний конечный автомат переходит в некорректное состояние. Следующий вызов onResume может не восстановить фокус ввода, и Activity останется «замороженной». Всегда вызывайте super.onPause() как можно раньше.
Часто задаваемые вопросы
Вызов finish() в onPause завершит Activity немедленно после возврата из метода. Это корректный сценарий, если при потере фокуса требуется закрыть экран (например, экран авторизации при сворачивании приложения). Однако finish() запускает полный цикл завершения: onStop → onDestroy, что добавляет задержку к переходу. Используйте finish() в onPause только когда это действительно необходимо.
onPause — для сохранения данных, которые должны пережить завершение процесса (черновики в SharedPreferences/Room). onSaveInstanceState — для сохранения временного состояния UI, которое нужно только до следующего onCreate (позиция скролла, выбранная вкладка). Bundle onSaveInstanceState не сохраняется при полном завершении приложения — он существует только в памяти. onPause-данные сохраняются на диске и переживают перезагрузку.
Не рекомендуется. Открытие диалога или всплывающего окна в onPause приводит к WindowLeakException, если Activity уже завершена. Если нужно показать уведомление при потере фокуса, используйте NotificationManager (системные уведомления) — это безопасно и ожидаемо для пользователя. Для отложенных действий используйте AlarmManager или WorkManager.
onPause гарантированно вызывается перед тем, как Activity перестанет быть активной. onStop может не вызываться, если система убивает процесс для освобождения памяти — в этом случае onDestroy также не вызывается. onPause — единственный метод после onResume, который вызывается всегда, независимо от причины потери фокуса. Поэтому все критичные данные сохраняют именно в onPause.
Для тестирования onPause используют Robolectric или FragmentScenario от AndroidX Test. FragmentScenario.create() → moveToState(State.STARTED) → moveToState(State.RESUMED) → moveToState(State.STARTED) последовательно вызывает onPause. Затем проверяется, что данные сохранены в SharedPreferences или что камера освобождена через mock-объект. Robolectric 4.12+ поддерживает эмуляцию onPause/onResume без физического устройства.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также