如何让Android FCM通知服务始终在后台运行?
Hey there, let's fix your FCM background notification issue. The core problem here is how you're sending messages from your server and a couple of missing client-side configurations. Let's break this down step by step:
1. Fix the Server-Side FCM Message Structure
Right now, you're only sending a data payload. On Android, when your app is in the background or closed, FCM doesn't deliver data messages directly to your FirebaseMessagingService—instead, you need to include a notification payload to trigger the system to automatically show the notification. You can still keep the data payload if you need to pass extra information alongside the notification.
Update your server code to include both payload types:
public static void pushFCMNotification(String DeviceIdKey, String title, String content) throws Exception { String authKey = AUTH_KEY_FCM; String FMCurl = API_URL_FCM; URL url = new URL(FMCurl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setUseCaches(false); conn.setDoInput(true); conn.setDoOutput(true); conn.setRequestMethod("POST"); conn.setRequestProperty("Authorization", "key=" + authKey); conn.setRequestProperty("Content-Type", "application/json"); JSONObject data = new JSONObject(); data.put("to", DeviceIdKey.trim()); // Add notification payload for background/closed app delivery JSONObject notification = new JSONObject(); notification.put("title", title); notification.put("body", content); notification.put("click_action", ".MainActivity"); data.put("notification", notification); // Keep your existing data payload for extra context JSONObject info = new JSONObject(); info.put("title", title); info.put("content-text", content); info.put("click_action", ".MainActivity"); data.put("data", info); System.out.println(data.toString()); OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); wr.write(data.toString()); wr.flush(); wr.close(); int responseCode = conn.getResponseCode(); System.out.println("Response Code : " + responseCode); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); }
2. Add Notification Channel for Android 8.0+
Your displayNotification method doesn't create a notification channel, which is mandatory for Android O (API 26) and above. Without this, notifications won't appear even if they're successfully delivered. Update your FCM_Messaging_Service to include channel setup:
public class FCM_Messaging_Service extends FirebaseMessagingService { private static final String CHANNEL_ID = "FCM_NOTIFICATION_CHANNEL"; private static final String CHANNEL_NAME = "FCM Notifications"; private static final String CHANNEL_DESCRIPTION = "Updates and alerts from your app"; @Override public void onCreate() { super.onCreate(); createNotificationChannel(); } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT ); channel.setDescription(CHANNEL_DESCRIPTION); channel.enableLights(true); channel.setLightColor(Color.RED); channel.setVibrationPattern(new long[]{0, 500}); channel.enableVibration(true); NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); } } @Override public void onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); String title = ""; String contentText = ""; // Handle both notification and data payloads if (remoteMessage.getNotification() != null) { title = remoteMessage.getNotification().getTitle(); contentText = remoteMessage.getNotification().getBody(); } // Fallback to data payload if notification is null if (remoteMessage.getData() != null) { if (title.isEmpty()) title = remoteMessage.getData().get("title"); if (contentText.isEmpty()) contentText = remoteMessage.getData().get("content-text"); } displayNotification(title, contentText, this); // Vibrate logic remains unchanged Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE)); } else { v.vibrate(500); } } @SuppressLint("NewApi") public void displayNotification(String title, String contentText, Context context) { NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle(title) .setContentText(contentText) .setSmallIcon(R.mipmap.ic_launcher_round) .setLights(Color.RED, 3000, 3000) .setSound(Uri.parse("android.resource://" + context.getPackageName() + "/" + R.raw.alarm)) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) .setPriority(NotificationCompat.PRIORITY_DEFAULT); NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.notify(14, builder.build()); } }
3. Complete Your AndroidManifest.xml Configuration
Your provided manifest is truncated—ensure you've registered your FCM services correctly and added all required permissions, including the Android 13+ notification permission:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.ddns.kodiakscloud.fastalker"> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Required for Android 13+ --> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <!-- Register Firebase Instance ID Service --> <service android:name=".FCM_Instance_ID_Service" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.INSTANCE_ID_EVENT" /> </intent-filter> </service> <!-- Register Firebase Messaging Service --> <service android:name=".FCM_Messaging_Service" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service> <!-- Default notification channel meta-data --> <meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="FCM_NOTIFICATION_CHANNEL" /> <!-- Your MainActivity and other components --> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <!-- Handle click_action from notifications --> <intent-filter> <action android:name=".MainActivity" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> </application> </manifest>
4. Verify Device-Specific Settings
- Double-check that your app has notification permissions enabled in the device settings (Settings > Apps > Your App > Notifications)
- For manufacturers like Xiaomi, Huawei, or Samsung, disable battery optimization for your app (Settings > Battery > Battery Optimization > Select your app > "Don't optimize")—these custom ROMs often restrict background services to save battery.
These changes should ensure notifications are delivered reliably whether your app is in the foreground, background, or fully closed.
内容的提问来源于stack exchange,提问作者Kodiak

