Back Press Handling — to mechanizm przechwytywania i obsługi systemowego przycisku „Wstecz” w Android, określający, jaka akcja jest wykonywana po jego naciśnięciu. Według Android Developers (2024), począwszy od Android 11 tradycyjna metoda onBackPressed() została zastąpiona przez OnBackPressedDispatcher. Nowe API pozwala komponentom na każdym poziomie hierarchii przechwytywać naciśnięcie, a nie tylko Activity. Kluczowa różnica — obsługa wielu callback w łańcuchu z priorytetami.
Najważniejsze
Back Press Handling — to systemowy mechanizm Android, który określa, co się dzieje, gdy użytkownik naciśnie sprzętowy lub programowy przycisk „Wstecz”. W zależności od kontekstu naciśnięcie może: zamknąć bieżący ekran i powrócić do poprzedniego, ukryć klawiaturę, zamknąć Drawer lub Bottom Sheet, wyjść z aplikacji, jeśli użytkownik znajduje się na ekranie głównym.
Zachowanie przycisku „Wstecz” ewoluowało z każdą wersją Android. W Android 10 pojawiły się gesty nawigacji, w Android 11 — OnBackPressedDispatcher jako standardowe API, a w Android 13 — ulepszona obsługa predictive back gesture, gdzie system pokazuje animację przejścia przed faktycznym wykonaniem akcji. Google konsekwentnie zmierza w stronę przewidywalnego i spójnego zachowania Back Press na wszystkich urządzeniach.
Prawidłowa obsługa przycisku „Wstecz” — krytyczny element UX w aplikacji Android. Użytkownik oczekuje, że naciśnięcie „Wstecz” przenosi go do poprzedniego ekranu w stosie nawigacji, a nie zamyka aplikację niespodziewanie. Naruszenie tego oczekiwania to jeden z głównych powodów negatywnych opinii i niskich ocen w Google Play.
Historia Back Press API w Android odzwierciedla ogólną ewolucję platformy: od prostej metody w Activity do elastycznego systemu callback z obsługą lifecycle i Compose. Przyjrzyjmy się trzem etapom rozwoju.
Od pierwszego Android API przycisk „Wstecz” był obsługiwany w metodzie onBackPressed klasy Activity. Programista nadpisywał tę metodę i pisał własną logikę. Problem polegał na tym, że Fragment i View nie mogły przechwycić naciśnięcia — całe sterowanie szło przez Activity. Prowadziło to do rozdętych Activity i skomplikowanego łańcucha if-else do określania, kto powinien obsłużyć naciśnięcie.
Z Activity 1.0.0 (AndroidX) Google wprowadziło OnBackPressedDispatcher. To centralny dyspozytor, który przyjmuje callback od dowolnego komponentu — Activity, Fragment, Dialog, niestandardowego View. Callback rejestrowane są z określeniem kolejności (przez priority) i mogą być dodawane lub usuwane dynamicznie. OnBackPressedDispatcher jest wywoływany wcześniej niż stary onBackPressed, co pozwala przechwycić naciśnięcie zanim obsłuży je Activity.
Android 13 wprowadził predictive back gesture — systemową animację, która pokazuje, dokąd prowadzi naciśnięcie „Wstecz”, jeszcze zanim użytkownik zakończy gest. Aby obsługiwać tę animację, programiści muszą używać OnBackPressedDispatcher i wskazywać, czy callback obsługuje systemową animację przez właściwość isEnabled. Jeśli callback nie obsługuje predictive animation, system pokazuje domyślną animację, która może nie odpowiadać kontekstowi aplikacji.
| API | Minimalny SDK | Obsługa Fragment | Predictive Back |
|---|---|---|---|
| onBackPressed | API Level 1 | Przez Activity | Nie |
| OnBackPressedDispatcher | Activity 1.0.0 | Bezpośrednia | Częściowa |
| OnBackPressedDispatcher + lifecycle | Activity 1.3.0 | Lifecycle-aware | Pełna |
OnBackPressedDispatcher — to rdzeń nowego Back Press API. Zarządza łańcuchem callback, wywołując je po kolei aż do pierwszego, który obsłuży zdarzenie. Jeśli żaden callback nie obsłuży naciśnięcia, dyspozytor wykonuje domyślną akcję — wywołanie finish() dla Activity lub popBackStack() dla Navigation Component.
Callback rejestruje się przez addCallback z podaniem LifecycleOwner i obiektu OnBackPressedCallback. Callback zawiera właściwość isEnabled — jeśli jest ustawiona na false, callback jest pomijany. Dla priority można przekazać wartość od 0 (najniższy) do Integer.MAX_VALUE. Fragment Activity Result API używa tego mechanizmu do automatycznej rejestracji callback powiązanych z cyklem życia.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callback = object : OnBackPressedCallback(enabled = true) {
override fun handleOnBackPressed() {
if (isDrawerOpen) { closeDrawer() }
else { isEnabled = false; onBackPressed() }
}
}
onBackPressedDispatcher.addCallback(this, callback)
}
Callback automatycznie usuwany, gdy LifecycleOwner przechodzi w stan DESTROYED. Rozwiązuje to stary problem wycieków callback przy obrocie ekranu. Jeśli callback został dodany we Fragment, zostanie usunięty przy zniszczeniu Fragment. Do tymczasowego wyłączenia callback używa się właściwości isEnabled — można ją przełączać bez usuwania i ponownej rejestracji.
Kolejność wywołania odwrotna do kolejności dodawania: ostatnio dodany callback otrzymuje sterowanie jako pierwszy. To logiczne, ponieważ najbardziej zagnieżdżony element UI (np. Bottom Sheet wewnątrz Fragment) powinien obsłużyć naciśnięcie wcześniej niż nadrzędny Fragment. Jeśli najgłębszy callback nie obsługuje naciśnięcia (isEnabled = false), sterowanie przekazywane jest do następnego w łańcuchu.
Fragment API zapewnia własną integrację z OnBackPressedDispatcher przez metodę requireActivity().onBackPressedDispatcher. Począwszy od Fragment 1.2.0, każdy Fragment może rejestrować własny callback, który automatycznie wiąże się z cyklem życia Fragment i jest usuwany przy jego zniszczeniu.
Rejestracja callback we Fragment odbywa się w onCreate, onViewCreated lub nawet w samym View — ważne tylko, aby LifecycleOwner (Fragment) był aktywny. Gdy Fragment przechodzi w stan STARTED, callback włącza się, przy STOPPED — wyłącza. Gwarantuje to, że ukryty Fragment (w ViewPager) nie będzie obsługiwał naciśnięcia „Wstecz”.
class EditorFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
showDiscardDialog()
}
}
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
}
}
Dialogi i BottomSheet automatycznie przechwytują naciśnięcie „Wstecz” do zamknięcia. Jeśli potrzebujesz wykonać dodatkową akcję przed zamknięciem — zarejestruj callback z wyższym priorytetem. Ważne: jeśli ustawisz setCancelable(false) na Dialog, callback nie zadziała — to systemowe zachowanie.
Jetpack Compose zapewnia deklaratywne API do obsługi przycisku „Wstecz” przez funkcję composable BackHandler. BackHandler przyjmuje enabled (stan boolowski) i onBack — callback, który jest wywoływany przy naciśnięciu. Jeśli enabled = false, naciśnięcie przekazywane jest dalej w łańcuchu.
BackHandler automatycznie rejestruje OnBackPressedCallback w OnBackPressedDispatcher nadrzędnej Activity. Uwzględnia cykl życia composable: przy wyjściu z kompozycji callback jest usuwany. enabled może być powiązany ze stanem — na przykład pokazywać okno potwierdzenia tylko, jeśli w formularzu są niezapisane zmiany.
@Composable
fun EditScreen(hasUnsavedChanges: Boolean) {
BackHandler(enabled = hasUnsavedChanges) {
// Pokaż okno potwierdzenia
}
Column {
TextField(value = ..., onValueChange = ...)
}
}
Predictive back gesture w Compose jest obsługiwany od Compose 1.5.0. BackHandler automatycznie obsługuje systemową animację przejścia, jeśli jest włączona na urządzeniu. Do niestandardowej predictive-animacji użyj modyfikatora predictiveBackHandler, który zwraca postęp gestu od 0 do 1.
Back Press Handling wydaje się prosty, ale w praktyce programiści popełniają szereg systematycznych błędów. Przyjrzyjmy się najczęstszym problemom i sposobom ich rozwiązania na podstawie zaleceń Google i doświadczenia społeczności.
Bezpośrednie wywołanie finish() w handleOnBackPressed może prowadzić do nieoczekiwanego wyjścia z aplikacji, jeśli w stosie nawigacji są ekrany w tle. Zawsze sprawdzaj NavController.backStack przez Navigation Component lub Coordinator przed zamknięciem Activity.
Jeśli zarejestrujesz callback bez LifecycleOwner (używając starego addCallback bez parametru), callback będzie żył wiecznie i może spowodować NullPointerException, jeśli Activity jest już zniszczona. Zawsze używaj addCallback(this, callback) z LifecycleOwner.
Dla okien modalnych (Bottom Sheet, Dialog) zawsze ustawiaj isEnabled = true tylko gdy okno jest widoczne. Użyj addCallback z lambdą, która sprawdza stan okna. Navigation Component automatycznie zarządza tym dla NavHost.
Szybkie podwójne naciśnięcie „Wstecz” może prowadzić do podwójnego wywołania finish(). Użyj flagi lub throttleLast do ochrony przed powtórnymi wywołaniami w ciągu 500 ms. Navigation Component wbudowanie obsługuje tę sytuację, ale w niestandardowych scenariuszach trzeba zaimplementować ochronę ręcznie.
private var lastBackPressTime = 0L
override fun handleOnBackPressed() {
val currentTime = System.currentTimeMillis()
if (currentTime - lastBackPressTime > 500) {
lastBackPressTime = currentTime
navigateBack()
}
}
Często zadawane pytania
onBackPressed został zdeprecjonowany w Android 11, ponieważ działa tylko na poziomie Activity. OnBackPressedDispatcher pozwala dowolnemu komponentowi (Fragment, Dialog, View) przechwytywać naciśnięcie przez jednolity mechanizm z obsługą cyklu życia.
System Android przekształca gest przesunięcia od krawędzi w systemowe naciśnięcie „Wstecz” zanim otrzyma je aplikacja. Na poziomie OnBackPressedDispatcher nie możesz rozróżnić tych dwóch zdarzeń — oba przychodzą jako handleOnBackPressed.
Tak, obsługa OnBackPressedDispatcher jest taka sama dla urządzeń z trzema przyciskami i z nawigacją gestami. Kod przechwytywania nie zależy od typu nawigacji — system sam przekształca gest w wywołanie dyspozytora.
Włącz predictive back w Developer Options emulatora Android 13+. Użyj ADB: `adb shell settings put global enable_back_animation 1`. Po włączeniu systemowa animacja pokaże podgląd przejścia przy naciśnięciu „Wstecz”.
Sprawdź dwa warunki: LifecycleOwner musi być w stanie STARTED lub RESUMED, a isEnabled callback musi być true. Jeśli oba warunki są spełnione, upewnij się, że callback został dodany do prawidłowego OnBackPressedDispatcher — użyj requireActivity().onBackPressedDispatcher we Fragment.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również