Skip to content

onBackPressed 替代方案

随着 Android 13 (API 33) 的发布,传统的 onBackPressed() 方法已被标记为弃用。本文介绍如何迁移到新的回退导航 API,并提供多种实用解决方案。

问题背景

在将 targetSdkVersioncompileSdkVersion 升级到 33 后,开发者会遇到 onBackPressed() 已弃用的警告。原始使用场景通常包括:

kotlin
override fun onBackPressed() {
    if (isTaskRoot) { // 检查是否是任务栈中最后一个活动
        startActivity(Intent(mContext, Dashboard::class.java))
        finish()
    } else {
        finishWithResultOK()
    }
}

推荐的解决方案

基本迁移方案

最简单的迁移方式是使用 OnBackPressedDispatcher

kotlin
// Kotlin
onBackPressedDispatcher.onBackPressed()

// Java
getOnBackPressedDispatcher().onBackPressed();

完整的回调实现

对于需要自定义处理的情况,使用 OnBackPressedCallback

kotlin
// 在 onCreate 中添加回调
private val backPressedCallback = object : OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {
        if (isTaskRoot) {
            startActivity(Intent(this@MyActivity, Dashboard::class.java))
            finish()
        } else {
            finishWithResultOK()
        }
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ...其他初始化代码
    
    onBackPressedDispatcher.addCallback(this, backPressedCallback)
}
java
// Java 版本
private final OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) {
    @Override
    public void handleOnBackPressed() {
        if (isTaskRoot()) {
            startActivity(new Intent(MyActivity.this, Dashboard.class));
            finish();
        } else {
            finishWithResultOK();
        }
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...其他初始化代码
    
    getOnBackPressedDispatcher().addCallback(this, backPressedCallback);
}

扩展函数方案

创建扩展函数可以简化回退处理逻辑:

Activity 扩展

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

fun Activity.performBackPress() {
    (this as? FragmentActivity)?.onBackPressedDispatcher?.onBackPressed()
}

Fragment 扩展

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

fun Fragment.performBackPress() {
    requireActivity().onBackPressedDispatcher.onBackPressed()
}

使用方式

kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
    onBackButtonPressed {
        // 执行自定义逻辑
        // 返回 true 表示已处理回退事件
        // 返回 false 让系统处理回退
        true
    }
}

对于使用 Jetpack Navigation 的应用:

kotlin
// 处理回退和向上导航
fun Fragment.onBackOrUpButtonPressed(navController: NavController, callback: () -> Boolean) {
    requireActivity().onBackPressedDispatcher.addCallback(
        viewLifecycleOwner,
        object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                performBackPress(navController, callback)
            }
        })
    
    requireActivity().addMenuProvider(object : MenuProvider {
        override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
            // 不需要为"向上"按钮创建菜单项
        }
        
        override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
            if (menuItem.itemId == android.R.id.home) {
                performBackPress(navController, callback)
            }
            return true
        }
    }, viewLifecycleOwner)
}

特殊情况处理

让系统处理默认回退行为

重要提示

如果只是简单调用 finish(),在某些 Android 版本中可能不会立即销毁 Activity。要模拟原来的 super.onBackPressed() 行为,需要移除回调后再次调用回退:

kotlin
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
    override fun handleOnBackPressed() {
        if (showPopUpBeforeClosing) {
            showDialog()
        } else {
            remove() // 移除回调
            onBackPressedDispatcher.onBackPressed() // 让系统处理
        }
    }
})

处理 DrawerLayout 和底部导航

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

版本兼容性考虑

虽然 Android 13 引入了 OnBackInvokedCallback,但官方推荐使用 OnBackPressedDispatcher,因为它已内部处理了版本兼容性问题:

kotlin
// 无需手动检查版本,OnBackPressedDispatcher 会自动处理
onBackPressedDispatcher.addCallback(this) {
    // 回退处理逻辑
    finish()
}

总结

迁移到新的回退导航 API 是面向未来的必要步骤。关键要点:

  1. 使用 OnBackPressedDispatcher 替代直接重写 onBackPressed()
  2. 通过 OnBackPressedCallback 实现自定义回退逻辑
  3. 利用扩展函数简化代码结构
  4. 合理处理系统默认回退行为
  5. 与 Navigation Component 无缝集成

新的 API 提供了更灵活的回退导航控制,同时保证了不同 Android 版本间的兼容性。