You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用BiometricPrompt验证PIN时调用Activity被销毁的问题排查

问题分析与解决方案

核心原因推测

问题仅出现在推送触发的TransactionActivity中,登录流程无异常,结合提供的PendingIntent代码,核心问题大概率和以下两点相关:

  • Intent.FLAG_ACTIVITY_NO_HISTORY 标记:当系统弹出PIN验证界面时,TransactionActivity进入后台,该标记会让系统直接销毁Activity,导致验证完成后没有可返回的页面,直接回到主屏幕。
  • Activity启动模式与PendingIntent的交互:推送启动的Activity没有合适的栈管理策略,验证完成后无法重新唤起原Activity。

解决方案

1. 调整Intent的Flags

移除FLAG_ACTIVITY_NO_HISTORY,改用更温和的栈管理标记,同时确保Activity在验证完成后能正确回到前台:

fun createPendingIntent(context: Context, transaction: Transaction): PendingIntent =
    Intent(context, TransactionActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS or 
                Intent.FLAG_ACTIVITY_CLEAR_TOP or 
                Intent.FLAG_ACTIVITY_SINGLE_TOP
        putExtra(INTENT_EXTRA_TRANSACTION_APPROVAL, transaction)
    }.let { intent ->
        val flags = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
        PendingIntent.getActivity(context, 1484, intent, flags)
    }
  • FLAG_ACTIVITY_CLEAR_TOP + FLAG_ACTIVITY_SINGLE_TOP:如果TransactionActivity已经存在于栈中,会复用现有实例并清除其上方的所有Activity,避免重复创建。
  • 保留FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:符合交易验证页面不留在最近任务列表的需求。

2. 给TransactionActivity设置合适的启动模式

在AndroidManifest.xml中给TransactionActivity添加launchMode="singleTask":

<activity
    android:name=".TransactionActivity"
    android:launchMode="singleTask"
    android:excludeFromRecents="true">
</activity>

这个配置会让Activity始终保持单实例,系统验证完成后能正确回到该实例,避免因栈管理问题导致的Activity销毁。

3. 处理Activity销毁后的认证回调

即使Activity被意外销毁,也要确保认证结果能被正确接收。可以通过ViewModel保存认证状态:

// 在TransactionActivity的ViewModel中保存认证状态
class TransactionViewModel : ViewModel() {
    val authenticationResult = MutableLiveData<AuthenticationResult?>()
    var hasInitiatedAuthentication = false
    
    fun setResult(result: AuthenticationResult) {
        authenticationResult.postValue(result)
    }
}

// 修改createBiometricPrompt的回调逻辑
fun FragmentActivity.createBiometricPrompt(viewModel: TransactionViewModel): BiometricPrompt {
    return BiometricPrompt(
        this,
        object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                viewModel.setResult(AuthenticationResult.Error(errorCode, errString))
            }

            override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                viewModel.setResult(AuthenticationResult.Succeeded(result))
            }

            override fun onAuthenticationFailed() {
                Timber.i("onAuthenticationFailed")
            }
        }
    )
}

然后在TransactionActivity的onCreate中观察ViewModel的结果,即使Activity被重建,也能获取到认证结果并继续处理交易逻辑:

class TransactionActivity : FragmentActivity() {
    private val viewModel: TransactionViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_transaction)
        
        viewModel.authenticationResult.observe(this) { result ->
            result?.let {
                // 处理认证结果,比如完成交易或提示错误
            }
        }
    }

    override fun onResume() {
        super.onResume()
        if (!viewModel.hasInitiatedAuthentication) {
            viewModel.hasInitiatedAuthentication = true
            lifecycleScope.launch {
                val result = authenticate(this@TransactionActivity)
                viewModel.setResult(result)
            }
        }
    }
}

4. 验证BiometricPrompt的调用时机

确保在Activity的onResume阶段调用authenticate(),避免在onCreate早期调用时,Activity还未完全初始化就触发系统验证,导致生命周期异常。

验证步骤

  1. 优先测试调整Intent Flags和Activity启动模式的方案,这是最直接解决栈管理问题的方法。
  2. 如果问题仍存在,再添加ViewModel的结果保存逻辑,确保认证结果不丢失。

内容的提问来源于stack exchange,提问作者Aaron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 12:54:58