即时通信 IM 接入离线推送(FCM)

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

相关产品
即时通信 IM 产品页,含版本规格、价格与购买入口。

一、开通服务

进入 IM 控制台 > 插件市场,单击立即购买或免费试用。

注意:购买及免费试用规则详见 计费说明。

二、如何选择推送渠道?

由于 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"

注意:说明:

注意: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.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? = 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.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 = 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 通话媒体类型,用于指定发起音频通话还是视频通话。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 此次通话的媒体类型。CallMediaType.Video:视频通话。CallMediaType.Audio:语音通话。
result CallDirection 通话结果/方向。CallDirection.Unknown:未知通话。CallDirection.Missed:未接通话。CallDirection.Incoming:来电通话。CallDirection.Outgoing:拨出通话。
duration Long 通话时长。
startTime Long 通话接通的开始时间。

十、运行效果

完成以上 3 步以后,来电通知效果如下:

十一、常见问题

若在集成过程中遇到问题,请务必先查阅 插件推送 > 常见问题 进行自助排查。

十二、应用被杀死后无法弹出来电界面

·确认收到了推送,收不到推送需要确认下 IM 控制台是否正确上传证书。参见文档上述快速接入的第一步,看下是否添加正确。

·确认控制台选择了 FCM 数据消息,对照上述准备条件的第二步。

·确认收到数据消息,过滤日志(关键字:TIMPush),检查下述日志是否有打印记录(如果收不到消息,可以使用 IM 控制台的 排查工具 查看原因)。

·确认实现了自动登录。自动登录后才会去拉取通话请求,才能显示来电界面。

十三、怎样强制使用 FCM 通道?

若你的业务想要强制使用 FCM 通道,可调用以下接口:

TIMPushManager.getInstance().forceUseFCMPushChannel(true);

十四、交流与反馈

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

赞 (0)
上一篇 4天前
下一篇 4天前