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

Android不同应用状态下通知点击跳转逻辑实现求助(API 32)

解决方案思路

核心是用一个通知路由Activity(比如NotificationRouterActivity)作为通知点击的唯一入口,所有通知的PendingIntent都指向它,然后在这个Activity的onCreate里实时判断当前应用状态和生物验证状态,再决定跳转逻辑。这样就能彻底解决广播里判断状态过时的问题。

步骤1:创建通知路由Activity

这个Activity做成透明无界面的(避免闪屏),只负责逻辑判断和跳转。

1.1 配置Manifest

在AndroidManifest.xml里注册该Activity,设置透明主题:

<activity
    android:name=".NotificationRouterActivity"
    android:theme="@style/Theme.Transparent"
    android:excludeFromRecents="true"
    android:launchMode="singleTask">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

透明主题在styles.xml中定义:

<style name="Theme.Transparent" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowAnimationStyle">@null</item>
</style>

1.2 路由Activity核心逻辑

在NotificationRouterActivity的onCreate中完成实时判断:

class NotificationRouterActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 1. 获取生物验证状态(BiometricActivity验证成功后存入SharedPreferences)
        val isBiometricAuthenticated = getSharedPreferences("AppPrefs", MODE_PRIVATE)
            .getBoolean("is_biometric_authed", false)

        // 2. 判断应用当前状态
        val isAppInForeground = isAppRunningInForeground()
        val isAppClosed = !isAppProcessRunning()

        // 3. 按场景处理跳转
        when {
            // 场景3:应用已关闭
            isAppClosed -> {
                val intent = Intent(this, SplashActivity::class.java).apply {
                    putExtra("open_notification", true)
                    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
                }
                startActivity(intent)
                finish()
            }
            // 场景1:应用前台且已完成生物验证
            isAppInForeground && isBiometricAuthenticated -> {
                val intent = Intent(this, NotificationActivity::class.java).apply {
                    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
                }
                startActivity(intent)
                finish()
            }
            // 场景2:应用后台
            else -> {
                if (isBiometricAuthenticated) {
                    // 调至前台并打开NotificationActivity
                    val intent = packageManager.getLaunchIntentForPackage(packageName)?.apply {
                        putExtra("open_notification", true)
                        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
                    }
                    startActivity(intent)
                    finish()
                } else {
                    // 先跳转生物验证页面,验证成功后打开目标页
                    val intent = Intent(this, BiometricActivity::class.java).apply {
                        putExtra("target_activity", "NotificationActivity")
                        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
                    }
                    startActivity(intent)
                    finish()
                }
            }
        }
    }

    // 判断应用是否在前台运行
    private fun isAppRunningInForeground(): Boolean {
        val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val appProcesses = activityManager.runningAppProcesses ?: return false
        for (processInfo in appProcesses) {
            if (processInfo.processName == packageName && processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                return true
            }
        }
        return false
    }

    // 判断应用进程是否存活(检测应用是否已关闭)
    private fun isAppProcessRunning(): Boolean {
        val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val runningProcesses = activityManager.runningAppProcesses ?: return false
        return runningProcesses.any { it.processName == packageName }
    }
}

步骤2:修改AlarmBroadcast的通知逻辑

把原来指向NotificationActivity的PendingIntent改成指向路由Activity:

class AlarmBroadcast : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        context ?: return

        // 创建指向路由Activity的PendingIntent
        val routerIntent = Intent(context, NotificationRouterActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
        }
        val pendingIntent = PendingIntent.getActivity(
            context,
            0,
            routerIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE // API32必须加FLAG_IMMUTABLE
        )

        // 构建并发送通知
        val notification = NotificationCompat.Builder(context, "your_channel_id")
            .setContentTitle("通知标题")
            .setContentText("通知内容")
            .setSmallIcon(R.drawable.ic_notification)
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)
            .build()

        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(1, notification)
    }
}

步骤3:处理其他页面的跳转衔接

3.1 SplashActivity处理启动标记

当应用从关闭状态启动时,根据标记跳转到目标页:

class SplashActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)

        // 模拟初始化流程
        Handler(Looper.getMainLooper()).postDelayed({
            val openNotification = intent.getBooleanExtra("open_notification", false)
            if (openNotification) {
                startActivity(Intent(this, NotificationActivity::class.java))
            } else {
                startActivity(Intent(this, BiometricActivity::class.java))
            }
            finish()
        }, 1000)
    }
}

3.2 BiometricActivity处理目标页跳转

验证成功后,根据路由传递的参数打开指定页面:

class BiometricActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_biometric)

        biometricButton.setOnClickListener {
            performBiometricAuthentication()
        }
    }

    private fun performBiometricAuthentication() {
        // 省略生物验证具体实现
        // 验证成功后更新状态
        getSharedPreferences("AppPrefs", MODE_PRIVATE)
            .edit()
            .putBoolean("is_biometric_authed", true)
            .apply()

        // 跳转到目标页
        val targetActivity = intent.getStringExtra("target_activity")
        if (targetActivity == "NotificationActivity") {
            startActivity(Intent(this, NotificationActivity::class.java))
        } else {
            startActivity(Intent(this, ProfileActivity::class.java))
        }
        finish()
    }
}

步骤4:维护生物验证状态

根据业务需求重置验证状态,比如应用后台时:

class MainActivity : AppCompatActivity() {
    override fun onStop() {
        super.onStop()
        // 示例:应用后台时重置验证状态,需根据实际业务调整
        getSharedPreferences("AppPrefs", MODE_PRIVATE)
            .edit()
            .putBoolean("is_biometric_authed", false)
            .apply()
    }
}

关键说明

  • 路由Activity的实时判断彻底解决了广播中状态过时的问题,所有逻辑在用户点击通知时才执行。
  • 用SharedPreferences存储生物验证状态,确保跨页面/进程的状态一致性。
  • 注意API32及以上PendingIntent必须添加FLAG_IMMUTABLE,避免兼容性报错。
  • 透明路由Activity不会产生视觉干扰,用户感知不到中间跳转过程。

内容的提问来源于stack exchange,提问作者Muhammad Umer Naeem Shami

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 12:35:24