如何实现Android Notification像WhatsApp一样永不失效?
这个问题我太熟悉了!之前很多开发者都踩过自己写后台Service保活的坑——毕竟Android从8.0(API 26)开始对后台进程的限制越来越严格,自定义Service很容易被系统查杀,导致推送中断。而WhatsApp、Facebook这类应用根本不是靠自己的Service维持在线的,它们用的是系统级的推送通道,也就是Google官方的Firebase Cloud Messaging(FCM),这才是实现“无需运行Service也能收通知”的核心方案。
下面我给你一步步拆解实现思路:
一、核心原理:为什么FCM能替代自定义Service?
FCM是Google提供的跨平台推送服务,它依赖Android系统内置的Google Play服务维持一条系统级的长连接。这条连接由系统托管,优先级远高于普通应用的后台Service,不会被轻易杀死。当你的Apache服务器有通知要发送时,只需要调用FCM的API,由Google的服务器通过这条系统连接推送到设备,系统再唤醒你的应用处理通知——全程不需要你的应用保持后台Service运行。
二、Android端集成FCM的步骤
1. 配置Firebase项目
- 登录Firebase控制台,创建一个新项目,添加你的Android应用(填写包名、签名信息)。
- 下载生成的
google-services.json文件,放到你的Android项目的app/目录下。
2. 配置项目依赖
在你的app/build.gradle中添加FCM相关依赖:
dependencies { // FCM核心依赖 implementation 'com.google.firebase:firebase-messaging:23.4.0' }
同时确保项目根目录的build.gradle中包含Google服务插件:
buildscript { dependencies { classpath 'com.google.gms:google-services:4.3.15' } }
并在app/build.gradle末尾添加插件应用:
apply plugin: 'com.google.gms.google-services'
3. 自定义消息处理服务
创建一个继承自FirebaseMessagingService的类,用来处理收到的推送消息:
public class MyFirebaseMessagingService extends FirebaseMessagingService { // 处理收到的消息(应用在前台时触发,后台时系统可能直接显示通知) @Override public void onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); // 获取通知内容 String title = remoteMessage.getNotification().getTitle(); String body = remoteMessage.getNotification().getBody(); // 自定义通知展示逻辑(如果需要) showNotification(title, body); } // 处理设备令牌更新(令牌是FCM识别设备的唯一标识,需要同步到你的Apache服务器) @Override public void onNewToken(String token) { super.onNewToken(token); // 把这个token发送到你的Apache服务器,存储起来用于后续推送 sendTokenToServer(token); } private void showNotification(String title, String body) { // Android 8.0+需要创建通知渠道 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("default_channel", "默认通知渠道", NotificationManager.IMPORTANCE_DEFAULT); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } Intent intent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "default_channel") .setSmallIcon(R.drawable.ic_notification) .setContentTitle(title) .setContentText(body) .setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_DEFAULT); NotificationManagerCompat.from(this).notify(1, builder.build()); } private void sendTokenToServer(String token) { // 这里实现把token发送到你的Apache服务器的逻辑(比如用Retrofit或HttpURLConnection) } }
然后在AndroidManifest.xml中注册这个服务:
<service android:name=".MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
三、Apache服务器端实现推送逻辑
你的Apache服务器需要调用FCM的HTTP API来发送通知,步骤如下:
1. 获取FCM服务器密钥
在Firebase控制台的“项目设置”→“云消息传递”中,找到“服务器密钥”,复制下来。
2. 发送POST请求到FCM API
用你熟悉的后端语言(比如PHP、Java、Python)发送POST请求到https://fcm.googleapis.com/fcm/send,请求头包含认证信息和内容类型,请求体是JSON格式的通知数据。
以PHP为例,示例代码如下:
<?php $serverKey = '你的FCM服务器密钥'; $deviceToken = '从Android端获取的设备FCM令牌'; $url = 'https://fcm.googleapis.com/fcm/send'; $headers = array( 'Authorization: key=' . $serverKey, 'Content-Type: application/json' ); $notification = array( 'title' => '测试通知', 'body' => '这是来自Apache服务器的推送通知' ); $data = array( 'to' => $deviceToken, 'notification' => $notification ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); $result = curl_exec($ch); curl_close($ch); echo $result; ?>
四、关键注意事项
- 通知渠道(Android 8.0+):必须创建通知渠道,否则通知无法在Android 8.0及以上系统显示。
- 令牌管理:设备的FCM令牌可能会变化(比如应用重装、系统更新),所以一定要在
onNewToken方法中同步更新服务器的令牌存储。 - 国内环境适配:如果你的应用需要在国内使用,由于Google Play服务可能不可用,建议集成厂商推送服务(小米、华为、OPPO等各自的推送平台),或者使用第三方推送平台(如极光推送),它们会自动适配不同厂商的系统通道。
- 后台消息处理:当应用在后台时,FCM会直接由系统展示通知,不会触发
onMessageReceived;如果需要处理自定义数据,可以在请求体中添加data字段,系统会在用户点击通知时把数据传递给你的应用。
这样实现后,你的应用就可以像WhatsApp一样,无需维持自定义Service,依靠系统级的推送通道稳定接收通知了。
内容的提问来源于stack exchange,提问作者Sohana

