androidandroid-jetpackandroid-jetpack-navigationonbackpressedandroid-tiramisu

onBackPressedDispatcher.onBackPressed() vs backPressedCallback.handleOnBackPressed()


Since the old Activity.onBackPressed() becomes deprecated starting Android 33, what is the better way to call it programmatically?

Example:

override fun onOptionsItemSelected(item: MenuItem): Boolean {

        when (item.itemId) {

            // Handle default back arrow click
            android.R.id.home -> {
                onBackPressed()
            }
 ...

We could create and add OnBackPressedCallback to the onBackPressedDispatcher like this.

onBackPressedDispatcher.addCallback(
            this, // Lifecycle owner
            backPressedCallback
        )

private val backPressedCallback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            if (viewPager.currentItem != 0)
                viewPager.setCurrentItem(0, true)
            else
                finish()
        }
    }

Then replace the old onBackPressed with

// Handle default back arrow click
            android.R.id.home -> {
                backPressedCallback.handleOnBackPressed()
            }

But I saw this public method in onBackPressedDispatcher and wondering if I could use it instead.

onBackPressedDispatcher.onBackPressed()

Does this method iterates on each OnBackPressedCallback that has been added in the onBackPressedDispatcher?


Solution

  • So basically onBackPressedDispatcher.onBackPressed() is the same as Activity.onBackPressed() and you can use it in the same manner if you don't care about precise navigation. How do I know that? - well, you can see the source code of the ComponentActivity(basically a parent of a regular Activity you are using), and its onBackPressed() looks like this:

        @Override
        @MainThread
        public void onBackPressed() {
            mOnBackPressedDispatcher.onBackPressed();
        }
    

    Regarding it calling over the callbacks queue - you are correct also, but it is not iterating over it - but just calling the most recent one - one at a time(per back press or per onBackPressed() call trigger), the documentation states:

    public void onBackPressed()

    Trigger a call to the currently added callbacks in reverse order in which they were added. Only if the most recently added callback is not enabled will any previously added callback be called.

    If hasEnabledCallbacks is false when this method is called, the fallback Runnable set by the constructor will be triggered.

    So your strategy here might be like this - if you need some specific stuff to be executed before the back navigation - you add it to the handleOnBackPressed of the callback. If no special behavior needed - you can just call mOnBackPressedDispatcher.onBackPressed() - it will still call the most recently added(if it is there of course) callback method, but if it is empty - the back will work just fine.

    You need to keep in mind, though, that there are two overrides of addCallback methods:

    addCallback(@NonNull OnBackPressedCallback onBackPressedCallback)
    

    and

    public void addCallback(
        @NonNull LifecycleOwner owner,
        @NonNull OnBackPressedCallback onBackPressedCallback
    )
    

    In the former - you have to handle the callback queue by yourself calling remove on callback when you need it not to be executed anymore. In the latter - LifecycleOwner state change has to handle all the needed stuff for you.

    More info here and here.