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

Android:Firebase推送通知深度链接冷启动失效(仅应用运行时正常)

Android:Firebase推送通知深度链接冷启动失效(仅应用运行时正常)

我之前也碰到过几乎一模一样的问题!结合你用的FCM+Jetpack Compose Navigation技术栈,给你梳理几个关键的排查点和解决方法,亲测有效:

1. 先把AndroidManifest的配置拉满,别漏关键项

冷启动时系统是直接通过启动Activity处理深度链接的,这里最容易踩坑。首先确保你的启动Activity(比如MainActivity)配置正确:

  • 必须设置android:exported="true"(API31+强制要求,否则系统无法启动它处理外部Intent)
  • 深度链接的<intent-filter>里要包含DEFAULT和BROWSABLE类别,data标签要对应你的scheme和host

示例配置:

<activity
    android:name=".MainActivity"
    android:exported="true"
    android:launchMode="singleTop"> <!-- 推荐用singleTop避免多实例 -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <!-- 深度链接专属Intent Filter -->
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="appName"
            android:host="chat" />
    </intent-filter>
</activity>

2. 冷启动时要手动把Intent传递给Compose NavHost

Compose Navigation初始化需要时间,冷启动时如果直接在NavHost初始化时获取Intent,可能拿不到最新的链接。可以在Activity里用State保存深度链接,等NavHost ready后再触发导航:

class MainActivity : ComponentActivity() {
    // 用State保存深度链接,确保Compose能感知变化
    private val pendingDeepLink = mutableStateOf<Uri?>(null)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 冷启动时从Intent里捞深度链接
        intent?.data?.let {
            pendingDeepLink.value = it
        }

        setContent {
            YourAppTheme {
                val navController = rememberNavController()

                // 当pendingDeepLink有值时,触发导航
                LaunchedEffect(pendingDeepLink.value) {
                    pendingDeepLink.value?.let { uri ->
                        navController.navigate(uri) {
                            launchSingleTop = true // 避免重复打开页面
                            restoreState = true // 恢复之前的页面状态(可选)
                        }
                        // 导航后清空,防止Activity重建时重复触发
                        pendingDeepLink.value = null
                    }
                }

                NavHost(navController = navController, startDestination = "home") {
                    // 一定要给Chat页面配置对应的深度链接规则
                    composable(
                        route = "chat/{chatId}/{userId}",
                        deepLinks = listOf(
                            navDeepLink {
                                uriPattern = "appName://chat/{chatId}/{userId}"
                            }
                        )
                    ) { backStackEntry ->
                        val chatId = backStackEntry.arguments?.getString("chatId")
                        val userId = backStackEntry.arguments?.getString("userId")
                        ChatScreen(chatId, userId)
                    }
                    // 其他页面的导航配置...
                }
            }
        }
    }

    // 热启动时(App在后台),新的Intent会走onNewIntent,这里也要处理
    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        intent?.data?.let {
            pendingDeepLink.value = it
        }
    }
}

3. 检查FCM消息的Payload格式,别用错字段

如果你的推送是用Notification Payload,系统可能会在App后台时直接处理通知,导致深度链接没正确传递。更推荐用Data Payload,这样所有字段都会直接传给你的App:

示例FCM Data Payload:

{
  "to": "你的设备Token",
  "data": {
    "link": "appName://chat/123/456",
    "title": "新消息提醒",
    "body": "有人给你发了一条消息"
  }
}

然后在你的FirebaseMessagingService里,要正确构建PendingIntent,把深度链接传进去:

class MyFirebaseMessagingService : FirebaseMessagingService() {
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        super.onMessageReceived(remoteMessage)
        val deepLink = remoteMessage.data["link"] ?: return

        // 构建打开深度链接的Intent
        val deepLinkIntent = Intent(Intent.ACTION_VIEW, Uri.parse(deepLink))
        deepLinkIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)

        // 注意:API30+必须加FLAG_IMMUTABLE或FLAG_MUTABLE
        val pendingIntent = PendingIntent.getActivity(
            this,
            0,
            deepLinkIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        // 构建通知并显示
        val notification = NotificationCompat.Builder(this, "你的通知渠道ID")
            .setContentTitle(remoteMessage.data["title"])
            .setContentText(remoteMessage.data["body"])
            .setSmallIcon(R.drawable.ic_notification)
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)
            .build()

        NotificationManagerCompat.from(this).notify(1, notification)
    }
}

4. 测试冷启动的正确姿势

别以为划掉最近应用就是冷启动了!有些设备会保留App进程,正确的测试方法是:

  • 划掉最近应用里的App
  • 等待3-5秒(确保系统杀死进程)
  • 再点击推送通知

额外坑:如果有启动页(SplashScreen)

如果你用了SplashActivity,一定要把它的Intent完整传递给MainActivity,不然深度链接会丢:

class SplashActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 把Splash的Intent所有数据传递给MainActivity
        val mainIntent = Intent(this, MainActivity::class.java)
        mainIntent.putExtras(intent)
        mainIntent.data = intent.data
        startActivity(mainIntent)
        finish()
    }
}

备注:内容来源于stack exchange,提问作者RJnr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 16:30:34