onBackPressed() объявлен устаревшим в Android 13 (Tiramisu). Как правильно обрабатывать нажатие кнопки «Назад»?
Почему onBackPressed() устарел и что пришло на смену?
Начиная с Android 13 (API 33) метод Activity.onBackPressed() официально помечен как deprecated. Google рекомендует использовать OnBackPressedDispatcher — более гибкий и компонуемый механизм, который позволяет обрабатывать нажатие кнопки «Назад» на разных уровнях (Activity, Fragment, View) без жёсткой привязки к жизненному циклу.
Подготовка: добавьте зависимость
Убедитесь, что у вас в build.gradle (модуль app) есть:
dependencies {
implementation("androidx.activity:activity-ktx:1.8.2")
implementation("androidx.fragment:fragment-ktx:1.6.2")
}
Основной подход: OnBackPressedCallback
Создайте callback, который будет управлять поведением. Регистрируйте его через onBackPressedDispatcher.addCallback().
Пример 1: Диалог подтверждения выхода (Activity)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
AlertDialog.Builder(this@MainActivity)
.setTitle("Выход")
.setMessage("Вы уверены, что хотите выйти?")
.setPositiveButton("Да") { _, _ -> isEnabled = false; onBackPressedDispatcher.onBackPressed() }
.setNegativeButton("Нет", null)
.show()
}
})
}
}
Ключевой момент: isEnabled = false временно отключает callback, чтобы вызвать стандартное поведение (закрытие Activity).
Пример 2: Разное поведение на разных фрагментах
В фрагменте редактирования (просто возврат назад):
class EditFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Регистрируем callback в onCreate (не в onViewCreated!)
requireActivity().onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// Просто уходим назад — вызываем стандартное поведение
isEnabled = false
requireActivity().onBackPressedDispatcher.onBackPressed()
}
})
}
}
В фрагменте с WebView (обработка истории):
class WebViewFragment : Fragment() {
private lateinit var webView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requireActivity().onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
} else {
isEnabled = false
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
})
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val view = inflater.inflate(R.layout.fragment_webview, container, false)
webView = view.findViewById(R.id.webView)
webView.webViewClient = WebViewClient()
webView.loadUrl("https://example.com")
return view
}
}
Важные моменты
- Жизненный цикл: Callback автоматически удаляется, когда вызывается
LifecycleOwner.onDestroy()(в нашем случае — фрагмент или Activity). - Приоритет: Если несколько callback-ов зарегистрированы, последний добавленный имеет приоритет. Если он не обрабатывает событие (
isEnabled = false), управление передаётся следующему. - Android 12 и ниже: Код работает на всех версиях, начиная с Android 2.0 (API 1).
OnBackPressedDispatcherдоступен в androidx.activity:activity-ktx с версии 1.0.0.
Что делать, если нужно глобальное поведение?
Используйте addCallback без указания LifecycleOwner (но это не рекомендуется, так как может привести к утечкам):
onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() { /* ... */ }
})
Полный сценарий для вашего случая
- В
MainActivityзарегистрируйте callback для диалога выхода. - В каждом фрагменте регистрируйте свой callback, который переопределяет поведение. Не забудьте отключать его, когда нужно передать управление дальше.
- Для WebView проверяйте
canGoBack()и вызывайтеgoBack()вместо стандартного закрытия.
Этот подход чище, тестируемее и соответствует современным рекомендациям Google.
<<Альтернативный подход: использование Navigation Component
Если вы используете Navigation Component (рекомендуется для приложений с несколькими экранами), можно вообще не писать ручную обработку кнопки «Назад». NavController автоматически обрабатывает навигацию: при нажатии «Назад» он возвращается на предыдущий экран или выходит из приложения, если стек пуст.
// В Activity
val navController = findNavController(R.id.nav_host_fragment)
NavigationUI.setupActionBarWithNavController(this, navController)
// Автоматическая обработка кнопки «Назад»
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp()
}
Для кастомного поведения (например, диалог выхода) используйте OnBackPressedCallback вместе с NavController:
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (navController.currentDestination?.id == R.id.mainFragment) {
// Показываем диалог
showExitDialog()
} else {
isEnabled = false
onBackPressedDispatcher.onBackPressed()
}
}
})
Преимущество: не нужно вручную управлять стеком фрагментов. NavController сам знает, какой экран был предыдущим.
Недостаток: требует миграции на Navigation Component, что может быть избыточно для простых приложений.
Ответов пока нет.
Войдите чтобы оставить ответ