如何检测推送通知点击并实现应用不同启动页跳转?
嘿,这个需求我在项目里折腾过好多次,给你分Android和iOS两边拆解下具体实现思路,都是实际跑通的靠谱方案~
Android 端实现步骤
- 首先给你要跳转的指定页面配置Deep Link,或者在Manifest里加特定的Intent Filter。比如你要跳转到
TargetHomeActivity,就在它的Manifest标签里这么写:
<activity android:name=".pages.TargetHomeActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- 自定义一个专属的scheme,比如和你应用包名挂钩的 --> <data android:scheme="myapp" android:host="targethome" /> </intent-filter> </activity>
- 然后发送推送的时候,在payload里带上这个Deep Link的信息。比如用FCM的话,payload里加
data字段:
{ "data": { "deep_link": "myapp://targethome", "message": "新消息提醒" }, "to": "用户的设备Token" }
- 接下来在推送接收服务里解析这个链接,启动目标页面。比如在
FirebaseMessagingService的onMessageReceived方法里:
override fun onMessageReceived(remoteMessage: RemoteMessage) { remoteMessage.data["deep_link"]?.let { deepLink -> val intent = Intent(Intent.ACTION_VIEW, Uri.parse(deepLink)) // 加这两个flag确保启动方式正确,避免重复栈 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP) applicationContext.startActivity(intent) } }
⚠️ 重点提醒:如果应用是被后台杀死的状态,点击推送会先启动默认的Launcher Activity,这时候得在Launcher Activity的onCreate里检查Intent的data,再跳转:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) intent.data?.let { uri -> if (uri.toString() == "myapp://targethome") { startActivity(Intent(this, TargetHomeActivity::class.java)) finish() // 关闭默认首页,避免返回时回到它 } } }
iOS 端实现步骤
- 先配置自定义URL Scheme(或者更安全的Universal Links,这里先讲简单的Scheme方式)。打开Xcode,进入项目设置 -> Targets -> Info -> URL Types,新增一个URL Scheme,比如
myapp。 - 发送APNs推送时,在payload里加自定义字段标记目标页面:
{ "aps": { "alert": "你有新消息", "sound": "default" }, "target_page": "TargetHome" }
- 然后处理推送点击事件,分两种情况:
- 用
AppDelegate的老版本写法:
- 用
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 检查是不是通过推送启动的应用 if let remoteNotification = launchOptions?[.remoteNotification] as? [String: Any] { handlePushJump(remoteNotification) } return true } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { handlePushJump(userInfo) completionHandler(.newData) } private func handlePushJump(_ userInfo: [AnyHashable: Any]) { guard let target = userInfo["target_page"] as? String, target == "TargetHome" else { return } // 替换根控制器为目标页面 if let window = UIApplication.shared.windows.first { let targetVC = TargetHomeViewController() window.rootViewController = UINavigationController(rootViewController: targetVC) window.makeKeyAndVisible() } }
- iOS 13+用
SceneDelegate的写法:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // 检查推送启动参数 if let remoteNotification = connectionOptions.remoteNotification { handlePushJump(remoteNotification.request.content.userInfo) } } func scene(_ scene: UIScene, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) { handlePushJump(userInfo) } private func handlePushJump(_ userInfo: [AnyHashable: Any]) { guard let target = userInfo["target_page"] as? String, target == "TargetHome" else { return } guard let windowScene = scene as? UIWindowScene, let window = windowScene.windows.first else { return } let targetVC = TargetHomeViewController() window.rootViewController = UINavigationController(rootViewController: targetVC) window.makeKeyAndVisible() }
通用注意事项
- 不管哪端,都要做好空值判断,避免解析payload时出现崩溃。
- Android端要注意
exported="true"和BROWSABLE分类的配置,不然系统会拦截Intent。 - iOS如果追求更高安全性,建议用Universal Links替代自定义Scheme,避免被其他应用劫持,不过配置需要在开发者后台和服务器上操作,稍微繁琐点。
- 测试时一定要覆盖三种状态:应用前台、后台运行、后台完全杀死,确保每种情况跳转都正常。
内容的提问来源于stack exchange,提问作者Kirill




