快速接入
本文将介绍如何在 Android 工程中集成 TIMPush。
前提条件
方式1:AI 集成
使用 npx 安装 @tencent-rtc/trtc-push-skill 到本地 AI IDE 中,辅助完成 TIMPush 离线推送集成。安装后,你可以直接向 AI 输入“集成 Android FCM 离线推送”等需求,AI 将根据项目类型引导你完成环境检测、厂商通道配置、凭据填写、代码接入和验证等步骤。详情可参考 AI Coding。
方式2:手动集成
步骤1:集成 TIMPush SDK
下载并添加 TIMPush 配置文件

下载后,将该文件添加到应用模块的
assets 目录。推荐存储路径:app/src/main/assets/timpush-configs.json,其中 app 是你项目的应用模块,可替换成实际名称。如果工程中没有 assets 目录,请在 src/main/ 下手动创建。配置 Gradle 仓库
配置仓库的目的是让 Gradle 能下载 TIMPush 及相关依赖。本节只配置与厂商无关的公共仓库:
google()、mavenCentral() 用于解析 Android 官方组件,腾讯云 Maven 仓库用于解析 TIMPush 等腾讯云依赖。Android 工程可能使用 Groovy DSL 或 Kotlin DSL,请先根据文件后缀判断当前工程使用的 DSL 类型:
文件 | DSL 类型 |
settings.gradle、build.gradle | Groovy DSL |
settings.gradle.kts、build.gradle.kts | Kotlin DSL |
请根据工程使用的 Gradle 版本选择配置位置。如果不确定工程使用的 Gradle 版本,可在项目根目录
gradle/wrapper/gradle-wrapper.properties 中查看 distributionUrl 对应的版本号。在项目级
settings.gradle(Groovy DSL)或 settings.gradle.kts(Kotlin DSL)中,同时在 pluginManagement > repositories 和 dependencyResolutionManagement > repositories 中添加仓库。pluginManagement {repositories {gradlePluginPortal()google()mavenCentral()maven { url "https://mirrors.tencent.com/nexus/repository/maven-public/" }}}dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)repositories {google()mavenCentral()maven { url "https://mirrors.tencent.com/nexus/repository/maven-public/" }}}
pluginManagement {repositories {gradlePluginPortal()google()mavenCentral()maven { url = uri("https://mirrors.tencent.com/nexus/repository/maven-public/") }}}dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)repositories {google()mavenCentral()maven { url = uri("https://mirrors.tencent.com/nexus/repository/maven-public/") }}}
1. 在项目级
build.gradle 的 buildscript > repositories 中添加插件仓库。buildscript {repositories {google()mavenCentral()maven { url "https://mirrors.tencent.com/nexus/repository/maven-public/" }}}
2. 在
settings.gradle 的 dependencyResolutionManagement > repositories 中添加依赖仓库。dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)repositories {google()mavenCentral()maven { url "https://mirrors.tencent.com/nexus/repository/maven-public/" }}}
在项目级
build.gradle 的 buildscript > repositories 和 allprojects > repositories 中添加仓库。buildscript {repositories {google()mavenCentral()maven { url "https://mirrors.tencent.com/nexus/repository/maven-public/" }}}allprojects {repositories {google()mavenCentral()maven { url "https://mirrors.tencent.com/nexus/repository/maven-public/" }}}
集成 TIMPush 基础依赖
在应用模块的
build.gradle 或 build.gradle.kts 中添加 TIMPush 基础依赖。基础包提供 TIMPush 注册、监听和通用能力。dependencies {implementation 'com.tencent.timpush:timpush:VERSION'implementation 'com.tencent.liteav.tuikit:tuicore:VERSION'}
dependencies {implementation("com.tencent.timpush:timpush:VERSION")implementation("com.tencent.liteav.tuikit:tuicore:VERSION")}
注意:
项目已接入 Chat SDK 或 TUIKit 时,也需要确认相关 SDK 版本兼容。
步骤2:集成厂商通道 SDK
请按照配置厂商通道。
前置
已在控制台添加 FCM 厂商证书;
已重新下载包含 FCM 证书的
timpush-configs.json,并放入应用模块 assets 目录;测试设备具备可用的 Google Play 服务(国内无 GMS 的设备无法验证 FCM 通道)。
1. 添加厂商配置文件
在 Firebase 控制台下载
google-services.json,添加到应用模块根目录(与应用模块的 build.gradle / build.gradle.kts 同级):app/google-services.json
2. 配置 Gradle 插件
配置项目级插件依赖
Google Services 插件通过
buildscript > dependencies > classpath 声明,因此项目级 build.gradle 或 build.gradle.kts 的 buildscript > repositories 中需包含 google() 等仓库,否则插件 classpath 可能解析失败。项目级已用 plugins {} 块声明 AGP 时保留原写法即可,无需迁移。buildscript {repositories {google()mavenCentral()gradlePluginPortal()maven { url "https://mirrors.tencent.com/nexus/repository/maven-public/" }}dependencies {// 请与项目当前 Android Gradle Plugin 版本保持一致。classpath 'com.android.tools.build:gradle:<AGP_VERSION>'// 如果项目使用 Kotlin Android 插件,请与项目当前 Kotlin Gradle Plugin 版本保持一致。classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:<KOTLIN_VERSION>'// Google FCM。4.4.2 要求 AGP 7.3.0 及以上,更低的 AGP 请改用 4.3.15。classpath 'com.google.gms:google-services:4.4.2'}}
buildscript {repositories {google()mavenCentral()gradlePluginPortal()maven { url = uri("https://mirrors.tencent.com/nexus/repository/maven-public/") }}dependencies {// 请与项目当前 Android Gradle Plugin 版本保持一致。classpath("com.android.tools.build:gradle:<AGP_VERSION>")// 如果项目使用 Kotlin Android 插件,请与项目当前 Kotlin Gradle Plugin 版本保持一致。classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:<KOTLIN_VERSION>")// Google FCM。4.4.2 要求 AGP 7.3.0 及以上,更低的 AGP 请改用 4.3.15。classpath("com.google.gms:google-services:4.4.2")}}
说明:
com.google.gms:google-services:4.4.2 要求 AGP 7.3.0 及以上;如果项目的 AGP 低于 7.3.0,请改用与当前 AGP 兼容的 Google Services Gradle Plugin 版本,例如 4.3.15。在项目级
build.gradle 的 buildscript > dependencies 中追加:classpath 'com.google.gms:google-services:4.3.15'
在应用模块启用插件
在应用模块的
build.gradle 或 build.gradle.kts 中启用 Google Services 插件。plugins {// Google FCM。id 'com.google.gms.google-services'}// 或:apply plugin: 'com.google.gms.google-services'
plugins {// Google FCM。id("com.google.gms.google-services")}// 或:apply(plugin = "com.google.gms.google-services")
3. 集成 TIMPush 厂商依赖
在应用模块
build.gradle 或 build.gradle.kts 的 dependencies 中追加 FCM 通道包,VERSION 与基础包保持一致:implementation 'com.tencent.timpush:fcm:VERSION'
implementation("com.tencent.timpush:fcm:VERSION")
4. 配置消息分类(可选)
Android 8.0 及以上可通过通知渠道 ID 控制 FCM 通知展示策略。渠道创建与配置见 Google FCM 厂商配置。发送离线推送时可通过 SDK API 设置,API 设置优先级通常高于控制台证书默认配置。调用
setAndroidFCMChannelID(String channelID) 设置 FCM 通道通知渠道 ID:V2TIMOfflinePushInfo pushInfo = new V2TIMOfflinePushInfo();pushInfo.setAndroidFCMChannelID("your_fcm_channel_id");
步骤3:注册推送服务
调用 registerPush
registerPush 用于向 TIMPush 注册当前设备的推送 token。注册成功后,TIMPush 会为当前设备建立一个推送目标标识
registrationID;服务端、控制台接入测试和排查工具可以使用该标识向这台设备下发离线推送。若 App 同时接入 Chat 并完成登录,也可以使用 userID 向该用户已建立推送关系的设备下发离线推送。appKey 的取值会影响注册方式和可用的推送标识:appKey = Push Key:注册 TIMPush 独立推送能力。Push Key 客户端密钥。appKey = null:复用 Chat 登录态注册推送,必须在 Chat login 成功后调用。请先根据业务场景确认调用顺序:
注意:
如果用户退出 Chat SDK,同时集成了 Chat SDK + TIMPush 的场景下已建立的
userID 与 registrationID 推送关系都会失效,需要重新完成对应注册。Chat 应用的密钥仅用于 Chat 登录,不能作为
registerPush 的 appKey。用户同意隐私政策后调用
registerPush(appKey = Push Key)。建议在 registerPush 成功回调里调用 getRegistrationID 打印 registrationID,方便后续根据 registrationID 发送离线推送消息。import android.util.Log;import com.tencent.timpush.TIMPushCallback;import com.tencent.timpush.TIMPushManager;// 在用户同意隐私政策后调用private void registerTIMPush() {int sdkAppId = 0; // TODO: Replace with your SDKAppID.String appKey = "你的客户端密钥"; // TODO: Replace with your Push Key.TIMPushManager.getInstance().registerPush(this, sdkAppId, appKey, new TIMPushCallback<Object>() {@Overridepublic void onSuccess(Object data) {Log.d(TAG, ">>>>> registerPush success, data = " + data);TIMPushManager.getInstance().getRegistrationID(new TIMPushCallback<Object>() {@Overridepublic void onSuccess(Object data) {String registrationID = (String) data;Log.d(TAG, ">>>>> getRegistrationID success, registrationID = " + registrationID);}@Overridepublic void onError(int errCode, String errMsg, Object data) {Log.e(TAG, ">>>>> getRegistrationID failed, errCode = " + errCode+ ", errMsg = " + errMsg);}});}@Overridepublic void onError(int errCode, String errMsg, Object data) {Log.e(TAG, ">>>>> registerPush failed, errCode = " + errCode+ ", errMsg = " + errMsg);}});}
import android.util.Logimport com.tencent.timpush.TIMPushCallbackimport com.tencent.timpush.TIMPushManager// 在用户同意隐私政策后调用private fun registerTIMPush() {val sdkAppId = 0 // TODO: Replace with your SDKAppID.val appKey = "你的客户端密钥" // TODO: Replace with your Push Key.TIMPushManager.getInstance().registerPush(this, sdkAppId, appKey, object : TIMPushCallback<Any?>() {override fun onSuccess(data: Any?) {Log.d("TIMPush", ">>>>> registerPush success, data = $data")TIMPushManager.getInstance().getRegistrationID(object : TIMPushCallback<Any?>() {override fun onSuccess(data: Any?) {Log.d("TIMPush", ">>>>> getRegistrationID success, registrationID = $data")}override fun onError(errCode: Int, errMsg: String?, data: Any?) {Log.e("TIMPush", ">>>>> getRegistrationID failed, errCode = $errCode, errMsg = $errMsg")}})}override fun onError(errCode: Int, errMsg: String?, data: Any?) {Log.e("TIMPush", ">>>>> registerPush failed, errCode = $errCode, errMsg = $errMsg")}})}
请在 Chat
login 成功回调中调用 registerPush(appKey = null)。建议在 registerPush 成功回调里调用 getRegistrationID 打印 registrationID,方便后续根据 registrationID 发送离线推送消息。import android.content.Context;import android.util.Log;import com.tencent.imsdk.v2.V2TIMCallback;import com.tencent.imsdk.v2.V2TIMManager;import com.tencent.imsdk.v2.V2TIMSDKConfig;import com.tencent.qcloud.tim.push.TIMPushCallback;import com.tencent.qcloud.tim.push.TIMPushManager;public void loginIMAndRegisterPush(Context context) {int sdkAppId = 0; // TODO: Replace with your SDKAppID.String userID = "<YOUR_USER_ID>";String userSig = "<YOUR_USER_SIG>";boolean initSuccess = V2TIMManager.getInstance().initSDK(context, sdkAppId, new V2TIMSDKConfig());if (!initSuccess) {Log.e("TIMPush", ">>>>> IM SDK init failed");return;}V2TIMManager.getInstance().login(userID, userSig, new V2TIMCallback() {@Overridepublic void onSuccess() {TIMPushManager.getInstance().registerPush(context, sdkAppId, null, new TIMPushCallback<Object>() {@Overridepublic void onSuccess(Object data) {Log.d("TIMPush", ">>>>> registerPush success, data = " + data);TIMPushManager.getInstance().getRegistrationID(new TIMPushCallback<Object>() {@Overridepublic void onSuccess(Object data) {String registrationID = (String) data;Log.d(TAG, ">>>>> getRegistrationID success, registrationID = " + registrationID);}@Overridepublic void onError(int errCode, String errMsg, Object data) {Log.e(TAG, ">>>>> getRegistrationID failed, errCode = " + errCode+ ", errMsg = " + errMsg);}});}@Overridepublic void onError(int errCode, String errMsg, Object data) {Log.e("TIMPush", ">>>>> registerPush failed, errCode = " + errCode+ ", errMsg = " + errMsg);}});}@Overridepublic void onError(int code, String desc) {Log.e("TIMPush", ">>>>> IM login failed, code = " + code + ", desc = " + desc);}});}
import android.content.Contextimport android.util.Logimport com.tencent.imsdk.v2.V2TIMCallbackimport com.tencent.imsdk.v2.V2TIMManagerimport com.tencent.imsdk.v2.V2TIMSDKConfigimport com.tencent.qcloud.tim.push.TIMPushCallbackimport com.tencent.qcloud.tim.push.TIMPushManagerfun loginIMAndRegisterPush(context: Context) {val sdkAppId = 0 // TODO: Replace with your SDKAppID.val userID = "<YOUR_USER_ID>"val userSig = "<YOUR_USER_SIG>"val initSuccess = V2TIMManager.getInstance().initSDK(context, sdkAppId, V2TIMSDKConfig())if (!initSuccess) {Log.e("TIMPush", ">>>>> IM SDK init failed")return}V2TIMManager.getInstance().login(userID, userSig, object : V2TIMCallback {override fun onSuccess() {TIMPushManager.getInstance().registerPush(context, sdkAppId, null, object : TIMPushCallback<Any>() {override fun onSuccess(data: Any?) {Log.d("TIMPush", ">>>>> registerPush success, data = $data")TIMPushManager.getInstance().getRegistrationID(object : TIMPushCallback<Any?>() {override fun onSuccess(data: Any?) {Log.d("TIMPush", ">>>>> getRegistrationID success, registrationID = $data")}override fun onError(errCode: Int, errMsg: String?, data: Any?) {Log.e("TIMPush", ">>>>> getRegistrationID failed, errCode = $errCode, errMsg = $errMsg")}})}override fun onError(errCode: Int, errMsg: String?, data: Any?) {Log.e("TIMPush", ">>>>> registerPush failed, errCode = $errCode, errMsg = $errMsg")}})}override fun onError(code: Int, desc: String?) {Log.e("TIMPush", ">>>>> IM login failed, code = $code, desc = $desc")}})}
警告:
如果你的业务仅使用即时通信 Chat 聊天能力,请勿在 Chat 登录前先调用
registerPush。否则 SDK 可能会按独立推送场景注册 Push 类型账号,并产生对应的 Push DAU。Push DAU 超出套餐额度后,可能会产生额外费用。验证:
1.
registerPush 的 onSuccess 回调被触发。2. 登录 控制台 > Push > App Push > 推送排查,按当前场景输入
registrationID 或 userID,确认 token 已上传。3. 如触发
onError,可按 错误码 查询 code 含义。自定义 registrationID(可选)
注意:
混用场景下,自定义
registrationID 必须与 Chat 登录使用的 userID 完全一致,否则会产生账号互踢导致推送丢失。步骤4:测试推送链路
完成上述集成步骤后,需要通过发送测试消息验证链路是否打通。发送消息前请确认:
1. Android 13 及以上已允许通知权限;
2. Android 8.0 及以上目标通知渠道已开启(包括横幅、锁屏、声音开关);
3. App 已置于后台。
发送测试消息可以采用下面几种方法:
仅集成 TIMPush 的用户,建议优先使用控制台接入测试能力验证离线推送。
V2TIMOfflinePushInfo pushInfo = new V2TIMOfflinePushInfo();pushInfo.setTitle("推送标题");pushInfo.setDesc("推送内容");pushInfo.setExt("业务自定义 ext".getBytes());V2TIMManager.getMessageManager().sendMessage(v2TIMMessage,userID,null,V2TIMMessage.V2TIM_PRIORITY_DEFAULT,false,pushInfo,new V2TIMSendCallback<V2TIMMessage>() {@Overridepublic void onProgress(int progress) {}@Overridepublic void onError(int code, String desc) {Log.e("TIMPush", ">>>>> sendMessage failed, code = " + code + ", desc = " + desc);}@Overridepublic void onSuccess(V2TIMMessage message) {Log.d("TIMPush", ">>>>> sendMessage success, msgID = " + message.getMsgID());}});
sendMessage 属于 IMSDK 消息发送能力。仅集成 TIMPush 的用户不需要为了验证离线推送而额外接入完整 Chat 初始化、登录和消息发送流程。验证:App 置于后台后发送测试消息,设备能收到离线推送通知。如果手机通知栏开启权限,通知栏会弹出离线推送消息弹框。
步骤5:处理通知点击跳转
通知点击跳转需要三步配合完成:控制台配置点击动作、发送推送时携带跳转参数、客户端注册监听并解析参数。三步缺一则跳转不生效。
配置控制台点击动作

发送推送时携带跳转参数
发送离线推送时,通过
ext 字段携带跳转所需的业务信息(如目标页面、会话 ID 等)。ext 是一个字符串,结构由业务自定义,推荐使用 JSON 格式便于客户端解析。下文示例统一使用如下结构演示:# conversationType 为 1 表示单聊(conversationID 填消息发送方 userID),为 2 表示群聊(conversationID 填 groupID)。{"conversationID":"user_A","conversationType":1}
通过 REST API 发送推送时,在请求体的
Ext 字段中设置 JSON 字符串:{"MsgBody": [],"OfflinePushInfo": {"PushFlag": 0,"Title": "离线推送标题","Desc": "离线推送内容","Ext": "{\"conversationID\":\"user_A\",\"conversationType\":1}"}}
控制台接入测试页面同样支持设置
Ext 字段,填入 JSON 字符串即可。如果你接入了 TUIKit,TUIKit 内置的消息发送链路会自动用
OfflinePushExtInfo 组装 ext,无需手动设置。下方示例适用于自行调用 Chat SDK 发送消息的场景。import android.util.Log;import com.tencent.imsdk.v2.V2TIMManager;import com.tencent.imsdk.v2.V2TIMMessage;import com.tencent.imsdk.v2.V2TIMOfflinePushInfo;import com.tencent.imsdk.v2.V2TIMSendCallback;V2TIMOfflinePushInfo pushInfo = new V2TIMOfflinePushInfo();pushInfo.setTitle("推送标题");pushInfo.setDesc("推送内容");// TODO: ext 由业务自定义,按需替换为您的目标页面、会话 ID 等参数。String ext = "{\"conversationID\":\"user_A\",\"conversationType\":1}";pushInfo.setExt(ext.getBytes());V2TIMMessage message = V2TIMManager.getMessageManager().createTextMessage("Hello TIMPush");V2TIMManager.getMessageManager().sendMessage(message,"<TARGET_USER_ID>", // 单聊填对端 userID,群聊该参数填 nullnull, // 群聊填 groupID,单聊该参数填 nullV2TIMMessage.V2TIM_PRIORITY_DEFAULT,false,pushInfo,new V2TIMSendCallback<V2TIMMessage>() {@Overridepublic void onProgress(int progress) {}@Overridepublic void onSuccess(V2TIMMessage msg) {Log.d("TIMPush", ">>>>> sendMessage success, msgID = " + msg.getMsgID());}@Overridepublic void onError(int code, String desc) {Log.e("TIMPush", ">>>>> sendMessage failed, code = " + code + ", desc = " + desc);}});
import android.util.Logimport com.tencent.imsdk.v2.V2TIMManagerimport com.tencent.imsdk.v2.V2TIMMessageimport com.tencent.imsdk.v2.V2TIMOfflinePushInfoimport com.tencent.imsdk.v2.V2TIMSendCallbackval pushInfo = V2TIMOfflinePushInfo()pushInfo.title = "推送标题"pushInfo.desc = "推送内容"// TODO: ext 由业务自定义,按需替换为您的目标页面、会话 ID 等参数。pushInfo.ext = "{\"conversationID\":\"user_A\",\"conversationType\":1}".toByteArray()val message = V2TIMManager.getMessageManager().createTextMessage("Hello TIMPush")V2TIMManager.getMessageManager().sendMessage(message,"<TARGET_USER_ID>", // 单聊填对端 userID,群聊该参数填 nullnull, // 群聊填 groupID,单聊该参数填 nullV2TIMMessage.V2TIM_PRIORITY_DEFAULT,false,pushInfo,object : V2TIMSendCallback<V2TIMMessage> {override fun onProgress(progress: Int) {}override fun onSuccess(msg: V2TIMMessage) {Log.d("TIMPush", ">>>>> sendMessage success, msgID = ${msg.msgID}")}override fun onError(code: Int, desc: String?) {Log.e("TIMPush", ">>>>> sendMessage failed, code = $code, desc = $desc")}})
客户端注册监听并解析跳转参数
请在
Application.onCreate() 中调用 addPushListener 注册 TIMPushListener,并在 onNotificationClicked 中解析 ext 后跳转到业务页面。如果你接入了 TUIKit 并使用 OfflinePushExtInfo 组装的 ext,可改为 new Gson().fromJson(ext, OfflinePushExtInfo.class) 解析后再跳转。import android.app.Application;import android.text.TextUtils;import android.util.Log;import com.tencent.timpush.TIMPushListener;import com.tencent.timpush.TIMPushManager;import org.json.JSONObject;public class App extends Application {private static final String TAG = "TIMPush";@Overridepublic void onCreate() {super.onCreate();TIMPushManager.getInstance().addPushListener(new TIMPushListener() {@Overridepublic void onNotificationClicked(String ext) {Log.d(TAG, ">>>>> TIMPush notification clicked, ext = " + ext);if (TextUtils.isEmpty(ext)) {return;}// 1. 解析 ext。JSON 结构由业务自定义,需与发送端约定一致。String conversationID;int conversationType;try {JSONObject json = new JSONObject(ext);conversationID = json.optString("conversationID");conversationType = json.optInt("conversationType");} catch (Exception e) {Log.e(TAG, ">>>>> parse ext failed: " + e.getMessage());return;}// 2. TODO: 根据业务字段跳转到目标页面。// 若使用了 Chat / TUIKit,建议在用户登录成功后再跳转;// 冷启动场景可先把参数缓存起来,登录回调中再跳转。}});}}
import android.app.Applicationimport android.text.TextUtilsimport android.util.Logimport com.tencent.timpush.TIMPushListenerimport com.tencent.timpush.TIMPushManagerimport org.json.JSONObjectclass App : Application() {override fun onCreate() {super.onCreate()TIMPushManager.getInstance().addPushListener(object : TIMPushListener() {override fun onNotificationClicked(ext: String?) {Log.d("TIMPush", ">>>>> TIMPush notification clicked, ext = $ext")if (TextUtils.isEmpty(ext)) return// 1. 解析 ext。JSON 结构由业务自定义,需与发送端约定一致。val conversationID: Stringval conversationType: Inttry {val json = JSONObject(ext)conversationID = json.optString("conversationID")conversationType = json.optInt("conversationType")} catch (e: Exception) {Log.e("TIMPush", ">>>>> parse ext failed: ${e.message}")return}// 2. TODO: 根据业务字段跳转到目标页面。// 若使用了 Chat / TUIKit,建议在用户登录成功后再跳转;// 冷启动场景可先把参数缓存起来,登录回调中再跳转。}})}}
