Android 16 预测性返回适配:React Native 混合应用返回键的正确处理

Android 16(API 36)及以上设备,在 React Native 混合应用(原生 Activity 嵌套 RN Fragment)中,点击物理返回键时,应用直接退到后台(桌面),而不是先返回 RN 内部的上一级页面。

Android 15 及以下,返回键的处理流程如下:

物理按键 → dispatchKeyEvent() → onKeyDown() → onBackPressed() → 自定义逻辑

onBackPressed() 的默认实现是 finish()(销毁 Activity),而开发者通常重写 onBackPressed() 或在其调用链中插入自定义逻辑(如"先让 RN 消费返回,否则退到后台")。

旧代码的返回逻辑是"挂载"在 onBackPressed() 这个环节上的。

而Android 16 引入了预测性返回(Predictive Back)动画,系统需要在执行返回动作之前提前预览返回后的界面。为了支持这个特性,旧的 KeyEvent.KEYCODE_BACK 分发机制被废弃。dispatchKeyEvent() → onKeyDown() → onBackPressed() 这条调用链在 Android 16 上被绕过。系统强制使用 OnBackPressedDispatcher 来管理所有返回事件。

再看下,旧代码失效,是因为HomeActivity 中原本有这段逻辑:

Kotlin 复制代码
override fun onBackPressedSupport() {
    if (currentFragment?.onBackPressedSupport() == true) {
        return
    }
    moveTaskToBack(true)  // 预期:退到后台
}

这段逻辑本身是正确的,但它没有注册到新的 OnBackPressedDispatcher 上。在 Android 16 中:

系统检查 OnBackPressedDispatcher,发现如果没有注册任何回调,系统执行默认策略:调用 super.onBackPressed() → finish() 。 由于 HomeActivity 是应用的根 Activity,finish() 会销毁 Activity 并将任务栈切到桌面。用户感知为:直接退到后台(跳过了 RN 内部页面的逐级返回)

Android 16 及以上(新调用链):

解决方案是,在 HomeActivity的 onCreate 中注册 OnBackPressedCallback,在回调中实现返回逻辑。 代码如下:

Kotlin 复制代码
class MainActivity : AppCompatActivity() {

    private var currentFragment: RNHostFragment? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 初始化代码。。。

        // 适配 Android 16 预测性返回
        onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                // 1. 优先让 RN Fragment 处理返回
                if (rnFragment?.onBackPressedSupport() == true) {
                    return
                }
                // 2. 如果 RN 未消费,则退到后台
                moveTaskToBack(true)
            }
        })
    }


    // 兼容旧版(可选,保留作为兜底)
    override fun onBackPressed() {
        if (rnFragment?.onBackPressedSupport() == true) {
            return
        }
        moveTaskToBack(true)
    }
}

RNHostFragment 内部需要实现一个查询方法 onBackPressedSupport(): Boolean:

返回 true:RN 内部有页面可返回,已执行 Pop 操作

返回 false:RN 已在根页面,需 Activity 自己处理

Kotlin 复制代码
class RNHostFragment : Fragment() {
    // 查询 RN 导航栈状态,返回 true 表示已消费
    fun onBackPressedSupport(): Boolean {
        // 具体实现取决于 具体 RN 封装
        // 例如:调用 ReactInstanceManager 的 getCurrentActivity()?.hasBackStack()
        return reactNavigationStack?.canGoBack() ?: false
    }
}

改完测试ok.