如何实现特定屏幕展示时的FCM通知接收限制?
FCM特定页面通知拦截实现方案
核心实现思路优先选择端侧拦截,无需服务端提前检测设备状态,准确率更高、实现成本更低,可完全满足「停留屏幕C时不接收通知、其余页面正常接收」的需求。
一、当前前台展示屏幕的判断方法
不要使用getRunningTasks等已被系统限制的API判断前台页面,通过全局生命周期监听维护页面状态是各平台官方推荐的合规实现方案:
- 原生Android:在
Application类中注册ActivityLifecycleCallbacks全局监听所有页面的生命周期,维护一个全局单例存储当前前台页面标识。在每个页面的onResumed回调中更新当前页面标记,onPaused回调中清空标记即可。// 全局页面状态存储单例 object ScreenStateHolder { var currentForegroundScreen: String? = null private set fun updateScreenState(screenTag: String?) { currentForegroundScreen = screenTag } } // Application中注册生命周期监听 registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { override fun onActivityResumed(activity: Activity) { when(activity) { is ScreenAActivity -> ScreenStateHolder.updateScreenState("SCREEN_A") is ScreenBActivity -> ScreenStateHolder.updateScreenState("SCREEN_B") is ScreenCActivity -> ScreenStateHolder.updateScreenState("SCREEN_C") else -> ScreenStateHolder.updateScreenState(null) } } override fun onActivityPaused(activity: Activity) { ScreenStateHolder.updateScreenState(null) } // 其余生命周期方法空实现即可 override fun onActivityStarted(activity: Activity) {} override fun onActivityStopped(activity: Activity) {} override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} override fun onActivityDestroyed(activity: Activity) {} override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} }) - 原生iOS:通过
UIViewController的viewDidAppear/viewDidDisappear生命周期回调,或者UINavigationController的栈变化监听,和Android端逻辑一致,维护全局的当前前台页面标识即可。 - 跨端框架(Flutter/React Native):借助框架自带的路由监听能力,比如Flutter的
RouteObserver、React Native的导航库生命周期钩子,统一维护全局当前路由名称标记即可。
二、特定屏幕的FCM通知限制实现
注意:不要依赖服务端下发前检测用户当前页面的方案,该方案需要端侧持续上报页面状态,存在状态延迟、耗电、隐私合规风险,端侧拦截是最优选择。
具体实现步骤:
- 调整需要拦截的FCM消息类型为数据消息(data-only message),不要使用默认的通知消息(notification message)。通知消息会由系统服务直接弹出通知,不会进入应用代码逻辑,无法实现自定义拦截;数据消息会100%触发应用内的FCM接收回调,开发者可完全控制通知的弹出逻辑。
- 在FCM的
onMessageReceived接收回调中,第一步先读取全局维护的页面状态标记:- 如果当前标记为
SCREEN_C,直接return丢弃该消息,不触发任何通知构造、弹出逻辑 - 如果当前标记为屏幕A/屏幕B/应用处于后台,按原有逻辑构造并弹出系统通知即可
核心判断逻辑示例(Android端):
- 如果当前标记为
override fun onMessageReceived(remoteMessage: RemoteMessage) { // 优先判断当前页面状态 if (ScreenStateHolder.currentForegroundScreen == "SCREEN_C") { // 停留屏幕C时直接拦截,不弹通知 return } // 其余场景正常弹出通知 val notification = NotificationCompat.Builder(this, PUSH_CHANNEL_ID) .setContentTitle(remoteMessage.data["title"]) .setContentText(remoteMessage.data["content"]) .setSmallIcon(R.drawable.ic_notification) .build() NotificationManagerCompat.from(this).notify(notifyId, notification) }
补充说明:若应用已被系统杀死,此时FCM数据消息的接收回调可能被系统限制,但该场景下用户不可能停留在应用内的屏幕C页面,不会出现逻辑误判,完全符合业务预期。
内容的提问来源于stack exchange,提问作者Lucy
相关产品推荐
相关产品推荐

