Android SDK接入(4.x)

更新时间:
复制 MD 格式

本文介绍移动推送 Android SDK 4.x 的接入方法,包括添加 SDK、初始化与注册、配置消息接收、接入厂商通道及验证接入结果。

前言

  • 推荐使用 Gradle 管理依赖的 Android Studio 项目。

  • 最低支持 Android 6.0(API 23)。

  • 本文适用于移动推送 Android SDK 4.x。

说明

移动推送 SDK 包含自有长连接通道和厂商通道。本文以 4.0.0 为例介绍接入步骤;接入厂商通道可提高应用离线时的通知到达率。

已有 3.x 项目升级请参见Android SDK 4.x升级指南;仍使用 3.x 的项目请参见Android SDK接入(3.x)。

准备工作

在 EMAS 控制台创建或选择 Android 应用,开通移动推送,并记录该应用的 AppKey、AppSecret。实际 APK 的 applicationId(包名)应与控制台登记的信息一致。具体操作请参见创建应用。

环境项

最低支持版本

Android 版本

Android 6.0(API 23)及以上。

compileSdk

34 及以上。

Android Gradle Plugin(AGP)

8.1.1 及以上。

Gradle

8.0 及以上。

构建 JDK

17 及以上。

Kotlin(仅 Kotlin 项目)

1.8.0 及以上。

构建工具按 compileSdk 34 的官方最低支持组合列出;使用更高版本 AGP 时,需匹配其要求的 Gradle 和构建 JDK 版本。

第一步:将 SDK 添加到您的应用

推荐使用 Maven 依赖方式集成 SDK。以下为 Groovy 示例,在项目已有的仓库配置和 dependencies 中添加相应内容即可。

1 Maven 依赖方式

1.1 配置 Maven 仓库

如果项目使用 dependencyResolutionManagement 管理依赖,在项目级 settings.gradle 的 repositories 中添加仓库。

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/' }
    }
}

如果项目使用 allprojects 管理依赖,在项目级 build.gradle 的 repositories 中添加仓库。

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/' }
    }
}

两种仓库配置方式按项目现有结构选择一种。接入华为、荣耀等厂商通道时,按对应文档补充厂商 Maven 仓库。

1.2 添加 SDK 依赖

在应用模块的 build.gradle 文件中,在 dependencies 中添加 SDK 依赖。

dependencies {
    implementation 'com.aliyun.ams:alicloud-android-push:4.0.0'
}
说明

请使用固定版本号集成,保留 Maven 传递依赖,并统一通过 Maven 管理 SDK。4.+ 等动态版本号、@aar 和 transitive = false 会改变版本或依赖解析结果;已通过 Maven 引入的 SDK 无需再放入 libs。

4.x 的完整依赖由 Maven 自动引入,单独复制主 SDK 的 AAR 文件不能替代完整接入。已有项目升级时,请按升级指南处理旧版依赖。

第二步:配置使用 SDK

1 配置 AppKey 和 AppSecret

支持代码配置和 AndroidManifest 文件配置两种方式,选择其中一种即可。AppKey 和 AppSecret 必须来自 EMAS 控制台的同一个 Android 应用。

1.1 代码配置方式

在 Application 中构建 PushInitConfig,并在下一节初始化时传入该配置。

PushInitConfig pushInitConfig = new PushInitConfig.Builder()
        .application(this) // 当前 Application 实例
        .appKey("YOUR_APP_KEY")
        .appSecret("YOUR_APP_SECRET")
        .build();
val pushInitConfig = PushInitConfig.Builder()
    .application(this) // 当前 Application 实例
    .appKey("YOUR_APP_KEY")
    .appSecret("YOUR_APP_SECRET")
    .build()

1.2 AndroidManifest 文件配置方式

在 AndroidManifest.xml 的 application 节点内配置应用参数。

<application ...>
    <meta-data android:name="com.alibaba.app.appkey" android:value="YOUR_APP_KEY" />
    <meta-data android:name="com.alibaba.app.appsecret" android:value="YOUR_APP_SECRET" />
</application>

2 SDK 初始化与设备注册

2.1 初始化 SDK

在自定义 Application 的 onCreate() 主线程中调用 PushServiceFactory.init。应用已有 Application 时,将初始化逻辑加入现有类,并在所有需要接收推送的进程中执行。

使用代码配置时,将上一步创建的 PushInitConfig 传入 init。

PushServiceFactory.init(pushInitConfig);
PushServiceFactory.init(pushInitConfig)

使用 AndroidManifest 配置时,通过 Context 初始化。

PushServiceFactory.init(getApplicationContext());
PushServiceFactory.init(applicationContext)

在 AndroidManifest.xml 中声明自定义 Application;下面的 PushApplication 请替换为实际类名。

<application
    android:name=".PushApplication"
    ...>
    <!-- 保留应用原有配置。 -->
</application>

2.2 注册设备

设备注册会启动推送连接,可根据业务需要在满足应用隐私告知和授权要求后执行。调用 register 前须完成上一步初始化。未自定义推送服务进程时,在应用主进程中执行以下注册逻辑;已自定义推送服务进程时,在对应进程中执行。

final CloudPushService pushService = PushServiceFactory.getCloudPushService();
// 接入调试时开启日志和配置检查。
pushService.setLogLevel(CloudPushService.LOG_DEBUG);
pushService.setDebug(true);
pushService.register(this, new CommonCallback() {
    @Override
    public void onSuccess(String response) {
        String deviceId = pushService.getDeviceId();
        Log.d("PushDemo", "register success, deviceId=" + deviceId);
        // 保存 deviceId,用于控制台或服务端定向推送。
    }
    @Override
    public void onFailed(String errorCode, String errorMessage) {
        Log.e("PushDemo", "register failed: " + errorCode + ", " + errorMessage);
    }
});
val pushService = PushServiceFactory.getCloudPushService()
// 接入调试时开启日志和配置检查。
pushService.setLogLevel(CloudPushService.LOG_DEBUG)
pushService.setDebug(true)
pushService.register(this, object : CommonCallback {
    override fun onSuccess(response: String?) {
        val deviceId = pushService.deviceId
        Log.d("PushDemo", "register success, deviceId=$deviceId")
        // 保存 deviceId,用于控制台或服务端定向推送。
    }
    override fun onFailed(errorCode: String?, errorMessage: String?) {
        Log.e("PushDemo", "register failed: $errorCode, $errorMessage")
    }
})

使用 register(Context, CommonCallback) 注册设备,并提供非空回调。收到 onSuccess 后,通过 getDeviceId() 获取设备标识,用于定向推送。

日志等级应在 init 之后、register 和厂商通道初始化之前设置。setDebug(true) 用于接入配置检查,不等同于开启 DEBUG 日志。上线前将日志等级调整为 LOG_ERROR,并关闭 setDebug。

更多初始化、设备标识、日志、接收方式设置请参见基础配置接口(4.x)。

3 配置通知通道和通知权限

3.1 创建通知通道

Android 8.0 及以上需要创建 NotificationChannel。以下示例在 Application 中创建通知通道;服务端推送时,AndroidNotificationChannel 参数须与客户端的 channelId 一致。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    NotificationManager manager = getSystemService(NotificationManager.class);
    String channelId = "push_default";
    NotificationChannel channel = new NotificationChannel(
            channelId, "消息通知", NotificationManager.IMPORTANCE_DEFAULT);
    manager.createNotificationChannel(channel);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val manager = getSystemService(NotificationManager::class.java)
    val channelId = "push_default"
    val channel = NotificationChannel(
        channelId, "消息通知", NotificationManager.IMPORTANCE_DEFAULT
    )
    manager.createNotificationChannel(channel)
}

3.2 申请通知权限

Android 13 及以上需要申请 POST_NOTIFICATIONS 运行时权限。先在 AndroidManifest.xml 中声明权限,再在 Activity 的合适时机请求并处理授权结果。以下示例适用于 targetSdk 33 及以上的应用。

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
package com.example.pushdemo;
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
public class MainActivity extends Activity {
    private static final int REQUEST_NOTIFICATIONS = 1001;
    // 在 Activity 中于用户主动开启推送等合适时机请求通知权限。
    public void requestPushNotificationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
                && checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS)
                   != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(
                    new String[]{Manifest.permission.POST_NOTIFICATIONS},
                    REQUEST_NOTIFICATIONS);
        }
    }
    @Override
    public void onRequestPermissionsResult(
            int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_NOTIFICATIONS) {
            boolean granted = grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED;
            Log.d("PushDemo", "notification permission granted=" + granted);
            // 被拒绝时,按应用的交互设计说明通知能力受到限制。
        }
    }
}
package com.example.pushdemo
import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
class MainActivity : Activity() {
    private val requestNotifications = 1001
    fun requestPushNotificationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
            checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) !=
                PackageManager.PERMISSION_GRANTED
        ) {
            requestPermissions(
                arrayOf(Manifest.permission.POST_NOTIFICATIONS), requestNotifications
            )
        }
    }
    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<out String>, grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == requestNotifications) {
            val granted = grantResults.isNotEmpty() &&
                grantResults[0] == PackageManager.PERMISSION_GRANTED
            Log.d("PushDemo", "notification permission granted=$granted")
        }
    }
}

通知栏展示需要应用已获得通知权限,且应用通知开关和对应通知渠道均已开启。请分别验证权限状态、通知展示和透传消息回调。

4 消息接收配置

重要

Android SDK 4.x 中,MessageReceiver 和 AliyunMessageIntentService 的业务回调范围如下:

  • ACCS 自有通道和 FCM:支持消息接收(onMessage)、通知到达(onNotification)、通知点击(onNotificationOpened)、通知清除(onNotificationRemoved)等完整业务回调。

  • 除 FCM 外的其他 Android 厂商通道:仅支持 onNotificationOpened(通知点击),不触发消息接收、通知到达、通知清除等其他回调。

创建 MyMessageReceiver,继承 MessageReceiver 并覆盖所需回调。示例中的 PushDemo 是应用自定义日志 TAG,用于观察回调;SDK 日志使用 EMAS_PUSH TAG。

package com.example.pushdemo;
import android.content.Context;
import android.util.Log;
import com.alibaba.sdk.android.push.MessageReceiver;
import com.alibaba.sdk.android.push.notification.CPushMessage;
import java.util.Map;
public class MyMessageReceiver extends MessageReceiver {
    private static final String TAG = "PushDemo";
    @Override
    public void onMessage(Context context, CPushMessage message) {
        Log.d(TAG, "message received");
        // 透传消息由应用处理,SDK 不会自动显示为通知。
    }
    @Override
    public void onNotification(Context context, String title, String summary,
                               Map<String, String> extraMap) {
        Log.d(TAG, "notification received");
    }
    @Override
    public void onNotificationOpened(Context context, String title, String summary,
                                     Map<String, String> extraMap) {
        Log.d(TAG, "notification opened");
        // 按业务需要处理通知点击,可从 extraMap 读取发送时设置的扩展参数。
    }
    @Override
    public void onNotificationRemoved(Context context, String messageId) {
        Log.d(TAG, "notification removed");
    }
}
package com.example.pushdemo
import android.content.Context
import android.util.Log
import com.alibaba.sdk.android.push.MessageReceiver
import com.alibaba.sdk.android.push.notification.CPushMessage
class MyMessageReceiver : MessageReceiver() {
    override fun onMessage(context: Context, message: CPushMessage) {
        Log.d("PushDemo", "message received")
        // 透传消息由应用处理。
    }
    override fun onNotification(
        context: Context, title: String?, summary: String?, extraMap: Map<String, String>?
    ) {
        Log.d("PushDemo", "notification received")
    }
    override fun onNotificationOpened(
        context: Context, title: String?, summary: String?, extraMap: Map<String, String>?
    ) {
        Log.d("PushDemo", "notification opened")
        // 按业务需要处理通知点击,可从 extraMap 读取发送时设置的扩展参数。
    }
    override fun onNotificationRemoved(context: Context, messageId: String?) {
        Log.d("PushDemo", "notification removed")
    }
}

在 AndroidManifest.xml 的 application 节点中声明 Receiver。SDK 自身组件和基础权限由 AAR 的 Manifest 自动合并。

<application ...>
    <receiver
        android:name=".MyMessageReceiver"
        android:exported="false">
        <intent-filter>
            <action android:name="com.alibaba.sdk.android.push.RECEIVE" />
            <action android:name="com.alibaba.push2.action.NOTIFICATION_OPENED" />
            <action android:name="com.alibaba.push2.action.NOTIFICATION_REMOVED" />
        </intent-filter>
    </receiver>
</application>

回调的线程和时机、通知显示控制,以及 AliyunMessageIntentService 接收方式请参见消息与通知处理接口(4.x)。接收回调中应避免耗时操作。

5 NDK 与混淆配置

5.1 NDK 配置

SDK 的传递依赖包含 libels.so 和 libjnidispatch.so,支持 arm64-v8a、armeabi-v7a、x86 和 x86_64。设置 abiFilters 或拆分 APK/AAB 时,须保留目标 ABI 下的上述库文件。

5.2 混淆与打包验证

4.0.0 主 SDK 及相关传递依赖已携带 consumer ProGuard 规则,通过 Maven 集成时自动合并。应用自身及厂商 SDK 如有额外混淆要求,请按对应文档配置。

使用开启混淆的 release 包验证初始化与收消息。遇到类或 native 库冲突时,可先通过依赖树定位重复依赖;同时检查最终 APK 是否包含目标 ABI 的库文件。

./gradlew :app:dependencies --configuration releaseRuntimeClasspath
./gradlew :app:assembleRelease

第三步:接入厂商通道

建议接入厂商通道,提高应用离线时的通知到达率。按所选通道完成厂商平台应用注册、EMAS 控制台参数配置、厂商 SDK 初始化及通知点击配置。

dependencies {
    // 需要厂商通道时添加公共组件,并选择实际接入的适配组件。
    implementation 'com.aliyun.ams:alicloud-android-third-push:4.0.0'
    // implementation 'com.aliyun.ams:alicloud-android-third-push-xiaomi:4.0.0'
    // implementation 'com.aliyun.ams:alicloud-android-third-push-huawei:4.0.0'
    // implementation 'com.aliyun.ams:alicloud-android-third-push-honor:4.0.0'
    // implementation 'com.aliyun.ams:alicloud-android-third-push-oppo:4.0.0'
    // implementation 'com.aliyun.ams:alicloud-android-third-push-vivo:4.0.0'
    // implementation 'com.aliyun.ams:alicloud-android-third-push-meizu:4.0.0'
    // implementation 'com.aliyun.ams:alicloud-android-third-push-fcm:4.0.0'
}

主 SDK、辅助通道 SDK 及所选厂商扩展包统一使用 4.0.0。厂商原生 SDK 的版本由对应扩展包的 Maven 依赖管理。

按设备厂商选择厂商通道集成中的对应说明。小米、华为、荣耀、OPPO、vivo、魅族等厂商系统通知的点击需要完成辅助弹窗接入(4.x),并在 onNotificationOpened 中处理点击业务。FCM 的 data 消息由适配器转交移动推送 SDK 处理,不要求经过辅助弹窗 Activity。

第四步:接入验证

1 打开 SDK 日志

4.x SDK 日志统一使用 EMAS_PUSH TAG,模块信息写在日志正文。Android Studio Logcat 可使用 tag:EMAS_PUSH;命令行可执行以下命令。第三方 SDK 直接输出的日志和 Android 系统日志仍按各自 TAG 查询。

adb logcat -s EMAS_PUSH

调试时在 init 后、register 及厂商初始化前显式设置 LOG_DEBUG。setDebug(true) 用于接入检查,不等同于开启 DEBUG 日志。示例应用回调日志使用 PushDemo,可以同时过滤观察:

adb logcat -s EMAS_PUSH PushDemo

2 验证接入结果

验证项

操作与通过标准

依赖及组件

构建成功,最终包包含所需 AAR 组件、资源和 native 库;设备安装后无初始化崩溃。

设备注册

观察 register 成功回调并取得非空 deviceId;失败时记录 errorCode、errorMessage 并定位处理。

透传消息

在控制台向该 deviceId 发送一条消息,观察 onMessage;记录测试时间和消息标识核对本次请求。

通知展示

先经自有通道向同一 deviceId 发送通知,指定已创建的通知渠道;验证 onNotification、通知栏展示、系统权限和渠道开关。厂商通道按下方离线场景单独验证。

点击及删除

分别点击 ACCS 自有通道和厂商通道通知,检查 onNotificationOpened 和业务页面;仅针对 ACCS 自有通道和 FCM 通知验证 onNotificationRemoved。

厂商离线场景

每个已接入厂商分别验证通道注册、离线通知展示及点击跳转,确认服务端对应参数。

发布包

使用开启混淆的 release 包重复验证;验证前台、后台以及实际业务关心的进程状态。

接入验证包括设备注册、发送请求、通知接收、展示和点击。register 成功回调表示设备注册成功,发送接口成功表示请求已受理;请继续结合设备回调、通知栏和点击行为验证完整推送流程。

失败时参见错误处理(4.x);接口扩展见Android SDK 4.x API。