15

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?

5
  • 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 Commented Jul 4, 2022 at 8:07
  • @AlvinDizon nope, based on this answer one has no backward compatibility while the other handles it internally. Commented Jul 5, 2022 at 22:28
  • I see, thanks for posting that Commented Jul 5, 2022 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 Commented Jul 12, 2022 at 0: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. Commented Jul 13, 2022 at 0:07

1 Answer 1

10
+100

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.

4
  • 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. Commented Sep 23, 2022 at 11:22
  • What is mOnBackPressedDispatcher?? Commented Jun 8, 2023 at 14:31
  • 1
    @IgorGanapolsky here 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. Commented Jun 8, 2023 at 20:50
  • Does this onBackPressedDispatcher have backward compatibility with older versions of Android SDK ? Commented Jun 30, 2023 at 16:41

Not the answer you're looking for? Browse other questions tagged or ask your own question.