Back Press Handling trong Android: bản chất, cơ chế và triển khai

Tác giả: IT Sectr Đã đăng: 2026-06-10 Thời gian đọc: 9 phút

Back Press Handling là cơ chế chặn và xử lý nút Quay lại hệ thống trong Android, xác định hành động nào được thực hiện khi nhấn nút. Theo Android Developers (2024), bắt đầu từ Android 11, phương thức onBackPressed() truyền thống đã được thay thế bằng OnBackPressedDispatcher. API mới cho phép các thành phần ở mọi cấp độ trong hệ thống phân cấp chặn nhấn nút, không chỉ Activity. Sự khác biệt chính — hỗ trợ nhiều callback trong chuỗi có ưu tiên.

Những điểm chính

  • Back Press Handling — cơ chế xử lý nút Quay lại hệ thống trong Android
  • OnBackPressedDispatcher — API mới thay thế onBackPressed() đã lỗi thời
  • Callback được đăng ký với ưu tiên và thực thi theo thứ tự chuỗi
  • Jetpack Compose sử dụng BackHandler để chặn nhấn nút trong composable
  • Support Library cung cấp khả năng tương thích ngược đến API Level 14

Back Press Handling là gì

Back Press Handling là một cơ chế hệ thống trong Android xác định điều gì xảy ra khi người dùng nhấn nút Quay lại phần cứng hoặc phần mềm. Tùy thuộc vào ngữ cảnh, nhấn nút có thể: đóng màn hình hiện tại và quay lại màn hình trước, ẩn bàn phím, đóng Drawer hoặc Bottom Sheet, thoát khỏi ứng dụng nếu người dùng đang ở màn hình gốc.

Hành vi của nút Quay lại đã tiến hóa qua từng phiên bản Android. Android 10 giới thiệu điều hướng bằng cử chỉ, Android 11 giới thiệu OnBackPressedDispatcher như API tiêu chuẩn, và Android 13 mang lại hỗ trợ cải thiện cho predictive back gesture, nơi hệ thống hiển thị hoạt ảnh chuyển tiếp trước khi hành động thực sự được thực thi. Google đang nhất quán hướng tới hành vi Back Press có thể dự đoán và nhất quán trên tất cả các thiết bị.

Xử lý đúng nút Quay lại là một yếu tố UX quan trọng trong ứng dụng Android. Người dùng mong đợi nhấn Quay lại sẽ đưa họ về màn hình trước trong ngăn xếp điều hướng, chứ không phải đóng ứng dụng một cách bất ngờ. Vi phạm kỳ vọng này là một trong những nguyên nhân chính dẫn đến đánh giá tiêu cực và xếp hạng thấp trên Google Play.

Sự tiến hóa của Back Press API: từ onBackPressed đến OnBackPressedDispatcher

Lịch sử của Back Press API trong Android phản ánh sự tiến hóa chung của nền tảng: từ một phương thức đơn giản trong Activity đến một hệ thống callback linh hoạt hỗ trợ vòng đời và Compose. Hãy xem xét ba giai đoạn phát triển.

Kỷ nguyên onBackPressed (API Level 1–30)

Ngay từ API Android đầu tiên, nút Quay lại được xử lý trong phương thức onBackPressed của lớp Activity. Nhà phát triển ghi đè phương thức này và viết logic riêng của mình. Vấn đề là Fragment và View không thể chặn nhấn nút — tất cả điều khiển đều thông qua Activity. Điều này dẫn đến Activity phình to và chuỗi if-else phức tạp để xác định ai nên xử lý nhấn nút.

Sự xuất hiện của OnBackPressedDispatcher (Activity 1.0.0)

Với Activity 1.0.0 (AndroidX), Google đã giới thiệu OnBackPressedDispatcher. Đây là một bộ điều phối trung tâm chấp nhận callback từ bất kỳ thành phần nào — Activity, Fragment, Dialog, View tùy chỉnh. Các callback được đăng ký theo thứ tự (thông qua priority) và có thể được thêm hoặc xóa động. OnBackPressedDispatcher được gọi trước onBackPressed cũ, cho phép chặn nhấn nút trước khi Activity xử lý.

Predictive Back Gesture (Android 13+)

Android 13 đã giới thiệu predictive back gesture — hoạt ảnh hệ thống hiển thị nơi nhấn Quay lại sẽ dẫn đến trước khi người dùng hoàn thành cử chỉ. Để hỗ trợ hoạt ảnh này, các nhà phát triển phải sử dụng OnBackPressedDispatcher và chỉ định liệu callback có hỗ trợ hoạt ảnh hệ thống thông qua thuộc tính isEnabled hay không. Nếu callback không hỗ trợ predictive animation, hệ thống hiển thị hoạt ảnh mặc định có thể không khớp với ngữ cảnh ứng dụng.

APISDK tối thiểuHỗ trợ FragmentPredictive Back
onBackPressedAPI Level 1Qua ActivityKhông
OnBackPressedDispatcherActivity 1.0.0Trực tiếpMột phần
OnBackPressedDispatcher + lifecycleActivity 1.3.0Lifecycle-awareĐầy đủ

OnBackPressedDispatcher — Kiến trúc và chuỗi callback

OnBackPressedDispatcher là cốt lõi của API Back Press mới. Nó quản lý một chuỗi các callback, gọi chúng theo thứ tự cho đến khi callback đầu tiên xử lý sự kiện. Nếu không có callback nào xử lý nhấn nút, bộ điều phối thực thi hành động mặc định — gọi finish() cho Activity hoặc popBackStack() cho Navigation Component.

Đăng ký callback với ưu tiên

Một callback được đăng ký qua addCallback với LifecycleOwner và đối tượng OnBackPressedCallback. Callback có thuộc tính isEnabled — nếu được đặt thành false, callback sẽ bị bỏ qua. Đối với ưu tiên, bạn có thể truyền giá trị từ 0 (thấp nhất) đến Integer.MAX_VALUE. Fragment Activity Result API sử dụng cơ chế này để tự động đăng ký callback gắn với vòng đời.

kotlin
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)
}

Dọn dẹp Lifecycle-aware

Callback được tự động xóa khi LifecycleOwner chuyển sang trạng thái DESTROYED. Điều này giải quyết vấn đề rò rỉ callback cũ khi xoay màn hình. Nếu callback được thêm trong Fragment, nó được đảm bảo sẽ bị xóa khi Fragment bị hủy. Để tạm thời vô hiệu hóa callback, hãy sử dụng thuộc tính isEnabled — bạn có thể chuyển đổi mà không cần xóa và đăng ký lại.

Chuỗi gọi

Thứ tự gọi ngược với thứ tự thêm: callback được thêm cuối cùng nhận quyền điều khiển trước. Điều này hợp lý vì phần tử UI lồng nhau nhất (ví dụ: Bottom Sheet trong Fragment) nên xử lý nhấn nút trước Fragment cha của nó. Nếu callback sâu nhất không xử lý nhấn nút (isEnabled = false), quyền điều khiển sẽ chuyển đến callback tiếp theo trong chuỗi.

Back Press trong Fragment và hộp thoại

Fragment API cung cấp tích hợp riêng với OnBackPressedDispatcher thông qua phương thức requireActivity().onBackPressedDispatcher. Bắt đầu từ Fragment 1.2.0, mỗi Fragment có thể đăng ký callback riêng của mình, tự động gắn với vòng đời của Fragment và bị xóa khi nó bị hủy.

Callback trong Fragment

Đăng ký callback trong Fragment được thực hiện trong onCreate, onViewCreated hoặc thậm chí trong chính View — điều quan trọng là LifecycleOwner (Fragment) đang hoạt động. Khi Fragment chuyển sang trạng thái STARTED, callback được kích hoạt; khi STOPPED, nó bị vô hiệu hóa. Điều này đảm bảo rằng một Fragment ẩn (trong ViewPager) sẽ không xử lý nhấn Quay lại.

kotlin
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)
    }
}

Dialog và BottomSheetDialog

Hộp thoại và BottomSheets tự động chặn nhấn Quay lại để đóng. Nếu bạn cần thực hiện hành động bổ sung trước khi đóng — hãy đăng ký callback với ưu tiên cao hơn. Quan trọng: nếu bạn đặt setCancelable(false) trên Dialog, callback sẽ không kích hoạt — đây là hành vi hệ thống.

Back Press Handling trong Jetpack Compose

Jetpack Compose cung cấp API khai báo để xử lý nút Quay lại thông qua hàm composable BackHandler. BackHandler chấp nhận enabled (trạng thái boolean) và onBack — callback được gọi khi nhấn nút. Nếu enabled = false, nhấn nút được chuyển tiếp xuống chuỗi.

BackHandler trong Compose

BackHandler tự động đăng ký OnBackPressedCallback trong OnBackPressedDispatcher của Activity cha. Nó tôn trọng vòng đời của composable: khi rời khỏi thành phần, callback sẽ bị xóa. enabled có thể được gắn với trạng thái — ví dụ: chỉ hiển thị hộp thoại xác nhận nếu biểu mẫu có thay đổi chưa được lưu.

kotlin
@Composable
fun EditScreen(hasUnsavedChanges: Boolean) {
    BackHandler(enabled = hasUnsavedChanges) {
        // Show confirmation dialog
    }

    Column {
        TextField(value = ..., onValueChange = ...)
    }
}

Predictive Back trong Compose

Predictive back gesture trong Compose được hỗ trợ từ Compose 1.5.0. BackHandler tự động xử lý hoạt ảnh chuyển tiếp hệ thống nếu nó được bật trên thiết bị. Đối với hoạt ảnh predictive tùy chỉnh, hãy sử dụng bổ từ predictiveBackHandler, trả về tiến trình cử chỉ từ 0 đến 1.

Lỗi thường gặp và thực hành tốt nhất

Back Press Handling có vẻ đơn giản, nhưng trong thực tế, các nhà phát triển mắc một số lỗi hệ thống. Hãy xem xét các vấn đề phổ biến nhất và giải pháp của chúng dựa trên khuyến nghị của Google và kinh nghiệm cộng đồng.

Lỗi: gọi finish() mà không kiểm tra ngăn xếp điều hướng

Gọi trực tiếp finish() trong handleOnBackPressed có thể dẫn đến thoát ứng dụng bất ngờ nếu có màn hình nền trong ngăn xếp điều hướng. Luôn kiểm tra NavController.backStack thông qua Navigation Component hoặc Coordinator trước khi đóng Activity.

Lỗi: bỏ qua vòng đời khi đăng ký callback

Nếu bạn đăng ký callback mà không có LifecycleOwner (sử dụng addCallback cũ không có tham số), callback sẽ tồn tại mãi mãi và có thể gây ra NullPointerException nếu Activity đã bị hủy. Luôn sử dụng addCallback(this, callback) với LifecycleOwner.

Thực hành tốt nhất: ngắt chuỗi cho cửa sổ phương thức

Đối với cửa sổ phương thức (Bottom Sheet, Dialog), luôn đặt isEnabled = true chỉ khi cửa sổ hiển thị. Sử dụng addCallback với lambda kiểm tra trạng thái cửa sổ. Navigation Component tự động quản lý điều này cho NavHost.

Thực hành tốt nhất: xử lý nhấn đúp

Nhấn đúp nhanh Quay lại có thể dẫn đến gọi đúp finish(). Sử dụng cờ hoặc throttleLast để bảo vệ khỏi các cuộc gọi lặp lại trong vòng 500 ms. Navigation Component xử lý tình huống này một cách tự nhiên, nhưng trong các tình huống tùy chỉnh, bạn cần triển khai bảo vệ thủ công.

kotlin
private var lastBackPressTime = 0L

override fun handleOnBackPressed() {
    val currentTime = System.currentTimeMillis()
    if (currentTime - lastBackPressTime > 500) {
        lastBackPressTime = currentTime
        navigateBack()
    }
}

Câu hỏi thường gặp

Tại sao onBackPressed bị lỗi thời?

onBackPressed bị lỗi thời trong Android 11 vì nó chỉ hoạt động ở cấp Activity. OnBackPressedDispatcher cho phép bất kỳ thành phần nào (Fragment, Dialog, View) chặn nhấn nút thông qua một cơ chế duy nhất hỗ trợ vòng đời.

Làm thế nào để phân biệt nhấn Quay lại với cử chỉ vuốt?

Hệ thống Android chuyển đổi cử chỉ vuốt từ cạnh thành nhấn Quay lại hệ thống trước khi ứng dụng nhận được nó. Ở cấp OnBackPressedDispatcher, bạn không thể phân biệt hai sự kiện này — cả hai đều đến dưới dạng handleOnBackPressed.

Tôi có cần hỗ trợ nút Quay lại trên thiết bị có cử chỉ không?

, xử lý OnBackPressedDispatcher giống nhau cho thiết bị ba nút và điều hướng bằng cử chỉ. Mã chặn không phụ thuộc vào loại điều hướng — hệ thống tự chuyển đổi cử chỉ thành cuộc gọi bộ điều phối.

Làm thế nào để kiểm tra predictive back gesture trên trình giả lập?

Bật predictive back trong Developer Options của trình giả lập Android 13+. Sử dụng ADB: `adb shell settings put global enable_back_animation 1`. Sau khi bật, hoạt ảnh hệ thống sẽ hiển thị xem trước chuyển tiếp khi nhấn Quay lại.

Phải làm gì nếu callback không được gọi?

Kiểm tra hai điều kiện: LifecycleOwner phải ở trạng thái STARTED hoặc RESUMED, và isEnabled của callback phải là true. Nếu cả hai điều kiện được đáp ứng, hãy đảm bảo callback được thêm vào OnBackPressedDispatcher chính xác — sử dụng requireActivity().onBackPressedDispatcher trong Fragment.

Tổng kết

  • Back Press Handling — cơ chế xử lý nút Quay lại hệ thống trong Android
  • OnBackPressedDispatcher — API hiện đại thay thế onBackPressed() từ Android 11
  • Callback được đăng ký qua addCallback với LifecycleOwner và tự động dọn dẹp
  • Chuỗi gọi — callback được thêm cuối cùng với isEnabled=true xử lý nhấn nút đầu tiên
  • Trong Fragment callback gắn với vòng đời và bị vô hiệu hóa khi fragment bị ẩn
  • Jetpack Compose sử dụng BackHandler composable để xử lý khai báo
  • Predictive back gesture — hoạt ảnh chuyển tiếp hệ thống có sẵn từ Android 13+

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm