腾讯云即时通信 IM 的终端用户需要随时都能够得知最新的消息,而由于移动端设备的性能与电量有限,当 App 处于后台时,为了避免维持长连接而导致的过多资源消耗,腾讯云即时通信 IM 推荐您使用各厂商提供的系统级推送通道来进行消息通知,系统级的推送通道相比第三方推送拥有更稳定的系统级长连接,可以做到随时接受推送消息,且资源消耗大幅降低。

说明:

  • 在没有主动退出登录的情况下,应用退后台、手机锁屏、或者应用进程被用户主动杀掉三种场景下,如果想继续接收到 IM 消息提醒,可以接入即时通信 IM 离线推送。
  • 如果应用主动调用 logout 退出登录,或者多端登录被踢下线,即使接入了 IM 离线推送,也收不到离线推送消息。

使用腾讯云 IM 厂商推送 Flutter 集成插件的离线推送能力,可快速接入主流厂商(苹果iOS/Google FCM/OPPO/VIVO/华为/小米/魅族)的离线推送。

本教程含接入腾讯云即时通信 IM 离线推送全流程。插件已封装上述厂商的 SDK,使用时仅需简单改造调用即可。

如果您的应用不需要离线推送,或场景不满足离线推送的需求,请直接看本文最后一节 “在线推送-在本地创建新消息通知” 在线推送部分。

如果您的应用已经自行完成厂商离线推送,仅需查看本教程 第一步第五步,在控制台内录入厂商信息,并在应用登录后,上报证书 ID 即可。

插件 API 概览

说明:

以下 API 若无特殊说明,均可自动兼容 Android/iOS 平台及支持厂商,插件内部进行平台及厂商判断,您直接调用即可。

API 说明
构造函数(TimUiKitPushPlugin) 实例化一个 Push 插件对象,并确定是否使用 Google Service FCM
init 初始化插件,绑定点击通知回调事件及传入厂商渠道信息
uploadToken 自动获取设备 Token 及证书 ID,自动上传至腾讯云 IM 服务端
requireNotificationPermission 申请推送权限
setBadgeNum 设置未读数角标 (仅支持部分 Android 设备,可参见 API 代码参数说明)
clearAllNotification 清除通知栏内,当前应用,所有的通知
getDevicePushConfig 获取当前厂商的推送相关信息,含机型/证书 ID/Token
getDevicePushToken 获取当前厂商的推送 Token
getOtherPushType 获取厂商信息
getBuzId 获取当前厂商对应的腾讯云控制台上注册的证书 ID
createNotificationChannel 为 Android 机型创建通知 Channel 渠道
clearAllNotification 清除通知栏内,当前应用,所有的通知,详见 Google 官方文档
displayNotification 在客户端本地,手动创建一条消息通知
displayDefaultNotificationForMessage 在客户端本地,按照默认的规则,自动为一个 V2TimMessage 创建一个消息通知

接入准备(注册厂商)

需要完成厂商开发者账号申请(一般需要企业认证),创建应用,申请 PUSH 权限,拿到 key 信息。

Apple

iOS

  1. 请根据该教程,完成 Apple 推送证书申请
  2. 分别将申请到的生产环境和开发环境证书托管至即时通信 IM 控制台。
  3. 打开 IM 控制台-基础配置 右侧,添加 iOS 证书。

Android

Google FCM

  1. 前往 Google Firebase控制台 创建一个项目,无需启用 Google Analysis。
  2. 单击您的应用卡片,进入应用配置页面。
  3. 单击 Project Overview 右侧的 ,选择项目设置>服务帐号,单击生成新的私钥下载私钥文件。
  4. 将该私钥文件托管至即时通信 IM 控制台。在 IM 控制台-基础配置 右侧,添加 Android 证书。选择 Google 后,请选择上传证书。

OPPO

开通服务

请参见 OPPO PUSH 服务开启指南 注册开发者账号,创建应用,并开通 PUSH 服务。

OPPO 推送平台 >配置管理>应用配置页面,您可以查看详细的应用信息。记录 AppId、AppKey、AppSecret 和 MasterSecret 信息。

创建消息通道

按照 OPPO 官网要求,在 OPPO Android 8.0 及以上系统版本必须配置 ChannelID,否则推送消息无法展示。您需要先在 App 中创建对应的 ChannelID(例如 tuikit)。

请在配置管理-新建通道内,创建一个新通道。通道ID即为Channel ID

说明:

OPPO 对于公信通道有每日上限,对于通讯类型消息,建议参见 OPPO 官方文档 申请私信通道。

上传证书至控制台
  1. IM 控制台-基础配置 右侧,添加 Android 证书。选择 OPPO 后,请填写相关信息。
  2. ChannelID 请填写此前在 OPPO 控制台为通讯能力申请的专用通道,最好是私信,以免到达每日推送上限。
  3. 打开方式请选择打开应用内指定页面 > activity,填写com.tencent.flutter.tim_ui_kit_push_plugin.pushActivity.OPPOMessageActivity

小米

开通服务

打开 小米开放平台官网 进行注册并通过开发者认证。

说明:

认证过程大约需要2天左右,请务必提前阅读 小米推送服务启用指南,以免影响您的接入进度。

在小米开放平台创建应用,并选择应用服务>PUSH服务,创建推送服务应用。

小米推送服务应用创建完成后,在应用详情中,您可以查看详细的应用信息。

记录主包名AppIDAppSecret 信息。

上传证书至控制台

IM 控制台-基础配置 右侧,添加 Android 证书。选择小米后,请填写相关信息,行为请选择打开应用。

vivo

开通服务

打开 vivo 开放平台官网 进行注册并通过开发者认证。

说明:

认证过程大约需要3天左右,请务必提前阅读 vivo 推送服务说明,以免影响您的接入进度。

  1. 登录 vivo 开放平台的管理中心,选择消息推送>创建>测试推送,创建 vivo 推送服务应用。

  2. vivo 推送服务应用创建完成后,在应用详情中,您可以查看详细的应用信息。记录 APP ID、APP key 和 App secret信息。

说明:

要求应用在上架后,才能使用正式推送服务。如果您需要在开发中调试 vivo 机器,请参见本文最后一节 vivo 调试 内容,开启测试模式。

上传证书至控制台

IM 控制台-基础配置 右侧,添加 Android 证书。选择 vivo 后,请填写相关信息。

  • 单击后续动作请选择为:打开应用内指定页面。
  • 应用内页面 配置为:tencent_im_push://${替换成您的包名}/message?#Intent;scheme=tencent_im_push;launchFlags=0x4000000;end

华为

获取密钥
  1. 进入 华为开放平台,注册和登录开发者账号,详情参见 账号注册认证(如果您是新注册账号,需进行实名认证)。
  2. 在华为推送平台中新建应用,详情参见 创建应用。记录AppIDAppSecret 信息。

说明:

若在应用信息>我的应用中无法找到 SecretKey,可前往项目设置>常规中查看 Client Secret。

配置 SHA256 证书指纹

获取 SHA256 证书指纹,并在华为推送平台中配置证书指纹,单击 保存。证书指纹获取可参见 生成签名证书指纹

说明:

如果您的应用需要经过流水线编译发布,每次编译在不同的构建机上进行,可在本地创建keystore.jks密钥文件,得到该 keystore 的 SHA256 值,填入华为推送平台中。

在流水线的构建脚本中,对完成构建后的产物进行归档对齐,及使用刚才的 keystore 签名。此时该最终产物签名 SHA256 值即可保持一致。代码如下:

zipalign -v -p 4 构建生成的apk.apk 打包生成的apk_aligned.apk 
apksigner sign --ks keystore.jks --ks-pass pass:您创建的keystore密码 --out 最终签名  完成的apk.apk 打包生成的apk_aligned.apk

获取华为推送配置文件

登录华为开放平台,进入我的项目> 选择项目 > 项目设置,下载华为应用最新配置文件 agconnect-services.json。放置于android/app目录下。

打开推送服务开关

在华为推送平台,单击全部服务>推送服务,进入推送服务页面。

推送服务页面,单击立即开通,详情请参见 打开推送服务开关

上传证书至控制台
  1. IM 控制台-基础配置 右侧,添加 Android 证书。
  2. 选择华为后,请填写相关信息。

角标参数请填写Android应用入口 Activity 类,如我们DEMO的 com.tencent.flutter.tuikit,否则华为通道下发通知的角标设置将不生效。

点击后续动作请选择打开应用。

20220614153143

魅族

开通服务
  1. 打开 魅族开放平台官网 进行注册并通过开发者认证。

    说明:

    认证过程大约需要3天左右,请务必提前阅读 魅族 Flyme 推送接入文档,以免影响您的接入进度。

  2. 登录魅族开放平台的管理控制台,选择服务>集成推送服务>推送后台,创建魅族推送服务应用。

  3. 魅族推送服务应用创建完成后,在应用详情中,您可以查看详细的应用信息。记录应用包名App IDApp Secret信息。

上传证书至控制台
  1. IM 控制台-基础配置 右侧,添加 Android 证书。
  2. 选择华为后,请填写相关信息。单击后续动作请选择:打开应用

使用插件跑通离线推送(全览 + Android)

在您的项目中安装 IM Flutter 离线推送插件:

flutter pub add tim_ui_kit_push_plugin

并根据该指南,在插件市场,启用推送插件。

步骤1: 汇总常量类

  1. 完成 接入准备(注册厂商)的配置后,可在即时通信 IM 的控制台首页右侧,查看我们后台为您的厂商渠道 App 信息分配的证书 ID。
  2. 请将这些信息,配上厂商渠道的账号信息,实例化一个静态的PushAppInfo类,汇总起来。后续步骤需要传入此对象。
  3. 该类支持配置所有您需要接入厂商推送机型的信息。无需完整填写构造函数字段。若需要使用某个厂商平台,请完整填写该平台相关字段。
import 'package:tim_ui_kit_push_plugin/model/appInfo.dart';

static final PushAppInfo appInfo = PushAppInfo(
  hw_buz_id: , // 华为证书ID
  mi_app_id: , // 小米APPID
  mi_app_key: , // 小米APPKey
  mi_buz_id: , // 小米证书ID
  mz_app_id: , // 魅族APPID
  mz_app_key: , // 魅族APPKey
  mz_buz_id: , // 魅族证书ID
  vivo_buz_id: , // vivo证书ID
  oppo_app_key: , // OPPO APPKey
  oppo_app_secret: , // OPPO APP Secret
  oppo_buz_id: , // OPPO证书ID
  oppo_app_id: , // OPPO APPID
  google_buz_id: , // Google FCM证书ID
  apple_buz_id: , // Apple证书ID
);

说明:

可参见我们DEMO lib/utils/push/push_constant.dart文件 中的做法。

步骤2: 代码中添加厂商工程配置

Google FCM

兼容 Android 模拟器调试

如果需要使用 Firebase Emulator Suite,请打开 android/app/src/main/AndroidManifest.xml 文件,在 application 中新增usesCleartextTraffic字段。

<application 
    android:usesCleartextTraffic="true" // 增加本行
>
  <!-- possibly other elements -->
</application>
集成 Google Firebase Flutter 能力
  1. 请打开 pubspec.yaml 文件,添加对firebase_core的依赖,使用1.12.0版本。

说明:

由于最新版 Google Firebase Flutter 插件最低支持的Dart版本为2.16.0,此处限制为2022年3月发布的1.12.0版本。

dependencies:
  firebase_core: 1.12.0
  1. 执行flutter pub get完成安装。
  2. 在控制台内,执行以下命令,结合操作提示,完成配置 Google Firebase Flutter 项目。 详见 Google FlutterFire 官方文档
// 安装Firebase CLI 
npm install -g firebase-tools
curl -sL https://firebase.tools | bash

dart pub global activate flutterfire_cli

// 生成配置文件
flutterfire configure
  1. 执行该步骤后,会将此项目与您在 Google Firebase 创建的项目关联起来,执行结果可以参见下图: main()方法中初始化 FirebaseAPP。
WidgetsFlutterBinding.ensureInitialized();

await Firebase.initializeApp(
  options: DefaultFirebaseOptions.currentPlatform,
);
不选装 Google FCM 推送
  1. 由于国内大部分机型不支持 Google Service,开发者可无需执行此配置。
  2. 后续引入插件时,将isUseGoogleFCM字段设为 false 即可。

华为

  1. 打开文件 android/build.gradle
  2. buildscript>repositories & dependencies下分别添加华为仓库地址和 HMS gradle 插件依赖:

    buildscript {
     repositories {
         google()
         jcenter()
         maven {url 'https://developer.huawei.com/repo/'}     // 添加华为 maven 仓库地址
     }
     dependencies {
         // 其他classpath配置
         classpath 'com.huawei.agconnect:agcp:1.3.1.300'     // 添加华为推送 gradle 插件依赖
     }
    
     // Set release signing and passwords in the same build configuration file
     signingConfigs {       
        release {            
            storeFile file('<keystore_file>')            
            storePassword '<keystore_password>'            
            keyAlias '<key_alias>'            
            keyPassword '<key_password>'        
        }    
    }  
    buildTypes {        
        // debug模式也要使用证书编译,否则华为指纹验证不通过
        debug {            
            signingConfig signingConfigs.release        
        }       
        release {            
            signingConfig signingConfigs.release        
        }    
     }
    }
    
  3. 打开 android/build.gradle 文件,在allprojects>repositories下添加华为依赖仓库地址:

    allprojects {
     repositories {
         google()
         jcenter()
         maven {url 'https://developer.huawei.com/repo/'}     // 添加华为 maven 仓库地址
     }
    }
    
  4. 登录华为开放平台,进入我的项目> 选择项目 > 项目设置,下载华为应用最新配置文件 agconnect-services.json。放置于android/app目录下。

应用层引入 HMS SDK gradle 插件

打开 android/app/build.gradle 文件,添加以下配置:

// app 其他 gradle 插件
apply plugin: 'com.huawei.agconnect'      // HMS SDK gradle 插件
android {
    // app 配置内容
}
华为/新荣耀推送角标权限

打开 android/app/src/main/AndroidManifest.xml 文件,如下添加 uses-permission 。

<uses-permission android:name = "com.huawei.android.launcher.permission.CHANGE_BADGE "/>
<uses-permission android:name = "com.hihonor.android.launcher.permission.CHANGE_BADGE" />

vivo

配置 APPID 及 APPKey

打开 android/app/build.gradle 文件,如下配置 vivo 的 APPIDApp_Key

      android: {
        defaultConfig {
          manifestPlaceholders = [
            ....
            vivo_APPID: "vivo的APPID"
            vivo_APPKEY:"vivo的APP_Key",
            .....
          ]
        }
      }

打开 android/app/src/main/AndroidManifest.xml 文件,在 <application> 中,如下添加meta-data。

    <meta-data
        android:name="com.vivo.push.api_key"
        android:value="" />
    <meta-data
        android:name="com.vivo.push.app_id"
        android:value="" />
</application>
VIVO角标权限

打开 android/app/src/main/AndroidManifest.xml 文件,如下添加 uses-permission 。

<uses-permission android:name="com.vivo.notification.permission.BADGE_ICON" />

小米/OPPO/魅族

  1. 打开 android/app/build.gradle 文件,在 defaultConfig 中加入包名。
    defaultConfig {
     applicationId "${替换成您的包名}"
     ...
    }
    
  2. 打开 android/app/src/main/AndroidManifest.xml 文件,配置各厂商权限列表。

     <!--小米 开始-->
     <permission
         android:name="${替换成您的包名}.permission.MIPUSH_RECEIVE"
         android:protectionLevel="signature" />
     <uses-permission android:name="${替换成您的包名}.permission.MIPUSH_RECEIVE" />
     <!--小米 结束-->
    
     <!--OPPO 开始-->
     <uses-permission android:name="com.coloros.mcs.permission.RECIEVE_MCS_MESSAGE" />
     <uses-permission android:name="com.heytap.mcs.permission.RECIEVE_MCS_MESSAGE" />
     <!--OPPO 结束-->
    
     <!--魅族 开始-->
     <!-- 可选,用于兼容 Flyme5 且推送服务是旧版本的情况-->
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <!-- 兼容 Flyme5 的权限配置-->
     <uses-permission android:name="com.meizu.flyme.push.permission.RECEIVE" />
     <permission android:name="${替换成您的包名}.push.permission.MESSAGE"
         android:protectionLevel="signature"/>
     <uses-permission android:name="${替换成您的包名}.push.permission.MESSAGE" />
     <!-- 兼容 Flyme3 的权限配置-->
     <uses-permission android:name="com.meizu.c2dm.permission.RECEIVE" />
     <permission android:name="${替换成您的包名}.permission.C2D_MESSAGE" android:protectionLevel="signature"
         />
     <uses-permission android:name="${替换成您的包名}.permission.C2D_MESSAGE"/>
     <!--魅族 结束-->
    

步骤3: 应用启动时初始化

  1. 调用插件init方法。该步骤会完成初始化各厂商通道。
  2. 该步骤建议在应用启动后就执行调用。

    说明:

    由于国内大部分 Android 设备不支持 Google Service, 因此提供一个开关isUseGoogleFCM供开发者根据主要用户群体判断,是否启用 Google Firebase Cloud Messaging 推送服务。

import 'package:tim_ui_kit_push_plugin/tim_ui_kit_push_plugin.dart';

final TimUiKitPushPlugin cPush = TimUiKitPushPlugin(
  isUseGoogleFCM: bool, // 是否启用Google Firebase Cloud Messaging,默认true启用
);
cPush.init(
    pushClickAction: pushClickAction, // 单击通知后的事件回调,会在STEP6讲解
    appInfo: PushConfig.appInfo, // 传入STEP1做的appInfo
);
  1. 初始化结束后,需要为部分厂商创建消息通道,如OPPO和小米均需此配置。调用createNotificationChannel方法即可。

    说明:

    如果向厂商申请的 channel ID 一致,同一个 channel ID 调用一次即可。

    cPush.createNotificationChannel(
    channelId: "new_message", 
    channelName: "消息推送", 
    channelDescription: "推送新聊天消息");
    
  2. 部分厂商(如 OPPO)默认不提供推送权限,需要开发者手动申请。调用requireNotificationPermission方法即可。

    说明:

    申请权限的时机可由您自行决定,您可以在用户登录成功后再调用。

cPush.requireNotificationPermission();

步骤4: 上报 Token 及证书 ID

需要将当前设备对应厂商的证书 ID 及 Device Token 上报至腾讯云即时通信后台,服务端才可正常使用厂商通道下行通知。

插件支持自动在appInfo内找到当前厂商的证书ID,并自动完成Token上报。

说明:

  • 根据个保法内隐私相关规定,请在用户Login后再调用该方法上报。
  • Device Token 在同一设备保持一致,仅需在登录时上报一次即可,无需每次启动都上报。

import 'package:tim_ui_kit_push_plugin/tim_ui_kit_push_plugin.dart';

final TimUiKitPushPlugin cPush = TimUiKitPushPlugin(
    isUseGoogleFCM: false,
  );

final bool isUploadSuccess = await cPush.uploadToken(PushConfig.appInfo);

步骤5: 前后台切换监听

  1. 需要在每次切换前后台时,通过 IM SDK 上报 IM 后端当前状态。
  2. 若为前台在线状态,则收到新消息不触发 notification 推送;反之则会进行推送。
  3. 具体请参见 Flutter 官方监听前后台切换方案

建议:在应用切换到 inactive/paused 状态前,使用插件中setBadgeNum( int badgeNum )方法,将最新未读数同步至桌面角标。iOS角标由IM SDK自动管理,此处本插件支持配置 XIAOMI(MIUI6 - MIUI 11机型), HUAWEI, HONOR, vivo 及 OPPO 设备角标。

说明:

OPPO角标属于OPPO侧高级权益,不默认开放。如需使用,请自行联系OPPO应用推送权益对接人。

  /// coreInstance
  @override
  Future<V2TimCallback> setOfflinePushStatus({required AppStatus status, int? totalCount}) {
    if(Platfrom.isIOS){
      return;
    }
    if(status == AppStatus.foreground){
      // 当应用status为前台时,上报doForeground()
      return TencentImSDKPlugin.v2TIMManager
          .getOfflinePushManager()
          .doForeground();
    }else{
      // 当应用status为后台时,上报doBackground(),并带上未读数
      return TencentImSDKPlugin.v2TIMManager
          .getOfflinePushManager()
          .doBackground(unreadCount: totalCount ?? 0);
    }
  }

  /// App
  final TimUiKitPushPlugin cPush = TimUiKitPushPlugin(
      isUseGoogleFCM: false,
    );

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) async {
    print("--" + state.toString());
    int? unreadCount = await _getTotalUnreadCount();
    switch (state) {
      case AppLifecycleState.inactive: 
        _coreInstance.setOfflinePushStatus(status: AppStatus.background, totalCount: unreadCount);
        if(unreadCount != null){
          cPush.setBadgeNum(unreadCount);
        }
        break;
      case AppLifecycleState.resumed:
        _coreInstance.setOfflinePushStatus(status: AppStatus.foreground);
        break;
      case AppLifecycleState.paused: 
        _coreInstance.setOfflinePushStatus(status: AppStatus.background, totalCount: unreadCount);
        if(unreadCount != null){
          cPush.setBadgeNum(unreadCount);
        }
        break;
    }
  }

步骤6: 发消息配置及单击通知跳转

发送消息

直接通过 SDK 发送

如您自行接入腾讯云 IM SDK,请在发消息时配置OfflinePushInfo offlinePushInfo字段。

OfflinePushInfo({
    this.title = '', // 推送通知标题。留空字符串时,按照优先级,IM后台自动替换成 sender的昵称 => sender ID。因此,如无特殊需求,该字段建议留空,可达到和微信一致的效果
    this.desc = '', // 推送第二行小字部分
    this.disablePush = false,
    this.ext = '', // 推送内额外信息,对方可于单击通知跳转时拿到。建议传含Conversation信息的JSON,用于收件方跳转至对应Chat。可参见下方TUIKit的实例代码。
    this.androidOPPOChannelID = '', // OPPO的channel ID
  });
接入TUIKit

如果您使用我们的 Flutter TUIKit 组件库,可直接在TIMUIKitChat组件TIMUIKitChatConfig中,使用notificationTitle/notificationOPPOChannelID/notificationBody/notificationExt/notificationIOSSound定义自定义推送。详情如下:

TIMUIKitChat(
    config: TIMUIKitChatConfig(
            notificationTitle: "",// 推送通知标题。留空字符串时,按照优先级,IM后台自动替换成sender的昵称 => sender ID。因此,如无特殊需求,该字段建议留空,可达到和微信一致的效果
            notificationOPPOChannelID: "", // 用于推送消息的OPPO配置Channel ID
            notificationBody: (V2TimMessage message, String convID, ConvType convType) {
              return "您根据给出的参数自定义的第二行通知";
            },
            notificationExt: (V2TimMessage message, String convID, ConvType convType) {
              // 您根据给出的参数自定义的EXT字段:此处建议传conversation id,JSON格式,即如下所示
              String createJSON(String convID){
                return "{\"conversationID\": \"$convID\"}";
              }
              String ext =  (convType == ConvType.c2c
                  ? createJSON("c2c_${message.sender}")
                  : createJSON("group_$convID"));
              return ext;
            }
        )
)

处理单击回调

  1. 此时填上 步骤3 初始化时,为 pushClickAction 埋的坑。
  2. 初始化时,注册该回调方法,可拿到含推送本体及 ext 信息在内的 Map。
  3. 如果上一步创建 OfflinePushInfo 时,在 ext 内传入了含 conversationID 的 JSON,此时即可直接跳转到对应 Chat。

说明:

在后台跳转情况下,此时 Flutter 首页可能已经 unmounted,无法为跳转提供 context,因此建议启动时缓存一个 context,保证跳转成功。

建议跳转成功后,清除通知栏中其他通知消息,避免太多IM消息堆积在通知栏中。调用插件中clearAllNotification()方法即可。

BuildContext? _cachedContext;
final TimUiKitPushPlugin cPush = TimUiKitPushPlugin(
      isUseGoogleFCM: false,
    );
// 仅限TUIKit
final TIMUIKitChatController _timuiKitChatController =
  TIMUIKitChatController();

@override
void initState() {
  super.initState();
  _cachedContext = context;
}

void handleClickNotification(Map<String, dynamic> msg) async {
    String ext = msg['ext'] ?? "";
    Map<String, dynamic> extMsp = jsonDecode(ext);
    String convId = extMsp["conversationID"] ?? "";

    // 【TUIKit】若当前的会话与要跳转至的会话一致,则不跳转。
    final currentConvID = _timuiKitChatController.getCurrentConversation();
    if(currentConvID == convId.split("_")[1]){
      return;
    }

    final targetConversationRes = await TencentImSDKPlugin.v2TIMManager
        .getConversationManager()
        .getConversation(conversationID: convId);

    V2TimConversation? targetConversation = targetConversationRes.data;

    if(targetConversation != null){
      cPush.clearAllNotification();
      Navigator.push(
          _cachedContext ?? context,
          MaterialPageRoute(
            builder: (context) => Chat(
              selectedConversation: targetConversation,
            ),
          ));
    }
  }

步骤7: 使用 TRTC 打单聊语音/视频通话,发送离线推送

一般情况下,发起 TRTC 通话使用信令消息通知对方。您可在信令消息中,按照 步骤6,加入offlinePushInfo字段。

Flutter 通话插件接入

  1. 如果您使用到我们的 tim_ui_kit_calling_plugin 插件,请将其升级至0.2.0版本以上,即可使用离线推送能力。
  2. 参见如下示例,直接在call方法第三个参数中,传入offlinePush对象即可。
final user = await sdkInstance.getLoginUser();
final myId = user.data;
OfflinePushInfo offlinePush = OfflinePushInfo(
  title: "",
  desc: "邀请您语音通话",
  ext: "{\"conversationID\": \"c2c_$myId\"}",
  disablePush: false,
  ignoreIOSBadge: false,
  androidOPPOChannelID: PushConfig.OPPOChannelID
);

_calling?.call(widget.selectedConversation.userID!, CallingScenes.Audio, offlinePush);

说明:

通话群邀请暂不支持离线推送。

使用插件跑通离线推送(iOS 增补)

本部分在使用插件跑通离线推送(Android)完成的基础上,补充对应步骤 iOS 端需要做的事情。

该页面没有提到过的步骤,和 Android 端一致。

步骤2: 代码中添加 iOS 工程配置

  1. 使用 Xcode 打开您的项目,在 Runner>Target 中,配置支持 PushSigning Profile
  2. 并在左上角新增Push Notification的 Capability。
  3. 执行flutter pub get安装好插件后进入 iOS 目录,执行:pod install安装依赖库。
  4. 将以下代码添加到 iOS 工程下ios/Runner/AppDelegate.swift文件didFinishLaunchingWithOptions方法中。 Objective-C:
    if (@available(iOS 10.0, *)) {
    [UNUserNotificationCenter currentNotificationCenter].delegate = (id<UNUserNotificationCenterDelegate>) self;
    }
    
    Swift:
    if #available(iOS 10.0, *) {
    UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }
    
  5. 如果不使用 Google Firebase 套件,需要在info.plist加入如下字段。
    <key>flutter_apns.disable_firebase_core</key>
    <false/>
    

步骤3: 应用启动时初始化

调用插件init方法。该步骤会完成初始化各厂商通道,并申请厂商通知权限。该步骤建议在应用启动后就执行调用。

import 'package:tim_ui_kit_push_plugin/tim_ui_kit_push_plugin.dart';

final TimUiKitPushPlugin cPush = TimUiKitPushPlugin();
cPush.init(
    pushClickAction: pushClickAction, // 单击通知后的事件回调,会在STEP6讲解
    appInfo: PushConfig.appInfo, // 传入STEP1做的appInfo
);

步骤6: 发消息配置及单击通知跳转

发送消息

直接通过 SDK 发送

如您自行接入腾讯云 IM SDK,请在发消息时配置OfflinePushInfo offlinePushInfo字段。

OfflinePushInfo({
    // ..其他配置
    this.iOSSound = "", // iOS离线推送声音设置, 当 iOSSound = kIOSOfflinePushNoSound,表示接收时不会播放声音。 当 iOSSound = kIOSOfflinePushDefaultSound,表示接收时播放系统声音。 如果要自定义 iOSSound,需要先把语音文件链接进 Xcode 工程,然后把语音文件名(带后缀)设置给 iOSSound。
    this.ignoreIOSBadge = false,
  });
接入TUIKit

如果您使用我们的 Flutter TUIKit 组件库,可直接在TIMUIKitChat组件TIMUIKitChatConfig中,使用notificationTitle/notificationOPPOChannelID/notificationBody/notificationExt/notificationIOSSound定义自定义推送。详情如下:

TIMUIKitChat(
    config: TIMUIKitChatConfig(
            // ..其他配置
            notificationIOSSound: "", // iOS离线推送声音设置, 当 iOSSound = kIOSOfflinePushNoSound,表示接收时不会播放声音。 当 iOSSound = kIOSOfflinePushDefaultSound,表示接收时播放系统声音。 如果要自定义 iOSSound,需要先把语音文件链接进 Xcode 工程,然后把语音文件名(带后缀)设置给 iOSSound。
        )
)

调试

离线推送自查

您可使用 离线推送自查 工具,检测终端状态/证书上报及发送测试消息。

vivo 调试

由于 vivo 官方限制,应用在 vivo 应用市场上架前,不允许使用正式 PUSH 能力,详见此文档。 开发过程中,需要调试,请参见本步骤:

  1. 获取测试设备(vivo 真机)的 regId(我们称做 Device Token)。
  2. 在 vivo 控制台内,添加该设备为测试设备。
  3. 此时可推送测试消息至测试设备。可参见 vivo 单播推送文档
  4. 由于腾讯云 IM 控制台的测试推送,和直接使用 IM SDK 发送聊天消息的推送,均不能修改推送模式为测试。因此请使用我们提供的,可触发测试消息的JS脚本,单击此处下载
  5. 下载后,请根据顶部五行注释,填入vivo相关参数。默认ext为conversationID,如果在处理单击回调跳转(可参见 步骤6)时需要其他字段,请自行修改JS代码。
  6. 执行脚本。npm install axios npm install js-md5node testvivo。推送结果会显示在 log 最后一行。
  7. 此时测试终端可收到测试消息推送,单击消息后,可触发 Dart 层回调。

厂商推送限制

1、国内厂商都有消息分类机制,不同类型也会有不同的推送策略。如果想要推送及时可靠,需要按照厂商规则设置自己应用的推送类型为高优先级的系统消息类型或者重要消息类型。反之离线推送消息会受厂商推送消息分类影响,与预期会有差异。

2、另外,一些厂商对于应用每天的推送数量也是有限制的,可以在厂商控制台查看应用每日限制的推送数量。 如果离线推送消息出现推送不及时或者偶尔收不到情况,需要考虑下这里:

  • 华为:将推送消息分为服务与通讯类和资讯营销类,推送效果和策略不同。另外,消息分类还和自分类权益有关:

    • 无自分类权益,推送消息厂商还会进行二次智能分类 。
    • 有申请自分类权益,消息分类会按照自定义的分类进行推送。 具体请参见 厂商描述
  • vivo:将推送消息分为系统消息类和运营消息类,推送效果和策略不同。系统消息类型还会进行厂商的智能分类二次修正,若智能分类识别出不是系统消息,会自动修正为运营消息,如果误判可邮件申请反馈。另外,消息推送也受日推总数量限制,日推送量由应用在厂商订阅数统计决定。 具体请参见 厂商描述1厂商描述2

  • OPPO:将推送消息分为私信消息类和公信消息类,推送效果和策略不同。其中私信消息是针对用户有一定关注度,且希望能及时接收的信息,私信通道权益需要邮件申请。公信通道推送数量有限制。 具体请参见 厂商描述1厂商描述2

  • 小米:将推送消息分为重要消息类和普通消息类,推送效果和策略不同。其中重要消息类型仅允许即时通讯消息、个人关注动态提醒、个人事项提醒、个人订单状态变化、个人财务提醒、个人状态变化、个人资源变化、个人设备提醒这8类消息推送,可以在厂商控制台申请开通。普通消息类型推送数量有限制。 具体请参见 厂商描述1厂商描述2

  • 魅族:推送消息数量有限制,具体可参见 魅族平台合约

  • FCM:推送上行消息频率有限制。 具体请参见 厂商描述

收不到离线推送怎么排查?

1、OPPO 手机

OPPO 手机收不到推送一般有以下几种情况:

  • 按照 OPPO 推送官网要求,在 Android 8.0 及以上系统版本的 OPPO 手机上必须配置 ChannelID,否则推送消息无法展示。配置方法可以参见 OPPO 推送配置
  • 在消息中 透传的离线推送的自定义内容 不是 JSON 格式,会导致 OPPO 手机收不到推送。
  • OPPO 安装应用通知栏显示默认关闭,需要确认下开关状态。

2、发送消息为自定义消息

自定义消息的离线推送和普通消息不太一样,自定义消息的内容我们无法解析,不能确定推送的内容,所以默认不推送,如果您有推送需求,需要您在sendMessage的时候设置offlinePushInfodesc字段,推送的时候会默认展示 desc 信息。

3、设备通知栏设置影响

离线推送的直观表现就是通知栏提示,所以同其他通知一样受设备通知相关设置的影响,以华为为例:

  • “手机设置-通知-锁屏通知-隐藏或者不显示通知”,会影响锁屏状态下离线推送通知显示。
  • “手机设置-通知-更多通知设置-状态栏显示通知图标”,会影响状态栏下离线推送通知的图标显示。
  • “手机设置-通知-应用的通知管理-允许通知”,打开关闭会直接影响离线推送通知显示。
  • “手机设置-通知-应用的通知管理-通知铃声” 和 “手机设置-通知-应用的通知管理-静默通知”,会影响离线推送通知铃音的效果。

4、按照流程接入完成,还是收不到离线推送

  • 首先在 IM 控制台通过 离线测试工具 自测下是否可以正常推送。 推送异常情况,设备状态异常,需要检查下 IM 控制台配置各项参数是否正确,再者需要检查下代码初始化注册逻辑,包括厂商推送服务注册和 IM 设置离线推送配置相关逻辑是否正确设置。 推送异常情况,设备状态正常,需要看下是否需要正确填写 channel ID 或者后台服务是否正常。
  • 离线推送依赖厂商能力,一些简单的字符可能会被厂商过滤不能透传推送。如 OPPO 则对 ext 字段限制为 JSON 格式。
  • 如果离线推送消息出现推送不及时或者偶尔收不到情况,需要看下厂商的推送限制。

在线推送-在本地创建新消息通知

本文以上部分介绍了,如何使用本插件,结合腾讯云IM后端的推送服务,实现通过厂商通道的离线推送。

但是,在某些情况下,厂商离线推送并不适用。如,您的目标客户端机型非我们兼容的厂商,使用华强北定制的Android设备等。

此时,您只得通过在线监听收到新消息回调,在客户端上,手动触发创建通知。这仅适用于,应用未被kill掉,还处于前后台状态,能正常与IM服务端通信。

为此种情况,本插件在0.3版本中,新增两个本地创建消息的方法,displayNotification 自定义通知,及 displayDefaultNotificationForMessage 根据消息生成默认通知,您可按需使用。

接入前准备

在您的项目中安装IM Flutter 推送插件:

flutter pub add tim_ui_kit_push_plugin

并根据该指南,在插件市场,启用推送插件。

Android

  1. 确保 @mipmap/ic_launcher 存在且为您的应用Icon。完整路径:android/app/src/main/res/mipmap/ic_launcher.png

20220713155110

如果不存在,可手动将您的应用Icon复制进去,或通过Android Studio自动创建不同分辨率版本(mipmap 目录右键,New => Image Asset)。

20220713155548

  1. 打开 android/app/src/main/AndroidManifest.xml 文件,在您应用的主activity中,添加如下代码。
<activity
    android:showWhenLocked="true"
    android:turnScreenOn="true">

iOS

如果您已经配置iOS端离线推送,可忽略本部分。若无,请在 ios/Runner/AppDelegate.swiftios/Runner/AppDelegate.m文件中, didFinishLaunchingWithOptions 函数内,添加如下代码。可参考我们的DEMO。

Objective-C:

if (@available(iOS 10.0, *)) {
  [UNUserNotificationCenter currentNotificationCenter].delegate = (id<UNUserNotificationCenterDelegate>) self;
}

Swift:

if #available(iOS 10.0, *) {
  UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}

初始化插件

请在IM SDK 初始化完成后,初始化本Push插件。实例化一个 cPush 插件类,供后续调用。

final TimUiKitPushPlugin cPush = TimUiKitPushPlugin();

cPush.init(
  // 此处绑定点击通知的跳转函数,下文会介绍
  pushClickAction: onClickNotification,
);

监听新消息回调触发通知

监听 V2TimAdvancedMsgListener

如果您已经挂载监听 V2TimAdvancedMsgListener ,可忽略本部分;若无,请在IM login后,挂载监听。

代码如下:

final advancedMsgListener = V2TimAdvancedMsgListener(
  onRecvNewMessage: (V2TimMessage newMsg) {
    // 这里完成监听回调触发事件
    // 下一步创建的方法,请在这里调用
  }, 
});

TencentImSDKPlugin.v2TIMManager
        .getMessageManager()
        .addAdvancedMsgListener(listener: advancedMsgListener);

触发本地消息通知

请从我们提供的两个API中,displayNotification 自定义通知,及 displayDefaultNotificationForMessage 根据消息生成默认通知,选一个合适的 API。

对于Android端,这两个API均需传入 channelIDchannelName。若还未创建 Android Push Channel ,请使用插件 createNotificationChannel API创建。

cPush.createNotificationChannel(
        channelId: "new_message",
        channelName: "消息推送",
        channelDescription: "推送新聊天消息");
displayNotification

本API需要您提供 title, body, 及 ext 用于点击跳转信息,三个参数。您可以根据需要自行解析收到的 V2TimMessage,生成这三个字段。

20220713163737

为便于跳转,此处ext的生成规则可查看 displayDefaultNotificationForMessage 的代码。

cPush.displayNotification(
  channelID: "new_message",
  channelName: "消息推送",
  title: "",
  body: "",
  ext: ""
);
displayDefaultNotificationForMessage

为了方便,推荐您使用此API,自动根据 V2TimMessage,生成通知。

您只需传入一个 V2TimMessage 即可。

cPush.displayDefaultNotificationForMessage(
        message: message, channelID: "new_message", channelName: "消息推送");

点击通知跳转

本步骤与上文离线推送的步骤6点击回调一致,均为在ext中,读取需要跳转的conversation,并导航过去。

如果您在上一步使用 displayDefaultNotificationForMessage,或在 displayNotification 中使用与default相同的ext生成函数,此时的ext结构为:"conversationID": "对应的conversation"

此时,填上初始化时,为pushClickAction埋的坑。

初始化时,注册该回调方法,可拿到含推送本体及ext信息在内的Map。

说明:

在后台跳转情况下,此时Flutter首页可能已经unmounted,无法为跳转提供context,因此建议启动时缓存一个context,保证跳转成功。

建议跳转成功后,清除通知栏中其他通知消息,避免太多IM消息堆积在通知栏中。调用插件中clearAllNotification()方法即可。

BuildContext? _cachedContext;
final TimUiKitPushPlugin cPush = TimUiKitPushPlugin(
      isUseGoogleFCM: false,
    );
// 仅限TUIKit
final TIMUIKitChatController _timuiKitChatController =
  TIMUIKitChatController();

@override
void initState() {
  super.initState();
  _cachedContext = context;
}

void onClickNotification(Map<String, dynamic> msg) async {
    String ext = msg['ext'] ?? "";
    Map<String, dynamic> extMsp = jsonDecode(ext);
    String convId = extMsp["conversationID"] ?? "";

    // 【TUIKit】若当前的会话与要跳转至的会话一致,则不跳转
    final currentConvID = _timuiKitChatController.getCurrentConversation();
    if(currentConvID == convId.split("_")[1]){
      return;
    }

    final targetConversationRes = await TencentImSDKPlugin.v2TIMManager
        .getConversationManager()
        .getConversation(conversationID: convId);

    V2TimConversation? targetConversation = targetConversationRes.data;

    if(targetConversation != null){
      cPush.clearAllNotification();
      Navigator.push(
          _cachedContext ?? context,
          MaterialPageRoute(
            builder: (context) => Chat(
              selectedConversation: targetConversation,
            ),
          ));
    }
  }

如果您自定义了 ext 结构,则需自实现点击跳转函数。

此时,您已完成在线推送的接入。测试通过后,你可以在 onRecvNewMessage 内定义,触发推送通知的时机及场景。

联系我们

如果您在接入使用过程中有任何疑问,请加入QQ群:788910197 咨询。

results matching ""

    No results matching ""