Android应用前台运行时禁止触发Firebase推送通知
嘿,这个问题我熟!我来给你两个靠谱的解决方案,完美解决你前台不显示通知、后台正常展示的需求,比你之前试的方法要靠谱得多~
核心思路其实很简单:准确判断应用当前是否处于前台状态,只有在后台/被杀死时才触发通知构建逻辑。下面是两种实现方式,你可以根据自己的项目情况选择:
方案一:通过Activity生命周期回调判断(无需依赖Jetpack)
这种方法适合没有引入Jetpack Lifecycle组件的项目,通过监听应用内所有Activity的生命周期变化,来判断应用是否在前台。
步骤1:创建自定义Application类
新建一个继承自Application的类,实现ActivityLifecycleCallbacks接口,用来维护应用的前后台状态:
public class MyApp extends Application implements Application.ActivityLifecycleCallbacks { private static boolean isAppInForeground = false; @Override public void onCreate() { super.onCreate(); // 注册Activity生命周期回调 registerActivityLifecycleCallbacks(this); } // 以下是接口方法实现,我们只需要关注onActivityStarted和onActivityStopped @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {} @Override public void onActivityStarted(@NonNull Activity activity) { // 只要有Activity启动,就标记应用为前台状态 isAppInForeground = true; } @Override public void onActivityResumed(@NonNull Activity activity) {} @Override public void onActivityPaused(@NonNull Activity activity) {} @Override public void onActivityStopped(@NonNull Activity activity) { // 当Activity停止时,检查当前应用是否还在前台 ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> runningProcesses = activityManager.getRunningAppProcesses(); if (runningProcesses != null) { for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) { // 匹配当前应用的包名,且进程处于前台优先级 if (processInfo.processName.equals(getPackageName()) && processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { isAppInForeground = true; return; } } } // 走到这里说明应用已经进入后台 isAppInForeground = false; } @Override public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {} @Override public void onActivityDestroyed(@NonNull Activity activity) {} // 对外提供获取前台状态的方法 public static boolean isAppInForeground() { return isAppInForeground; } }
步骤2:在Manifest中注册自定义Application
打开AndroidManifest.xml,把默认的application标签的android:name属性指向我们刚才创建的MyApp:
<application android:name=".MyApp" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" ...> <!-- 其他配置 --> </application>
步骤3:修改FCM的消息接收逻辑
在你的onMessageReceived方法中,先判断应用是否在前台,只有不在前台时才构建并显示通知:
@Override public void onMessageReceived(RemoteMessage remoteMessage) { // 先从remoteMessage中获取标题和内容(根据你的消息格式调整) String messageTitle = remoteMessage.getNotification() != null ? remoteMessage.getNotification().getTitle() : remoteMessage.getData().get("title"); String messageBody = remoteMessage.getNotification() != null ? remoteMessage.getNotification().getBody() : remoteMessage.getData().get("body"); // 关键判断:只有应用不在前台时,才显示系统通知 if (!MyApp.isAppInForeground()) { // 保持你原来的通知构建逻辑 long[] pattern = {0, 1000, 500, 1000}; Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); PendingIntent pendingIntent = /* 这里是你原来的PendingIntent逻辑 */; NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.ic_stat_name) .setContentTitle(messageTitle) .setContentText(messageBody) .setAutoCancel(true) .setVibrate(pattern) .setLights(Color.BLUE, 1, 1) .setSound(defaultSoundUri) .setContentIntent(pendingIntent); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(notificationId, notificationBuilder.build()); } // 如果应用在前台,这里可以处理你的内部通知逻辑(比如更新UI、弹出内部弹窗等) }
方案二:用Jetpack ProcessLifecycleOwner(更简洁高效)
如果你的项目已经引入了Jetpack组件,推荐用这个方法,它能直接跟踪整个应用的生命周期,代码更简洁,判断也更准确。
步骤1:添加依赖
在build.gradle(Module级)中添加Lifecycle Process的依赖:
dependencies { // 其他依赖... implementation "androidx.lifecycle:lifecycle-process:2.6.2" }
步骤2:修改自定义Application类
用ProcessLifecycleOwner替换之前的Activity生命周期监听,代码简化很多:
public class MyApp extends Application { private static boolean isAppInForeground = false; @Override public void onCreate() { super.onCreate(); // 监听应用的全局生命周期 ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() { @Override public void onResume(@NonNull LifecycleOwner owner) { // 应用进入前台 isAppInForeground = true; } @Override public void onPause(@NonNull LifecycleOwner owner) { // 应用进入后台 isAppInForeground = false; } }); } public static boolean isAppInForeground() { return isAppInForeground; } }
步骤3:同样修改onMessageReceived逻辑
和方案一的步骤3完全一样,通过MyApp.isAppInForeground()判断是否显示通知即可。
这两种方法都能准确判断应用的前后台状态,避免了手动维护布尔值的各种坑。如果用Jetpack的话优先选方案二,代码更简洁;没有用Jetpack就选方案一,兼容性更好。
内容的提问来源于stack exchange,提问作者Parth Bhoiwala

