13

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?

Neoh
  • 15,906
  • 14
  • 66
  • 78
Bitwise DEVS
  • 2,858
  • 4
  • 24
  • 67
  • My understanding is that registering an `OnBackInvokedCallback` to an Activity's `onBackInvokedDispatcher` replaces usages of custom back invocations usually put in `onBackPressed()`. Fragments on the other hand use `onBackPressedDispatcher`/`OnBackPressedCallback` – Alvin Dizon Jul 04 '22 at 08:07
  • @AlvinDizon nope, based on this [answer](https://stackoverflow.com/a/72634975/12204620) one has no backward compatibility while the other handles it internally. – Bitwise DEVS Jul 05 '22 at 22:28
  • I see, thanks for posting that – Alvin Dizon Jul 05 '22 at 23:02
  • If I try using onBackPressedCallback on activities, on first back press nothing happens, second backpress is when things work, so I'm not sure what's going on here – Alvin Dizon Jul 12 '22 at 00:06
  • @AlvinDizon there are many possible where onBackPressedCallback is not working, first is you probably pass the LifecycleOwner in `addCallback`. This can cause some issue like when your activity goes to onPause and onStop because another activity was open above it or the app was to minimize, the added callback will be remove internally in this case. Other possible reason is you set false during initialization of `OnBackPressedCallback(false)` or set the callback `.isEnabled` to false which also prevent it to work. – Bitwise DEVS Jul 13 '22 at 00:07

1 Answers1

9

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.

Pavlo Ostasha
  • 14,527
  • 11
  • 35
  • Thanks a lot, I used a wrong terminology it is not actually iterating but a stack. What I did was to add a callback whenever the fragment is resumed and remove it whenever it is onstop. I also remove lifecycle for the callback on activity since providing it will take the back control away from its fragments which is usually a not preferred behavior. – Bitwise DEVS Sep 23 '22 at 11:22
  • What is `mOnBackPressedDispatcher`?? – IgorGanapolsky Jun 08 '23 at 14:31
  • 1
    @IgorGanapolsky [here](https://developer.android.com/reference/androidx/activity/OnBackPressedDispatcher) it is. Basically, an abstraction that was created to encapsulate and decouple back navigation from the UI. First appeared in AndroidX libraries if I remember correctly. It is responsible for the back navigation within activities and fragments giving much more flexibility and space for the needed manipulations during the overall back navigation routine disregarding the source of the back event. Much more verbose than before, but also more comfortable IMHO. – Pavlo Ostasha Jun 08 '23 at 20:50
  • Does this onBackPressedDispatcher have backward compatibility with older versions of Android SDK ? – Kevin Meneses Palta Jun 30 '23 at 16:41