如何在Android 10下实现FCM触发的类似WhatsApp的后台来电通知
Android 10 (API 29) 开始系统禁止后台应用直接启动Activity,只有满足特定豁免条件才能绕过限制,按照以下步骤即可实现类似WhatsApp、Telegram的后台来电唤起效果:
1. 申请必需的特殊权限
首先在AndroidManifest.xml中声明核心权限:
<!-- 悬浮窗权限,后台弹窗核心权限 --> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!-- 全屏通知权限(Android 10+) --> <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" /> <!-- 唤醒屏幕、锁屏显示权限 --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <!-- Android 12+ 精确闹钟权限,用于来电准时触发 --> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" android:maxSdkVersion="33" /> <uses-permission android:name="android.permission.USE_EXACT_ALARM" android:minSdkVersion="34" />
悬浮窗权限需要引导用户手动授权:
// 检查悬浮窗权限 if (!Settings.canDrawOverlays(context)) { // 跳转系统授权页 val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:${context.packageName}")) startActivityForResult(intent, REQUEST_OVERLAY_PERMISSION) }
2. 调整FCM消息接收逻辑
FCM消息分为通知消息和数据消息,后台时通知消息会由系统直接托管,不会触发应用的onMessageReceived回调,所以必须将来电推送设置为数据消息,确保不管应用处于前台还是后台、进程是否被杀死,都能触发应用自定义的来电处理逻辑。
注意:不要依赖FCM通知类型消息做来电触发,后台进程被杀死的场景下,通知消息不会唤醒应用的自定义处理逻辑。
3. 高优先级通知+全屏Intent实现唤起
收到FCM来电数据消息后,创建高优先级通知搭配全屏Intent,系统识别到带全屏Intent的通话类高优先级通知时,会优先唤起指定的来电Activity,即使设备处于锁屏状态也能正常弹出:
// 创建来电通知通道(Android 8.0+必需) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( CHANNEL_ID_INCOMING_CALL, "来电通知", NotificationManager.IMPORTANCE_HIGH ).apply { description = "来电通知,会弹出全屏来电界面" enableVibration(true) vibrationPattern = longArrayOf(1000, 1000) } notificationManager.createNotificationChannel(channel) } // 构造指向来电Activity的全屏Intent val fullScreenIntent = Intent(context, IncomingCallActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP // 携带来电相关参数,比如来电人ID、手机号等 putExtra("caller_id", callerId) } val fullScreenPendingIntent = PendingIntent.getActivity( context, REQUEST_CODE_INCOMING_CALL, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) // 构建来电通知 val notificationBuilder = NotificationCompat.Builder(context, CHANNEL_ID_INCOMING_CALL) .setSmallIcon(R.drawable.ic_call) .setContentTitle("来电") .setContentText("$callerName 正在呼叫你") .setPriority(NotificationCompat.PRIORITY_MAX) .setCategory(NotificationCompat.CATEGORY_CALL) .setFullScreenIntent(fullScreenPendingIntent, true) .setOngoing(true) .setAutoCancel(false) // 发送通知触发唤起 notificationManager.notify(NOTIFICATION_ID_INCOMING_CALL, notificationBuilder.build())
如果应用已经获得SYSTEM_ALERT_WINDOW权限,也可以跳过通知步骤,直接启动IncomingCallActivity,两种方案可以搭配使用覆盖更多系统和厂商场景。
4. 来电Activity特殊配置
在AndroidManifest.xml中给来电Activity添加配置,确保它能在锁屏、后台状态下正常弹出:
<activity android:name=".ui.call.IncomingCallActivity" android:exported="false" android:showOnLockScreen="true" android:turnScreenOn="true" android:excludeFromRecents="true" android:launchMode="singleTop" />
同时在Activity的onCreate中添加窗口属性进一步适配:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { setShowWhenLocked(true) setTurnScreenOn(true) } else { window.addFlags( WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON ) } // 后续处理布局加载、接听/挂断逻辑 }
5. 厂商定制ROM兼容
小米、华为、OPPO、vivo等国内厂商做了更严格的后台权限限制(比如后台弹出界面权限、自启动权限),可以在首次启动应用时引导用户开启对应的厂商权限,避免后台无法唤起的问题。
内容的提问来源于stack exchange,提问作者Akash das

