IM terminal users need to obtain the latest messages at any time. However, considering the limited performance and battery SOC of mobile devices, IM recommends you use the system-grade push channels provided by vendors for message notifications when the app is running in the background to avoid excessive resource consumption caused by maintaining a persistent connection. Compared with third-party push channels, system-grade push channels provide more stable system-grade persistent connections, enabling users to receive push messages at any time and greatly reducing resource consumption.
Note:
- If you want users to receive IM message notifications when, without proactive logout, the app is switched to the background, the mobile phone screen is locked, or the app process is killed by a user, you can enable the IM offline push.
- If the
logout
API is called to log out proactively or you are forced to log out due to multi-device login, you cannot receive offline push messages even though IM offline push is enabled.
The TUIKit demo has integrated the offline push feature as described below. You may see the code links in the documentation.
The offline push feature depends on vendors' original channels. You need to register your app with each vendor's push platform to obtain parameters such as AppID
and AppKey
. Currently, mobile phone vendors supported are Google FCM, Mi, Huawei, OPPO, vivo, and Meizu.
You need to log in to the IM console to add the push certificates of required vendors, and configure parameters obtained in Step 1 such as AppId
, AppKey
, and AppSecret
to the push certificates in the console. Take Google as an example.
Vendor Push Platform | Configuring in the IM console |
---|---|
![]() |
![]() |
An offline push message received will be displayed in the notification bar as shown in the figure below. You can click the notification bar to open the app and go to the redirected-to page configured. Follow the steps below to configure the activities to be redirected to upon notification message clicking.
Configuring in the console
The redirected-to page configuration varies by vendor as follows:
Vendor | Action after Click | Specified In-app Page |
---|---|---|
Mi | Open the specified in-app page | intent://`your hostname`/`your path`#Intent;scheme=`your protocol, that is, the scheme you defined`;launchFlags=0x4000000;component=`complete class name of the page to which your app is to be redirected`;end TUIKit demo configuration: intent://com.tencent.qcloud/detail#Intent;scheme=pushscheme;launchFlags=0x4000000;component=com.tencent.qcloud.tim.tuikit/com.tencent.qcloud.tim.demo.main.MainActivity;end |
Huawei | Open the specified in-app page | intent://`your hostname`/`your path`#Intent;scheme=`your protocol, that is, the scheme you defined`;launchFlags=0x4000000;component=`complete class name of the page to which your app is to be redirected`;end TUIKit demo configuration: intent://com.tencent.qcloud/detail#Intent;scheme=pushscheme;launchFlags=0x4000000;component=com.tencent.qcloud.tim.tuikit/com.tencent.qcloud.tim.demo.main.MainActivity;end |
Meizu | Open the specified in-app page | Complete class name of the page to which your app is to be redirected TUIKit demo configuration: com.tencent.qcloud.tim.demo.main.MainActivity |
OPPO | Open the specified in-app page | Complete class name of the page to which your app is to be redirected TUIKit demo configuration: activity: com.tencent.qcloud.tim.demo.main.MainActivity |
vivo | Open the specified in-app page | intent://`your hostname`/`your path`#Intent;scheme=`your protocol, that is, the scheme you defined`;launchFlags=0x4000000;component=`complete class name of the page to which your app is to be redirected`;end TUIKit demo configuration: intent://com.tencent.qcloud/detail#Intent;scheme=pushscheme;launchFlags=0x4000000;component=com.tencent.qcloud.tim.tuikit/com.tencent.qcloud.tim.demo.main.MainActivity;end |
Google FCM | No need to configure | Redirect to the Launcher page of the app by default |
Manifest file configuration
In AndroidManifest.xml, configure the redirected-to page parameters. Please note that this configuration must be consistent with the Action after Click configured in the corresponding certificate in the IM console.
<!-- The redirected-to page configured in the TUIKit demo is `MainActivity`, so you need to fill in `com.tencent.qcloud.tim.demo.main.MainActivity` here. After integration with your app, you need to replace the class name with the complete class name of the page to which your app is to be redirected.-->
<activity
android:name="complete class name of the page to which your app is to be redirected"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize|stateHidden">
<!-- Open the in-app page for offline push -->
<intent-filter>
<action android:name="android.intent.a, and configuration for Google FCM: pushscheme://com.tencent.qcloud/detail -->
<data
android:host="your hostname"
android:path="your path"
android:scheme="your protocol, that is, the scheme you defined" />
</intent-filter>
</activity>
Push certificate ID:
Parameters to be filled in:
public class PrivateConstants {
// Certificate ID generated after uploading a third-party push certificate in the IM console
public static final long GOOGLE_FCM_PUSH_BUZID = 15518;
}
<!-- Note: The `applicationId` of the TUIKit demo is `com.tencent.qcloud.tim.tuikit`. You need to replace xxxx here with the `applicationId` of your app. -->
<!-- ********Google cloud messaging starts******** -->
<service
android:name="xxxx.GoogleFCMMsgService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- ********Google cloud messaging ends******** -->
Download the configuration file and place it under the root directory of the project.
Add the following configuration under "buildscript -> dependencies" of the project-level build.gradle
file.
dependencies {
...
classpath 'com.google.gms:google-services:4.2.0'
}
Add the following configuration in the app-level build.gradle
file.
apply plugin: 'com.google.gms.google-services'
Click Sync Now of the project.
dependencies {
......
// Main package
implementation 'com.tencent.tpns:tpns:1.3.1.1-release'
// Google FCM
implementation "com.tencent.tpns:fcm:1.3.1.1-release"
// Google cloud messaging
implementation ('com.google.firebase:firebase-messaging:19.0.1')
// Mi
implementation "com.tencent.tpns:xiaomi:1.3.1.1-release"
// Meizu
implementation "com.tencent.tpns:meizu:1.3.1.1-release"
// OPPO
implementation "com.tencent.tpns:oppo:1.3.1.1-release"
// vivo
implementation "com.tencent.tpns:vivo:1.3.2.0-release"
// Huawei
implementation 'com.tencent.tpns:huawei:1.3.1.1-release'
implementation 'com.huawei.hms:push:5.0.2.300'
}
Adding push classes
Include the vendor push classes. The methods are specific to vendors. See and copy the following files in TUIOfflinePush code links:
Registering push services
To meet compliance requirements, you need to initialize and register the vendor push service after the user agrees to the privacy policy and logs in successfully, store the token (obtained after successful registration) in the registration result callback, and call the setOfflinePushConfig API to report the push token to the backend. For some vendors, the token can also be returned via API calls after registration. See the following code for details.
public void init() {
...
if (BrandUtil.isBrandXiaoMi()) {
// Mi offline push
MiPushClient.registerPush(this, PrivateConstants.XM_PUSH_APPID, PrivateConstants.XM_PUSH_APPKEY);
} else if (BrandUtil.isBrandHuawei()) {
// For Huawei offline push, set whether to receive the call example of the push notification bar message
HmsMessaging.getInstance(this).turnOnPush().addOnCompleteListener(new com.huawei.hmf.tasks.OnCompleteListener<Void>() {
@Override
public void onComplete(com.huawei.hmf.tasks.Task<Void> task) {
if (task.isSuccessful()) {
DemoLog.i(TAG, "huawei turnOnPush Complete");
} else {
DemoLog.e(TAG, "huawei turnOnPush failed: ret=" + task.getException().getMessage());
}
}
});
new Thread(){
@Override
public void run() {
try{
// read from agconnect-services.json
String appId = AGConnectServicesConfig.fromContext(MainActivity.this).getString("client/app_id");
String token = HmsInstanceId.getInstance(MainActivity.this).getToken(appId, "HCM");
DemoLog.i(TAG, "huawei get token:" + token);
if(!TextUtils.isEmpty(token)) {
// Call the `setOfflinePushConfig` API of the IM SDK to report this token
String token = (String) o;
}
} catch (ApiException e) {
DemoLog.e(TAG, "huawei get token failed, " + e);
}
}
}.start();
} else if (MzSystemUtils.isBrandMeizu(this)) {
// Meizu offline push
PushManager.register(this, PrivateConstants.MZ_PUSH_APPID, PrivateConstants.MZ_PUSH_APPKEY);
} else if (BrandUtil.isBrandVivo()) {
// vivo offline push
PushClient.getInstance(getApplicationContext()).initialize();
DemoLog.i(TAG, "vivo support push: " + PushClient.getInstance(getApplicationContext()).isSupport());
PushClient.getInstance(getApplicationContext()).turnOnPush(new IPushActionListener() {
@Override
public void onStateChanged(int state) {
if (state == 0) {
String regId = PushClient.getInstance(getApplicationContext()).getRegId();
DemoLog.i(TAG, "vivopush open vivo push success regId = " + regId);
// // Call the `setOfflinePushConfig` API of the IM SDK to report this token
String token = (String) o;
} else {
// According to the vivo documentation, state = 101 means this particular vivo device or system version does not support vivo Push. See https://dev.vivo.com.cn/documentCenter/doc/156 for details.
DemoLog.i(TAG, "vivopush open vivo push fail state = " + state);
}
}
});
} else if (HeytapPushManager.isSupportPush()) {
// OPPO offline push
OPPOPushImpl oppo = new OPPOPushImpl();
oppo.createNotificationChannel(this);
// According to the OPPO documentation, the app must call the init(...) API before proceeding to subsequent operations.
HeytapPushManager.init(this, false);
HeytapPushManager.register(this, PrivateConstants.OPPO_PUSH_APPKEY, PrivateConstants.OPPO_PUSH_APPSECRET, oppo);
} else if (BrandUtil.isGoogleServiceSupport()) {
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new com.google.android.gms.tasks.OnCompleteListener<InstanceIdResult>() {
@Override
public void onComplete(Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
DemoLog.w(TAG, "getInstanceId failed exception = " + task.getException());
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
DemoLog.i(TAG, "google fcm getToken = " + token);
// Call the `setOfflinePushConfig` API of the IM SDK to report this token
String token = (String) o;
}
});
}
}
Take Google FCM as an example. Store the token (obtained after successful registration) in the registration result callback, and call the setOfflinePushConfig API to report the token to the backend.
public class GoogleFCMMsgService extends FirebaseMessagingService {
...
@Override
public void onNewToken(String token) {
DemoLog.i(TAG, "onNewToken token=" + token);
// Call the `setOfflinePushConfig` API of the IM SDK to report this token
String pushToken = token;
}
...
}
businessID
as the certificate ID of the vendor and isTPNSToken
as false
and report the token obtained after successful registration of the vendor push service. Note: If you use the offline push service of TPNS, set isTPNSToken
as true
and report the token obtained after successful registration of the TPNS push service. Then, TPNS will provide the push service.V2TIMOfflinePushConfig v2TIMOfflinePushConfig = null;
// Set `businessID` as the certificate ID of the vendor and `isTPNSToken` as `false`, and report the token obtained after registration of the vendor push service.
v2TIMOfflinePushConfig = new V2TIMOfflinePushConfig(0, token, true);
V2TIMManager.getOfflinePushManager().setOfflinePushConfig(v2TIMOfflinePushConfig, new V2TIMCallback() {
@Override
public void onError(int code, String desc) {
DemoLog.d(TAG, "setOfflinePushToken err code = " + code);
}
@Override
public void onSuccess() {
DemoLog.d(TAG, "setOfflinePushToken success");
}
});
If a message newly received needs to be displayed in the phone's notification bar when your app is switched to the background, call the doBackground() API of the IM SDK to sync the app status to the IM backend. When your app is switched back to the foreground, call the doForeground() API of the IM SDK to sync the app status to the IM backend. For the scheme for listening for the app switching between foreground and background, see relevant logic in the initActivityLifecycle() class of TUIOfflinePushService.
// When the app is switched to the background
V2TIMManager.getOfflinePushManager().doBackground(totalCount, new V2TIMCallback() {
@Override
public void onError(int code, String desc) {
DemoLog.e(TAG, "doBackground err = " + code + ", desc = " + desc);
}
@Override
public void onSuccess() {
DemoLog.i(TAG, "doBackground success");
}
});
// When the app is switched back to the foreground
V2TIMManager.getOfflinePushManager().doForeground(new V2TIMCallback() {
@Override
public void onError(int code, String desc) {
DemoLog.e(TAG, "doForeground err = " + code + ", desc = " + desc);
}
@Override
public void onSuccess() {
DemoLog.i(TAG, "doForeground success");
}
});
When you call sendMessage to send messages, you can use V2TIMOfflinePushInfo to set offline push parameters. For more information, see the sendMessage() method in ChatProvider.
OfflineMessageContainerBean containerBean = new OfflineMessageContainerBean();
OfflineMessageBean entity = new OfflineMessageBean();
entity.content = message.getExtra().toString();
entity.sender = message.getFromUser();
entity.nickname = chatInfo.getChatName();
entity.faceUrl = TUIChatConfigs.getConfigs().getGeneralConfig().getUserFaceUrl();
containerBean.entity = entity;
V2TIMOfflinePushInfo v2TIMOfflinePushInfo = new V2TIMOfflinePushInfo();
v2TIMOfflinePushInfo.setExt(new Gson().toJson(containerBean).getBytes());
// For OPPO, you must set the `ChannelID` to receive push messages. The `ChannelID` must be identical with that in the console.
v2TIMOfflinePushInfo.setAndroidOPPOChannelID("tuikit");
final V2TIMMessage v2TIMMessage = message.getTimMessage();
String msgID = V2TIMManager.getMessageManager().sendMessage(v2TIMMessage, isGroup ? null : userID, isGroup ? groupID : null,
V2TIMMessage.V2TIM_PRIORITY_DEFAULT, false, v2TIMOfflinePushInfo, new V2TIMSendCallback<V2TIMMessage>() {
@Override
public void onProgress(int progress) {
}
@Override
public void onError(int code, String desc) {
TUIChatUtils.callbackOnError(callBack, TAG, code, desc);
}
@Override
public void onSuccess(V2TIMMessage v2TIMMessage) {
TUIChatLog.v(TAG, "sendMessage onSuccess:" + v2TIMMessage.getMsgID());
message.setMsgTime(v2TIMMessage.getTimestamp());
TUIChatUtils.callbackOnSuccess(callBack, message);
}
});
When a phone receives an offline push message, the message is displayed in the system notification bar. When you click the message in the notification bar, the system automatically redirects to the page configured in Step 4. On this page, you can call getIntent().getExtras()
to get the offline push parameters configured in Step 6. For sample code, see the handleOfflinePush() method of the TUIKit demo.
private void handleOfflinePush() {
// Determine whether to log in to IM again based on the login status
// 1. If the login status is V2TIMManager.V2TIM_STATUS_LOGOUT, redirect to the login page and log in to IM again.
if (V2TIMManager.getInstance().getLoginStatus() == V2TIMManager.V2TIM_STATUS_LOGOUT) {
Intent intent = new Intent(MainActivity.this, SplashActivity.class);
if (getIntent() != null) {
intent.putExtras(getIntent());
}
startActivity(intent);
finish();
return;
}
// 2. If the login status is not V2TIMManager.V2TIM_STATUS_LOGOUT, the app runs in the background, and the offline push parameters can be parsed directly.
final OfflineMessageBean bean = OfflineMessageDispatcher.parseOfflineMessage(getIntent());
if (bean != null) {
setIntent(null);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (manager != null) {
manager.cancelAll();
}
if (bean.action == OfflineMessageBean.REDIRECT_ACTION_CHAT) {
if (TextUtils.isEmpty(bean.sender)) {
return;
}
TUIUtils.startChat(bean.sender, bean.nickname, bean.chatType);
}
}
}
Note:
- For Google FCM, clicking the message in the notification bar will redirect to the Launcher page of the app by default. On this page, you can call
getIntent().getExtras()
to get the offline push parameters configured in Step 6, and then parse messages and customize the redirection.
Upon completion of the above configurations, when your app is switched to the background or the process is killed, the messages will be pushed offline and displayed in the notification bar. You can click the message in the notification bar to redirect to the specified app page.
Join a Tencent Cloud IM group for:
Telegram group: join
Disacord group: join
SDK v6.1.2155 or a later version supports customizing alert tones on devices of Huawei, Mi, FCM and APNS. See the setAndroidSound() and setIOSSound() APIs of V2TIMOfflinePushInfo for specific methods.
This generally occurs for the following reasons:
The offline push for custom messages is different from that for ordinary messages. As we cannot parse the content of custom messages and determine the push content, custom messages are not pushed offline by default. If you need offline push for custom messages, set the desc field in offlinePushInfo when you call sendMessage, and the desc
information will be displayed by default in the push message.
The offline push message can be intuitively expressed by the notification bar alert, so, just as other notifications, it is subject to the notification settings of the device. Take a Huawei device as an example.
Page redirection is implemented as follows: The backend delivers the redirection modes and page parameters that you configure for various vendors in the console to vendor servers based on vendor API rules. When you click the notification bar for offline push messages, the system opens and redirects to the corresponding page. Opening of the corresponding page also depends on the manifest file. Only when the configuration in the manifest file is consistent with that in the console, the corresponding page can be opened and redirected properly.
Was this page helpful?