接入离线推送(FCM)

最近更新时间:2026-02-25 16:24:32

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


开通服务

进入 IM 控制台 > 插件市场,单击立即购买免费试用(每个应用可免费试用一次,有效期7天)。

注意:
推送插件试用或购买到期后,将自动停止提供推送服务(包括普通消息离线推送、全员/标签推送等服务)。为避免影响您的业务正常使用,请提前 购买/续费

集成建议 [必看]

如何选择推送渠道?

由于 FCM 依赖于 Google 服务,请根据您的业务运营区域选择合适的集成策略
跨国全球业务(涉及国内外):为确保全球范围内消息的触达率,建议同时集成国内各厂商推送服务以及 FCM 推送服务。
仅限海外市场(不涉及中国大陆):无需集成国内厂商服务。请直接参考本文档集成 FCM Data Message 即可。
仅限中国大陆境内:无需集成 FCM。请参考集成 Notification,按需集成 Notification (小米、华为、OPPO、vivo 等国内厂商) 推送即可。

如何提高通话触达率?

根据我们的经验,超过 90% 的用户接不到通话通知的原因是手动关闭了应用的通知权限。为确保通话可达率,我们强烈建议:
1. 审慎发送通知:避免向用户推送低质量或无关联的通知,减少用户因骚扰而关闭通知权限的可能性。
2. 提供精细控制:借鉴微信等优秀应用的做法,在应用设置内为用户提供独立的开关,分别控制"Notification"和"FCM Data Message",而非引导用户去系统设置中一刀切地关闭所有通知。


快速接入

步骤1:厂商配置

1. 将您自己的应用注册到 FCM 推送平台,得到 AppIDAppKey 等参数以及 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"
说明:
TIMPush 需要集成 IM SDK 在7.9.5666版本及以上。TIMPush 的版本号及更新信息,可以在 更新日志 中进行查询。

步骤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.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import androidx.localbroadcastmanager.content.LocalBroadcastManager

class 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? = null

override 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.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.os.Build
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat

class IncomingCallNotificationManager(private val context: Context) {
private val channelId = "incoming_call_channel"
private val channelName = "来电通知"
private val notificationId = 1001
private var remoteViews: RemoteViews? = null
private val notificationManager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

init {
// 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">

<!-- 用户头像 -->
<ImageView
android: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" />

<!-- 用户信息和描述 -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dp"
android:orientation="vertical">

<TextView
android: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="来电" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:orientation="horizontal"
android:gravity="center_vertical">

<ImageView
android:id="@+id/img_call_type"
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_audio_call" />

<TextView
android: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? = null

override 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.inviterId
val 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);

交流与反馈

如果您在使用过程中,有什么建议或者意见,可以 联系我们,感谢您的反馈。