在 Android 平台,当您的应用进程被杀死或处于后台时,若收到音视频通话邀请,对方将无法收到任何通知,导致通话无法接通。因此,集成离线推送是确保您的音视频通话功能可用的必要条件(用户呼叫始终在线的 Web 端坐席等特殊情况除外)。本文档将指导您完成 FCM Data Message 的推送集成步骤。即使应用未运行,也能将通话邀请等关键信令送达设备,从而唤醒应用并启动通话界面。

开通服务

集成建议 [必看]
如何选择推送渠道?
由于 FCM 依赖于 Google 服务,请根据您的业务运营区域选择合适的集成策略:
跨国全球业务(涉及国内外):为确保全球范围内消息的触达率,建议同时集成国内各厂商推送服务以及 FCM 推送服务。
仅限海外市场(不涉及中国大陆):无需集成国内厂商服务。请直接参考本文档集成 FCM Data Message 即可。
仅限中国大陆境内:无需集成 FCM。请参考集成 Notification,按需集成 Notification (小米、华为、OPPO、vivo 等国内厂商) 推送即可。
如何提高通话触达率?
根据我们的经验,超过 90% 的用户接不到通话通知的原因是手动关闭了应用的通知权限。为确保通话可达率,我们强烈建议:
1. 审慎发送通知:避免向用户推送低质量或无关联的通知,减少用户因骚扰而关闭通知权限的可能性。
2. 提供精细控制:借鉴微信等优秀应用的做法,在应用设置内为用户提供独立的开关,分别控制"Notification"和"FCM Data Message",而非引导用户去系统设置中一刀切地关闭所有通知。

快速接入
步骤1:厂商配置
1. 将您自己的应用注册到 FCM 推送平台,得到 AppID 和 AppKey 等参数以及
google-services.json 文件 ,来实现离线推送功能。2. 登录 即时通信 IM 控制台,在推送管理 > 接入设置功能栏,选择 FCM,添加 FCM 的证书,其中,消息类型选择透传(数据)消息。
厂商推送平台 | IM 控制台配置 |
![]() | ![]() |
步骤2:接入推送插件
1. 添加配置文件:完成控制台厂商推送信息填写后,下载并添加配置文件到工程。将下载的
timpush-configs.json 文件添加到应用模块的 assets 目录下,将 google-services.json 添加到工程 app 目录下。选择下载配置文件 timpush-configs.json | 下载文件 google-services.json | 添加到您的工程 |
![]() | ![]() | ![]() |
2. 集成推送插件:在项目的
app 目录下的build.gradle文件中添加如下依赖:implementation "com.tencent.timpush:timpush:latest.release"implementation "com.tencent.timpush:fcm:latest.release"
说明:
步骤3:工程配置
1. 在项目级
build.gradle 文件的 buildscript > dependencies 下添加以下配置:buildscript {dependencies {classpath 'com.google.gms:google-services:4.3.15'}}
2. 在项目的
app 目录下的build.gradle文件中添加下方配置apply plugin: 'com.google.gms.google-services'
3. 在项目的
app 目录下,找到并打开build.gradle文件,将应用包名修改为您的实际应用包名。applicationId 'com.****.callkit'
自定义来电推送
步骤1:监听 FCM 推送
当 FCM 推送唤醒应用时,TIMPush 插件会发送“应用唤醒”广播。收到该广播后,请按以下步骤处理以确保来电通知正常显示:
1. 监听应用唤醒广播:应用被唤醒,收到 TIMPush 发送的广播。
2. 执行自动登录:登录后通话服务状态可用,如何登录可参考 login 。
import android.content.BroadcastReceiverimport android.content.Contextimport android.content.Intentimport android.content.IntentFilterimport android.os.Bundleimport androidx.localbroadcastmanager.content.LocalBroadcastManagerclass PushWakeupReceiver : BroadcastReceiver() {override fun onReceive(context: Context?, intent: Intent?) {if (intent?.action == "TIMPush.BROADCAST_IM_LOGIN_AFTER_APP_WAKEUP") {val bundle = intent.getExtras()// 2.执行自动登录// autoLogin()}}}class MainActivity : AppCompatActivity() {private val wakeupReceiver = PushWakeupReceiver()private var callListener: CallListener? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 1.监听应用唤醒广播observeAppWakeup()}private fun observeAppWakeup() {val filter = IntentFilter("TIMPush.BROADCAST_IM_LOGIN_AFTER_APP_WAKEUP")LocalBroadcastManager.getInstance(this).registerReceiver(wakeupReceiver, filter)}}
步骤2:自定义来电通知样式
您可以使用
NotificationManager 通知管理类显示您定制的来电通知界面,实现方式如下:1. 创建一个来电通知管理类(
IncomingCallNotificationManager):封装推送的创建和显示等逻辑。2. 自定义来电通知的 UI 样式:您可以使用
RemoteViews 定制通知布局。关于如何使用 NotificationCompat.Builder 配置通知的详细参数(如 Channel、PendingIntent、优先级等),请参阅 Android 官方文档:NotificationCompat.Builder。import android.app.Notificationimport android.app.NotificationChannelimport android.app.NotificationManagerimport android.content.Contextimport android.content.Intentimport android.os.Buildimport android.widget.RemoteViewsimport androidx.core.app.NotificationCompatclass IncomingCallNotificationManager(private val context: Context) {private val channelId = "incoming_call_channel"private val channelName = "来电通知"private val notificationId = 1001private var remoteViews: RemoteViews? = nullprivate val notificationManager: NotificationManager =context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagerinit {// 1.创建通知渠道createNotificationChannel()}private fun createNotificationChannel() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {val channel = NotificationChannel(channelId, // 渠道 ID(唯一标识)"来电通知", // 渠道名称(用户可见)NotificationManager.IMPORTANCE_HIGH // 重要性:高优先级).apply {description = "显示来电通知" // 渠道描述(用户可见)enableLights(true) // 启用通知灯enableVibration(true) // 启用震动setShowBadge(false) // 不显示角标}notificationManager.createNotificationChannel(channel)}}// 2. 显示自定义来电通知的 UI 样式fun showIncomingCallNotification(callerName: String,callerAvatar: Int?, // Bitmap 或 Int (资源 ID)isVideoCall: Boolean) {// RemoteViews 用于在通知中显示自定义的布局remoteViews = RemoteViews(context.packageName, R.layout.incoming_call_notification)// 设置来电者姓名remoteViews?.setTextViewText(R.id.tv_caller_name, callerName)// 设置通话类型描述(视频通话 或 语音通话)remoteViews?.setTextViewText(R.id.tv_call_desc, if (isVideoCall) "视频通话" else "语音通话")// 设置通话类型图标val callTypeIcon = if (isVideoCall) {R.drawable.ic_video_call} else {R.drawable.ic_audio_call}remoteViews?.setImageViewResource(R.id.img_call_type, callTypeIcon)// 设置呼叫用户头像remoteViews?.setImageViewResource(R.id.img_avatar, callerAvatar)// 创建通知对象val notification = NotificationCompat.Builder(context, channelId).setSmallIcon(R.drawable.ic_notification_small) // 状态栏显示的小图标.setContent(remoteViews) // 设置自定义布局.setCustomContentView(remoteViews) // 标准视图(通知栏收起时).setCustomBigContentView(remoteViews) // 展开视图(通知栏展开时).setOngoing(true) // 持续通知(用户无法滑动删除).setAutoCancel(true) // 点击后自动取消.setCategory(NotificationCompat.CATEGORY_CALL) // 分类为通话通知.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) // 锁屏时可见.setTimeoutAfter(60000) // 60秒后自动取消.build()notificationManager.notify(notificationId, notification)}}
上述来电通知样式的布局文件如下(
R.layout.incoming_call_notification ):<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="16dp"android:background="#FFFFFF"android:gravity="center_vertical"><!-- 用户头像 --><ImageViewandroid:id="@+id/img_avatar"android:layout_width="56dp"android:layout_height="56dp"android:layout_gravity="center_vertical"android:scaleType="centerCrop"android:src="@drawable/callview_ic_avatar"android:background="@drawable/avatar_background" /><!-- 用户信息和描述 --><LinearLayoutandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:layout_gravity="center_vertical"android:layout_marginStart="16dp"android:orientation="vertical"><TextViewandroid:id="@+id/tv_caller_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="18sp"android:textStyle="bold"android:textColor="#212121"android:maxLines="1"android:ellipsize="end"android:text="来电" /><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="6dp"android:orientation="horizontal"android:gravity="center_vertical"><ImageViewandroid:id="@+id/img_call_type"android:layout_width="16dp"android:layout_height="16dp"android:src="@drawable/ic_audio_call" /><TextViewandroid:id="@+id/tv_call_desc"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="6dp"android:textSize="14sp"android:textColor="#757575"android:text="语音通话" /></LinearLayout></LinearLayout></LinearLayout>
步骤3:展示来电通知
您需要监听来电事件,登录成功后(步骤1),如果有离线来电事件,CallStore 会自动补发,您可以在该事件中显示定制的推送通知(步骤2),实现方式如下:
1. 监听来电事件:订阅 onCallReceived 事件。该事件在收到来电时触发,并包含通话所需的关键信息。
2. 展示来电通知:当收到 onCallReceived 事件后,根据该事件携带的通话信息展示通知界面。在 onCallReceived 回调中,根据事件携带的通话信息,并调用自定义的推送通知(参考步骤 2)进行展示。
class MainActivity : AppCompatActivity() {private val wakeupReceiver = PushWakeupReceiver()private var callListener: CallListener? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 1.监听来电事件observeCallReceived()}private fun observeCallReceived() {callListener = object : CallListener() {override fun onCallReceived(callId: String, mediaType: CallMediaType, userData: String) {super.onCallReceived(callId, mediaType, userData)// 2.显示来电通知showIncomingCallNotification(mediaType)}}callListener?.let { CallStore.shared.addListener(it) }}private fun showIncomingCallNotification(mediaType: CallMediaType) {try {val callerName = CallStore.shared.observerState.activeCall.value.inviterIdval notificationManager = IncomingCallNotificationManager(this)notificationManager.showIncomingCallNotification(callerName = callerName,callerAvatar = R.drawable.callview_ic_avatar,isVideoCall = mediaType == CallMediaType.Video)} catch (e: Exception) {}}}
onCallReceived 事件详细说明:当收到来电事件时触发。
参数 | 类型 | 说明 |
callId | String | 此次通话的唯一标识。 |
mediaType | 通话媒体类型,用于指定发起音频通话还是视频通话。 CallMediaType.Video :视频通话。CallMediaType.Audio :语音通话。 |
activeCall 结构体详细说明:活跃通话响应式数据,包含通话 ID、主叫 ID 、被叫 ID 列表、通话媒体类型、通话计时等关键信息。
字段 | 类型 | 说明 |
callId | String | 此次通话的唯一标识。 |
roomId | String | 此次通话的房间 ID 。 |
inviterId | String | 发起通话用户的 ID 。 |
inviteeIds | LinkedHashSet<String> | 被叫用户的 ID 列表。 |
chatGroupId | String | 此次通话的群组 ID ,与 Chat 配合使用。 |
mediaType | 此次通话的媒体类型。 CallMediaType.Video:视频通话。CallMediaType.Audio:语音通话。 | |
result | 通话结果/方向。 CallDirection.Unknown:未知通话。CallDirection.Missed:未接通话。CallDirection.Incoming:来电通话。CallDirection.Outgoing:拨出通话。 | |
duration | Long | 通话时长。 |
startTime | Long | 通话接通的开始时间。 |
运行效果
完成以上 3 步以后,来电通知效果如下:

常见问题
应用被杀死后无法弹出来电界面
确认收到了推送,收不到推送需要确认下 IM 控制台是否正确上传证书。参见文档上述快速接入的第一步,看下是否添加正确。
确认控制台选择了 FCM 数据消息,对照上述准备条件的第二步。
确认收到数据消息,过滤日志(关键字:TIMPush),检查下述日志是否有打印记录(如果收不到消息,可以使用 IM 控制台的 排查工具 查看原因)。


确认实现了自动登录。自动登录后才会去拉取通话请求,才能显示来电界面。
怎样强制使用 FCM 通道?
若您的业务想要强制使用 FCM 通道,可调用以下接口:
TIMPushManager.getInstance().forceUseFCMPushChannel(true);




