onBackPressed() is deprecated. What is the alternative?

120.7k Views Asked by At

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()
    }
}
21

There are 21 best solutions below

17
On BEST ANSWER

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.

0
On

you can use onBackPressedDispatcher. Below an example:

val back = this.onBackPressedDispatcher
back.addCallback(this, object : OnBackPressedCallback(true){
    override fun handleOnBackPressed() {
        //println("back pressed")
    }
})
0
On

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

override fun onBackPressed() {
  onBackPressedDispatcher.onBackPressed() 
}
0
On

override the onDismiss() function for BottomSheets.

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

You can use the OnBackInvokedCallback

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

2
On

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:).

0
On

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,

    }
}
0
On

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()
1
On

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().

2
On

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.

[email protected]()

example:

myCloseButton.setOnClickListener { [email protected]() }

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

    [email protected](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() {
    [email protected]()
}
0
On
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()
    }
}
0
On

You could use the onBackPressedDispatcher

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

in here "this" means the lifeCycleOwner

0
On

If you're using Java then here's a way I'm doing it.

// Creating a global variable
private final OnBackPressedDispatcher onBackPressedDispatcher = getOnBackPressedDispatcher();
// In your onCreate method
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.my_activity);

   // Add callback listener
   onBackPressedDispatcher.addCallback(new OnBackPressedCallback(true) {
       @Override
       public void handleOnBackPressed() {
           // Handle onback pressed
       }
   });
}

Then if you want to force call onBackPressed you can do it using:

mBtnBack.setOnClickListener(v -> onBackPressedDispatcher.onBackPressed());
0
On

If you like the back button handled you need this part in your onCreate() method of your activity. Don't forget to actually finish the activity in the handleOnBackPressed().

getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
    @Override
    public void handleOnBackPressed() {
        //Add custom actions here (like before in onBackPressed())
        finish();
    }
});

If you have a up arrow inside your activity, this involves the method onSupportNavigateUp() for AppCompatActivity.

So you also need to handle this. Do it this way.

@Override
public boolean onSupportNavigateUp() {
    getOnBackPressedDispatcher().onBackPressed();
    return true;
}
3
On

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()
}
4
On

Replace onBackPressed() with below code.

onBackPressedDispatcher.onBackPressed()
4
On

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()
    }
})
0
On

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) {
               onBackPressed.invoke()
        }
    } else {
        onBackPressedDispatcher.addCallback(
            this,
            object : OnBackPressedCallback(true) {
                override fun handleOnBackPressed() {
                  onBackPressed.invoke()
                }
            })
    }
}

That's it!

0
On

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
})
0
On

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() {
}
0
On

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