You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

开发VoIP应用:如何实现后台来电通知?WhatsApp/Telegram方案咨询

Hey there! Let's break down your VoIP app's background notification issue and how the big players like WhatsApp and Telegram nail near-perfect delivery rates.

1. Why Your FCM Isn't Triggering onMessageReceived in Background

Your hunch about battery optimization is spot-on—Android's Doze mode and App Standby aggressively restrict background services to save power. But there's another key detail: FCM has two message types, and only one triggers onMessageReceived in the background:

  • Notification messages: Handled directly by the Android system when your app is backgrounded—your service won't get called.
  • Data messages: Delivered directly to your MyFirebaseMessagingService regardless of app state, as long as you set the priority to high.
2. Fixing FCM Background Delivery for VoIP Calls

Here's how to adjust your setup to ensure incoming call notifications get through:

a. Send High-Priority Data Messages

Make sure your server sends data-only messages with priority: high (critical for bypassing Doze restrictions). Example payload:

{
  "to": "DEVICE_FCM_TOKEN",
  "data": {
    "type": "voip_incoming_call",
    "caller_id": "user_123",
    "caller_name": "Alice Smith",
    "call_session_id": "session_456"
  },
  "priority": "high"
}

b. Update Your Manifest for VoIP Priority

Add permissions and declare your service with proper intent filters, plus VoIP-specific foreground service permissions for Android 12+:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CALL" />

<service
    android:name=".MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

c. Refine Your MyFirebaseMessagingService

Handle data messages, launch a full-screen incoming call notification, and start a foreground service to avoid being killed:

public class MyFirebaseMessagingService extends FirebaseMessagingService {
    private static final String TAG = "FCM Service";
    private static final int FOREGROUND_SERVICE_ID = 1001;
    private static final String VOIP_CHANNEL_ID = "voip_call_channel";

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);

        if (remoteMessage.getData().isEmpty()) return;

        Map<String, String> callData = remoteMessage.getData();
        if ("voip_incoming_call".equals(callData.get("type"))) {
            // Show full-screen incoming call notification (critical for Android 10+)
            showIncomingCallNotification(callData);
            // Start foreground service to keep app alive
            startVoipForegroundService();
        }
    }

    private void showIncomingCallNotification(Map<String, String> callData) {
        // Create full-screen intent for incoming call activity
        Intent callIntent = new Intent(this, IncomingCallActivity.class);
        callIntent.putExtra("caller_name", callData.get("caller_name"));
        callIntent.putExtra("session_id", callData.get("call_session_id"));
        PendingIntent fullScreenIntent = PendingIntent.getActivity(
                this,
                0,
                callIntent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, VOIP_CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_call_icon)
                .setContentTitle("Incoming Call")
                .setContentText(callData.get("caller_name"))
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setCategory(NotificationCompat.CATEGORY_CALL)
                .setFullScreenIntent(fullScreenIntent, true);

        // Create channel for Android O+
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    VOIP_CHANNEL_ID,
                    "VoIP Calls",
                    NotificationManager.IMPORTANCE_HIGH
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }

        NotificationManagerCompat.from(this).notify(1, builder.build());
    }

    private void startVoipForegroundService() {
        NotificationCompat.Builder serviceBuilder = new NotificationCompat.Builder(this, "voip_service_channel")
                .setSmallIcon(R.drawable.ic_service_icon)
                .setContentTitle("Voip Service Active")
                .setContentText("Ready to receive calls")
                .setPriority(NotificationCompat.PRIORITY_LOW);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel serviceChannel = new NotificationChannel(
                    "voip_service_channel",
                    "VoIP Background Service",
                    NotificationManager.IMPORTANCE_LOW
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(serviceChannel);
            startForeground(FOREGROUND_SERVICE_ID, serviceBuilder.build());
        }
    }

    @Override
    public void onNewToken(String token) {
        Log.d(TAG, "Refreshed FCM token: " + token);
        // Send this token to your server for future call notifications
    }
}

d. Request Battery Optimization Whitelisting

Prompt users to exclude your app from battery restrictions (critical for devices from Xiaomi, Huawei, OPPO, etc.):

private void requestBatteryWhitelist() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
        String packageName = getPackageName();
        if (!pm.isIgnoringBatteryOptimizations(packageName)) {
            Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
            intent.setData(Uri.parse("package:" + packageName));
            startActivity(intent);
        }
    }
}
3. How WhatsApp & Telegram Achieve Near-Perfect Delivery

They don't rely solely on FCM—here's their secret sauce:

  • Persistent TCP Long Connections: They maintain their own direct connection to their servers, so even if FCM is blocked, calls come through via this channel.
  • Persistent Foreground Service: They run a silent foreground service 24/7, which prevents the system from killing their app process.
  • Vendor-Specific Optimizations: They work directly with Android OEMs (like Xiaomi, Samsung) to get their apps whitelisted from aggressive background killing by default.
  • Telecom API Integration: They use Android's ConnectionService (part of the Telecom Manager), which gives VoIP apps system-level priority for incoming calls—ensuring notifications wake the device regardless of state.
  • Multi-Channel Retries: If FCM fails, their servers retry via their own long connection, and vice versa, until the notification is delivered.
  • Smart Wake-Up Triggers: They use subtle system events (like network changes) to re-establish connections if they get dropped.

内容的提问来源于stack exchange,提问作者jerrymouse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:46:32