126

I have upgraded targetSdkVersion and compileSdkVersion to 33.

I am now getting a warning telling me that onBackPressed is deprecated.

I see suggestions to use android.window.OnBackInvokedCallback or androidx.activity.OnBackPressedCallback to handle back navigation instead. Can anyone can help me use the updated method?

Example

onBackPressedDeprecated

Use Case

I use if (isTaskRoot) {} inside the onBackPressed() method to check whether the activity is the last one on the activity stack.

override fun onBackPressed() {
    if (isTaskRoot) { // Check whether this activity is last on the activity stack. (Check whether this activity opened from a Push Notification.)
        startActivity(Intent(mContext, Dashboard::class.java))
        finish()
    } else {
        finishWithResultOK()
    }
}
Adil Hussain
  • 30,049
  • 21
  • 112
  • 147
Rumit Patel
  • 8,830
  • 18
  • 51
  • 70
  • 1
    If you're just calling `super.onBackPressed()`, then you can just remove the method entirely as you're not writing any custom back behavior. If you *are* writing custom back behavior, show your code. – ianhanniballake Jun 15 '22 at 16:19
  • @ianhanniballake , I am using `if (isTaskRoot) {}` inside `onBackPressed()` to check activity is last on the activity-stack. I have also updated the question. – Rumit Patel Jun 23 '22 at 13:08
  • You shouldn't be doing that at all on any API level. Please include your code. – ianhanniballake Jun 23 '22 at 13:27
  • The product detail page is opening clicking on the push notification. If the app is closed, now the user enters in product-detail-page clicking on push notification. Then clicking onBackpress, the user should not directly exit the app. for this case I use `onBackPressed().` – Rumit Patel Jun 23 '22 at 14:36
  • 1
    Did you figure out the `isTaskRoot` conditional? Most the answer seem to be very focused on trivial use-cases. – TWiStErRob Oct 09 '22 at 21:03
  • @TWiStErRob, Yes. `isTaskRoot` it's conditional. Whether the activity already has back stack activity or not. – Rumit Patel Oct 10 '22 at 05:33
  • Yeah, I got that, what I'm asking is how to do the conditional, at which point do you have the `if`? Because based on the dispatcher's code, it'll call the first enabled callback, and then stop. – TWiStErRob Oct 11 '22 at 08:12
  • To understand in detail: https://medium.com/@khadijahameed415/onbackpressed-deprecated-in-targetsdkversion-33-b4c27096bafe – Khadija Hameed Feb 02 '23 at 05:43
  • I am calling onBackPressed() to put the app into the background - that is my customized behavior – Gerry Mar 05 '23 at 18:29

18 Answers18

106

Replace onBackPressed() with below code.

onBackPressedDispatcher.onBackPressed()
Jai Khambhayta
  • 4,198
  • 2
  • 22
  • 29
91

According your API level register:

This requires to at least use appcompat:1.6.0-alpha03; the current is 1.6.0-alpha04:

 implementation 'androidx.appcompat:appcompat:1.6.0-alpha04'
// kotlin
import androidx.activity.addCallback

if (BuildCompat.isAtLeastT()) {
    onBackInvokedDispatcher.registerOnBackInvokedCallback(
        OnBackInvokedDispatcher.PRIORITY_DEFAULT
    ) {
        // Back is pressed... Finishing the activity
        finish()
    }
} else {
    onBackPressedDispatcher.addCallback(this /* lifecycle owner */, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            // Back is pressed... Finishing the activity
            finish()
        }
    })
}

// ====================================================
/* Or for lambda simplicity: */
// ====================================================
if (BuildCompat.isAtLeastT()) {
    onBackInvokedDispatcher.registerOnBackInvokedCallback(
        OnBackInvokedDispatcher.PRIORITY_DEFAULT
    ) {
        // Back is pressed... Finishing the activity
        finish()
    }
} else {
    onBackPressedDispatcher.addCallback(this /* lifecycle owner */) {
        // Back is pressed... Finishing the activity
        finish()
    }
}


UPDATE:

Thanks to @ianhanniballake comment; you can just use OnBackPressedDispatcher even in API level 33+

The OnBackPressedDispatcher is already going to be using the Android T specific API internally when using Activity 1.6+,

So, you can just do:

// kotlin
import androidx.activity.addCallback

onBackPressedDispatcher.addCallback(this /* lifecycle owner */, object : OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {
        // Back is pressed... Finishing the activity
        finish()
    }
})

// ====================================================
/* Or for lambda simplicity: */
// ====================================================
onBackPressedDispatcher.addCallback(this /* lifecycle owner */) {
    // Back is pressed... Finishing the activity
    finish()
}

// java
import androidx.activity.OnBackPressedCallback;

getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
    @Override
    public void handleOnBackPressed() {
        // Back is pressed... Finishing the activity
        finish();
    }
});

Note that you shouldn't override the onBackPressed() as that will make the onBackPressedDispatcher callback not to fire; check this answer for clarifying that.

Zain
  • 37,492
  • 7
  • 60
  • 84
  • 12
    The `OnBackPressedDispatcher` is already going to be using the Android T specific API internally when using Activity 1.6+, so there's absolutely no reason to ever use the Android T APIs directly - you can just use the `OnBackPressedDispatcher` on all API levels. – ianhanniballake Jun 15 '22 at 16:46
  • 1
    The problem I see here is now you have two ways to handle back press which is kinda messy. Is there any way to restrict overriding of the old `onBackPressed`? – Bitwise DEVS Jun 25 '22 at 22:02
  • How can I invoke `OnBackPressedCallback` `handleOnBackPressed` programmatically, say I have a button that needs to call `onBackPressed()` programmatically? – Bitwise DEVS Jun 25 '22 at 22:14
  • @BitwiseDEVS AFAIK, still `onBackPressed()` exposed to developers; at some newer API this shouldn't be true; but handling both simultaneously isn't that right as the note left at the end of the answer. – Zain Jun 27 '22 at 22:20
  • @BitwiseDEVS For calling `onBackPressed()` I'd rather prefer to pop the back stack; you can `finish()` the activity, or for navigation components using `navController.popBackStack()` – Zain Jun 27 '22 at 22:36
  • I get `Cannot resolve symbol 'addCallback'` when trying `import androidx.activity.addCallback`. – Luis A. Florit Sep 09 '22 at 21:55
  • @LuisA.Florit Have you upgraded to `implementation 'androidx.appcompat:appcompat:1.6.0-alpha04'`? – Zain Sep 10 '22 at 11:11
  • @Zain Yes. I also tried with the newer `1.6.0-rc01`. I also get `Cannot resolve symbol 'onBackPressedDispatcher'`. Maybe it is the Kotlin code that I am translating wrongly to Java...? – Luis A. Florit Sep 10 '22 at 15:59
  • @LuisA.Florit Just updated the answer with java snippet. Please have a look – Zain Sep 10 '22 at 16:52
  • Access the callback function by `onBackPressedDispatcher.onBackPressed()` – Ananthakrishnan K R Sep 12 '22 at 16:08
  • @Zain With your updated java code I can import now, yet I still get error: `Cannot resolve method 'getOnBackPressedDispatcher' in 'FullScreenBrowser'.` – Luis A. Florit Sep 12 '22 at 16:33
  • @LuisA.Florit Could you please open a question for that providing your gradle dependencies it seems that something not clear; I just test it out, it worked even in java. – Zain Sep 12 '22 at 16:39
  • 1
    @Zain It was my fault: I was extending Activity instead of AppCompatActivity. Sorry! – Luis A. Florit Oct 16 '22 at 00:48
  • @LuisA.Florit Wow it's a nightmare! Congrats! – Zain Oct 16 '22 at 03:29
  • If you're in a fragment, there is no "finish()". If you addCallback, but want to continue the normal back behavior, the only method I found is to disable the callback and hit back again. So from within the callback: this.isEnabled = false; requireActivity().onBackPressedDispatcher.onBackPressed(); – phazei Apr 10 '23 at 00:51
  • Dont forget to add ` android:enableOnBackInvokedCallback="true"` to manifest otherwiseit won;t work for newer API's – GeekWithGlasses Jul 26 '23 at 19:24
35

With a combination of top answers. Here is a solution :

1. When you need to press the back button, copy this :

Note: it will automatically destroy your activity.

button.setOnClickListener {
    onBackPressedDispatcher.onBackPressed()
}

2. When you need to handle the back button pressed, copy this :

onBackPressedDispatcher.addCallback(this, object: OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {
        // Whatever you want
        // when back pressed
        println("Back button pressed")
        finish()
    }
})
AI Shakil
  • 1,015
  • 10
  • 20
28

Simply replace

override fun onBackPressed() {
    super.onBackPressed() // Replace this deprecated line
}

with

override fun onBackPressed() {
    onBackPressedDispatcher.onBackPressed() // with this line
}

Update

Given that override fun onBackPressed() is deprecated, replace it with the following code:

onBackPressedDispatcher.addCallback(this, object: OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {
        println("Back button pressed")
        // Code that you need to execute on back press, e.g. finish()
  }
})

Add the above code to an activity lifecycle function like onCreate(savedInstanceState:).

Adil Hussain
  • 30,049
  • 21
  • 112
  • 147
Abdul Waheed
  • 562
  • 6
  • 14
  • 2
    'The above code we need to write inside some method like onCreate() function.' This is what I was missing, thank you.. – Scamparelli Jun 09 '23 at 15:04
11

In Kotlin, this way is working

1- Remove onBackPressed()

2- below onCreate(savedInstanceState: Bundle?) add these lines:

 if (Build.VERSION.SDK_INT >= 33) {
        onBackInvokedDispatcher.registerOnBackInvokedCallback(
            OnBackInvokedDispatcher.PRIORITY_DEFAULT
        ) {
           
            exitOnBackPressed()
        }
    } else {
        onBackPressedDispatcher.addCallback(
            this,
            object : OnBackPressedCallback(true) {
                override fun handleOnBackPressed() {
                  
                    Log.i("TAG", "handleOnBackPressed: Exit")
                    exitOnBackPressed()
                }
            })
    }

3- Define a new function for handling

fun exitOnBackPressed() {
}
Mori
  • 2,653
  • 18
  • 24
7

You could use the onBackPressedDispatcher

onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {
         
    }
})

in here "this" means the lifeCycleOwner

Yasiru Nayanajith
  • 1,647
  • 17
  • 20
6
import android.os.Bundle
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder

class SampleActivity : AppCompatActivity(R.layout.activity_sample) {

    private val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            showAppClosingDialog()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
    }

    private fun showAppClosingDialog() {
        MaterialAlertDialogBuilder(this)
            .setTitle("Warning")
            .setMessage("Do you really want to close the app?")
            .setPositiveButton("Yes") { _, _ -> finish() }
            .setNegativeButton("No", null)
            .show()
    }
}
Maruf Alam
  • 228
  • 4
  • 10
4

You can use the OnBackInvokedCallback

OnBackInvokedCallback as described in the documentation and follow this guide here to update your code

4

use onBackPressedDispatcher.onBackPressed() instead of super.onBackPressed()

override fun onBackPressed() {
  onBackPressedDispatcher.onBackPressed() 
}
Swapnil
  • 121
  • 7
4

For Kotlin Users:

If you are trying to call the Default Native Back Button function using the 'new' way, you can use the code bellow inside your Activity.

this@MyActivity.onBackPressedDispatcher.onBackPressed()

example:

myCloseButton.setOnClickListener { this@MyActivity.onBackPressedDispatcher.onBackPressed() }

The example above sets a click function inside a button, to make the button act like the Native Android Back Button.

However, if you want to customize the onBackPressedDispatcher, you can follow the example bellow, always inside your Activity, because this behaviour needs an Activity Context to work.

Example of Customized OnBackPressed:

override fun onCreate(savedInstanceState: Bundle?) {
    
   val callback: OnBackPressedCallback = object : OnBackPressedCallBack(true) {
        override fun handleOnBackPressed() {
            //ToDo Implement your custom event here    
        }
    }

    this@MyActivity.onBackPressedDispatcher.addCallback(this@MyActivity, callback)
}

The expected result is to make any onBackPressedDispatcher event to do whatever you want, or do nothing at all. But this is not recommended, since your user might get stuck in a screen, without being able to use his Phone's Back Button. Because of that, avoid leaving handleOnBackPressed override empty. So try something like this:

override fun handleOnBackPressed() {
    this@MyActivity.finish()
}
3

Bonus: To close DrawerLayout when onBackPressed use like below (according to this I/O talk),

val callback = onBackPressedDispatcher.addCallback(this, false) {
    binding.drawerLayout.closeDrawer(GravityCompat.START)
}
            
binding.drawerLayout.addDrawerListener(object : DrawerListener {  
        
    override fun onDrawerOpened(drawerView: View) {
        callback.isEnabled = true
    }
        
    override fun onDrawerClosed(drawerView: View) {
        callback.isEnabled = false
    }

    override fun onDrawerSlide(drawerView: View, slideOffset: Float) = Unit    
    override fun onDrawerStateChanged(newState: Int) = Unit
})
3

Here is the extension function to implement OnBackPressedCallback in activity.

fun AppCompatActivity.addOnBackPressedDispatcher(onBackPressed: () -> Unit = { finish() }) {
    onBackPressedDispatcher.addCallback(
        this,
        object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                onBackPressed.invoke()
            }
        }
    )
}

Usage:

addOnBackPressedDispatcher {
    //doSomething()
}
Abdullah Javed
  • 459
  • 3
  • 17
  • Are there any code samples for Xamarin C#? I have seen one for .NET but it used a private class and I couldn't get it to compile. I have tried installing some new Nuget classes, but that just became a rathole. Is there a simple way to replace my OnBackPressed calls? Thanks – fbs419 Oct 12 '22 at 23:35
  • https://stackoverflow.com/questions/73619091/using-onbackpressedcallback-in-xamarin – Abdullah Javed Oct 14 '22 at 13:03
  • 2
    The function should better be called `addOnBackPressedCallback()`, because that is what it does. There is only one Dispatcher which handles all the callbacks. – Ridcully Dec 05 '22 at 14:26
1

I solved this by simply adding an extension to AppCompatActivity:

fun AppCompatActivity.onBackButtonPressed(callback: (() -> Unit)? = null){
    val onBackPressed: OnBackPressedCallback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            callback?.let { callback() } ?: run { finish() }
        }
    }
    this.onBackPressedDispatcher.addCallback(this, onBackPressed)
}

So I can use it by calling "onBackButtonPressed" in two ways

1- Implement back pressed to finish activity

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // implement back pressed
    onBackButtonPressed()
}

2- Handle back pressed clicks:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // implement back pressed
    onBackButtonPressed {
        // handle back pressed click here,

    }
}
1

first,we have to add a callback object because, onBackPressedDispatcher.onBackPressed() triggers a call to the currently added callbacks in reverse order in which they were added. So after adding the callback only we should call the method onBackPressed()

The code (in kotlin) is as follows:

 onBackPressedDispatcher.addCallback(this,object :OnBackPressedCallback(true){
            override fun handleOnBackPressed() {
                Log.i("TAG","back has been called")
                finish()
            }
        })

onBackPressedDispatcher.onBackPressed()
helvete
  • 2,455
  • 13
  • 33
  • 37
0

override the onDismiss() function for BottomSheets.

override fun onDismiss(dialog: DialogInterface) {
   super.onDismiss(dialog)
   //TODO your logic.
   }
0

To set up ability to override default behaviour: (call setupBackPressedHandling() From Activity onCreate()):

  private fun setupBackPressedHandling() {
        onBackPressedDispatcher.addCallback(this) {
            if (!backPressHandled())
                invokeDefaultBackPressedHandling()
        }
    }

open fun backPressHandled(): Boolean = false

Global extension function:

  fun AppCompatActivity.invokeDefaultBackPressedHandling(navController: NavController?) {
    when(navController) {
        null -> {
            if (!supportFragmentManager.isStateSaved && !supportFragmentManager.popBackStackImmediate())
                finish()
        }
        else -> {
            if (!navController.popBackStack())
                finish()
        }
    }
}
hmac
  • 267
  • 3
  • 9
0

Most answers are calling finish() when you want to close the activity. This works fine for most cases but in some situations this doesn't work. For example, in Android 13, when you press back on the last activity in the stack and return to the home screen, onDestroy() is not called immediately and app remains in memory. If you open up the app again right then, it starts from onStart().

So, in some situations its better to let the system handle the closing of the app, or in the other words, let super.onBackPressed() happen.

To replace this

override fun onBackPressed() {
    if(showPopUpBeforeClosing){
        showDialog()
    } else {
        super.onBackPressed()
    }
}

do this -

onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {
        if (showPopUpBeforeClosing) {
            showDialog()
        } else {
            //Removing this callback
            remove()
            onBackPressedDispatcher.onBackPressed()
        }
    }
})

If you add a callback to the onBackPressedDispatcher, and call onBackPressedDispatcher.onBackPressed(), it always calls handleOnBackPressed(). There is no method like onSuperBackPressed() or something to let the system know to handle the backPress on its own once you're done. If you don't add the callback, then the system does its own thing, but if you've added it, then calling onBackPress will invoke handleOnBackPressed(). So what we do is, once you're done handling the back-press, you callback.remove() the callback removes itself and now when you do onBackPressedDispatcher.onBackPressed(), it will not invoke handleOnBackPressed() and handle the back-press as the system would do, which is equivalent to super.onBackPressed().

vepzfe
  • 4,217
  • 5
  • 26
  • 46
-1

Use like below,

override fun onClick(v: View?) {
    when (v?.id) {
        R.id.iv_back -> onBackPressedMethod()
    }
}

and now create that method for handling back event

private fun onBackPressedMethod(){
    if (Build.VERSION.SDK_INT >= 33) {
        onBackInvokedDispatcher.registerOnBackInvokedCallback(
            OnBackInvokedDispatcher.PRIORITY_DEFAULT) {
            // back button pressed... finishing the activity
            finish()
        }
    } else {
        onBackPressedDispatcher.addCallback(
            this,
            object : OnBackPressedCallback(true) {
                override fun handleOnBackPressed() {
                // back button pressed... finishing the activity
                finish()
                }
            })
    }
}

That's it!

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Hardik Hirpara
  • 2,594
  • 20
  • 34