Skip to content

Handling Back Button Press in Android: Replacing deprecated onBackPressed()

Problem Statement

With the release of Android 13 (API level 33) and newer versions of the AndroidX libraries, the traditional onBackPressed() method has been deprecated. Developers upgrading their targetSdkVersion and compileSdkVersion to 33 or higher now encounter warnings and need to migrate to the new back navigation system.

The deprecated approach looked like this:

kotlin
override fun onBackPressed() {
    if (isTaskRoot) {
        startActivity(Intent(mContext, Dashboard::class.java))
        finish()
    } else {
        finishWithResultOK()
    }
}

Solution Overview

Android now provides two main approaches for handling back navigation:

  1. OnBackPressedDispatcher (AndroidX) - Recommended for most cases, works back to API level 13
  2. OnBackInvokedCallback (Platform) - For API level 33+ only

The AndroidX implementation is generally preferred as it provides backward compatibility and integrates seamlessly with other Jetpack components.

Basic Implementation

Kotlin

kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                // Handle back button press
                if (isTaskRoot) {
                    startActivity(Intent(this@MainActivity, Dashboard::class.java))
                    finish()
                } else {
                    finishWithResultOK()
                }
            }
        })
    }
}

Java

java
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
            @Override
            public void handleOnBackPressed() {
                // Handle back button press
                if (isTaskRoot()) {
                    startActivity(new Intent(MainActivity.this, Dashboard.class));
                    finish();
                } else {
                    finishWithResultOK();
                }
            }
        });
    }
}

Advanced Usage Patterns

Simplified Lambda Syntax (Kotlin)

kotlin
onBackPressedDispatcher.addCallback(this) {
    if (isTaskRoot) {
        startActivity(Intent(this@MainActivity, Dashboard::class.java))
        finish()
    } else {
        finishWithResultOK()
    }
}

Extension Functions for Reusability

Create extension functions to simplify back press handling across your app:

kotlin
// Activity extension
fun Activity.onBackButtonPressed(callback: (() -> Boolean)) {
    (this as? FragmentActivity)?.onBackPressedDispatcher?.addCallback(
        this, 
        object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (!callback()) {
                    remove()
                    onBackPressedDispatcher.onBackPressed()
                }
            }
        }
    )
}

// Fragment extension
fun Fragment.onBackButtonPressed(callback: (() -> Boolean)) {
    requireActivity().onBackPressedDispatcher.addCallback(
        viewLifecycleOwner,
        object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (!callback()) {
                    remove()
                    requireActivity().onBackPressedDispatcher.onBackPressed()
                }
            }
        }
    )
}

Usage:

kotlin
// In your Activity or Fragment
onBackButtonPressed {
    if (shouldShowConfirmationDialog) {
        showConfirmationDialog()
        true // Prevent default behavior
    } else {
        false // Allow default behavior
    }
}

Integration with Navigation Components

For apps using Jetpack Navigation, you can integrate back press handling with your navigation graph:

With Bottom Navigation

kotlin
private val backPressedCallback = object : OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {
        NavigationUI.navigateUp(navController, appBarConfig)
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    
    // Setup navigation components
    val appBarConfig = AppBarConfiguration.Builder(setOf(R.id.home, R.id.profile, R.id.settings)).build()
    
    onBackPressedDispatcher.addCallback(this, backPressedCallback)
}

With Navigation Drawer

kotlin
private val backPressedCallback = object : OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {
        if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
            drawerLayout.closeDrawer(GravityCompat.START)
        } else {
            NavigationUI.navigateUp(navController, drawerLayout)
        }
    }
}

Handling the Up Button

When using AppCompatActivity, don't forget to handle the up button in the toolbar:

kotlin
override fun onSupportNavigateUp(): Boolean {
    onBackPressedDispatcher.onBackPressed()
    return true
}

Manual Back Press Trigger

To programmatically trigger back navigation (e.g., from a custom button):

kotlin
backButton.setOnClickListener {
    onBackPressedDispatcher.onBackPressed()
}

Platform-Specific Implementation (API 33+)

WARNING

This approach is only necessary if you're not using AndroidX libraries. For most cases, the AndroidX solution is recommended for its backward compatibility.

kotlin
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    onBackInvokedDispatcher.registerOnBackInvokedCallback(
        OnBackInvokedDispatcher.PRIORITY_DEFAULT
    ) {
        // Handle back press for API 33+
        if (isTaskRoot) {
            startActivity(Intent(this, Dashboard::class.java))
            finish()
        } else {
            finishWithResultOK()
        }
    }
} else {
    // Use AndroidX implementation for older versions
    onBackPressedDispatcher.addCallback(this) {
        if (isTaskRoot) {
            startActivity(Intent(this@MainActivity, Dashboard::class.java))
            finish()
        } else {
            finishWithResultOK()
        }
    }
}

Best Practices

  1. Use AndroidX Implementation: Prefer OnBackPressedDispatcher over the platform-specific OnBackInvokedCallback for better compatibility
  2. Proper Lifecycle Management: Ensure callbacks are tied to the appropriate lifecycle owner to prevent memory leaks
  3. Conditional Removal: Use remove() on the callback when you want to revert to default behavior
  4. Test Thoroughly: Test your back navigation across different Android versions and navigation patterns

Common Pitfalls

DANGER

Avoid overriding onBackPressed() when using OnBackPressedDispatcher as it will prevent the dispatcher callbacks from firing.

kotlin
// ❌ Don't do this when using dispatcher callbacks
override fun onBackPressed() {
    // This will override all dispatcher callbacks
    super.onBackPressed()
}

Migration Example

Converting from the deprecated approach:

kotlin
// ❌ Deprecated
override fun onBackPressed() {
    if (isTaskRoot) {
        startActivity(Intent(mContext, Dashboard::class.java))
        finish()
    } else {
        finishWithResultOK()
    }
}

// ✅ Modern approach
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...
    
    onBackPressedDispatcher.addCallback(this) {
        if (isTaskRoot) {
            startActivity(Intent(this@MainActivity, Dashboard::class.java))
            finish()
        } else {
            finishWithResultOK()
        }
    }
}

Conclusion

The migration from onBackPressed() to the new back navigation system is straightforward with the AndroidX OnBackPressedDispatcher. This modern approach provides better flexibility, lifecycle awareness, and compatibility across Android versions. By using the patterns and examples provided, you can effectively handle back navigation in your Android applications while following current best practices.