iOS消息推送的工作机制如下图所示:
第一阶段:应用程序把要发送的消息、目的iPhone的标识打包,发给APNS。
第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发送到iPhone。
第三阶段:iPhone把发来的消息传递给相应的应用程序,并且按照设定弹出Push通知。
在实际的开发中,会使用第三方的移动推送平台运营商,如极光推送、阿里推送,这里我们使用的是阿里推送来做介绍
简单介绍一下iOS移动推送预备工作,首先进入苹果开发者平台使用苹果开发者账号登陆
选择中间的Certificates, Identifiers & Profiles
找到应用对应的Identifiers(Bundle ID)进入Edit your App ID Configuration,勾选Push Notifications, 生成推送证书,以上步骤需要有可用的Apple 开发者账号和已创建的应用ID。配置好后下载证书文件双击安装, 系统会自动打开钥匙串,选中安装的证书文件,右击导出为.p12文件保存好用于上传至第三方推送平台
使用Xcode打开项目代码,点击左侧项目导航栏并选中项目,选择Signing & Capabilities
点击下方的+ Capability会弹出视图
分别搜索Push Notification和Remote Notifications,成功添加后如下图所示,Xcode移动推送配置就算完成了
1、登录EMAS控制台。创建应用并通过审核
2、在控制台首页产品及应用模块,单击选择您的iOS应用,进入当前应用的应用管理页面。
3、单击右上角的应用配置,选择推送配置页签。
4、分别上传配置好的.P12证书、输入证书密码单击保存并验证证书。
5、输入测试设备的device Token,单击测试推送,测试成功后单击确定完成配置。
登录您的 阿里云 EMAS 控制台 显示如下页面 点击 SDK 下载:
在右侧弹窗 下载您需要集成的SDK:
公共包依赖
libz.tbdlibresolv.tbdCoreTelephony.frameworkSystemConfiguration.frameworklibsqlite3.tbdSDK目录结构
CloudPushSDK.frameworkAlicloudUtils.frameworkUTDID.frameworkUTMini.framework引入Framework
Xcode中,直接把下载SDK目录中的framework拖入对应Target下即可,在弹出框勾选Copy items if needed。在 Build Phases -> Link Binary With Libraries中,引入2.1.2列出的公共包;在项目工程中引入CloudPush
#import <CloudPushSDK/CloudPushSDK.h>应用的targets -> Build Settings -> Linking -> Other Linker Flags,请加上-ObjC这个属性,否则推送服务无法正常使用;如果之前已经设置了force_load,需要设置-force_load <framework_path>/CloudPushSDK.framework/CloudPushSDK。
注册阿里推送初始化,传入的参数就是在阿里注册应用时得到的appKey和appSecret,注册的应用包名必须和本地项目代码的Bundle ID一致
- (void)initCloudPush { // SDK初始化 [CloudPushSDK asyncInit:@"*****" appSecret:@"*****" callback:^(CloudPushCallbackResult *res) { if (res.success) { NSLog(@"Push SDK init success, deviceId: %@.", [CloudPushSDK getDeviceId]); } else { NSLog(@"Push SDK init failed, error: %@", res.error); } }]; }向苹果 APNs 注册获取 deviceToken 并上报到阿里云推送服务器;
# pragma mark -- 向APNs注册, 获取deviceToken并上报给SDK - (void)registerAPNS:(UIApplication *)application { float systemVersionNum = [[[UIDevice currentDevice] systemVersion] floatValue]; NSLog(@"\n \n ==== 当前手机系统版本 : %f",systemVersionNum); if (systemVersionNum >= 10.0) { _notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; // 创建 category , 并注册到通知中心 [self createCustomNotificationCategory]; // 遵循协议 _notificationCenter.delegate = self; // 咨询客户是否允许推送通知 , 以及设置推送的类型 [_notificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) { if (granted) { NSLog(@"\n \n ==== 客户允许通知. "); // 向APNs注册, 获取 deviceToken // 要求在主线程中 dispatch_async(dispatch_get_main_queue(), ^{ [application registerForRemoteNotifications]; }); } else { } }]; } else if (systemVersionNum >= 8.0) { // 适配 iOS_8, iOS_10.0 // iOS 8 Notifications #pragma clang diagnostic push #pragma clang diagnostic ignored"-Wdeprecated-declarations" [application registerUserNotificationSettings: [UIUserNotificationSettings settingsForTypes: (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]]; [application registerForRemoteNotifications]; #pragma clang diagnostic pop } else { // iOS < 8 Notifications #pragma clang diagnostic push #pragma clang diagnostic ignored"-Wdeprecated-declarations" [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)]; #pragma clang diagnostic pop } } /* * 苹果推送注册成功回调,将苹果返回的deviceToken上传到CloudPush服务器 */ - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [CloudPushSDK registerDevice:deviceToken withCallback:^(CloudPushCallbackResult *res) { if (res.success) { NSLog(@"Register deviceToken success."); } else { NSLog(@"Register deviceToken failed, error: %@", res.error); } }]; } /* * 苹果推送注册失败回调 */ - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { NSLog(@"didFailToRegisterForRemoteNotificationsWithError %@", error); }推送消息到来监听;
/** * 注册推送消息到来监听 */ - (void)registerMessageReceive { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMessageReceived:) name:@"CCPDidReceiveMessageNotification" object:nil]; } /** * 处理到来推送消息 * * @param notification */ - (void)onMessageReceived:(NSNotification *)notification { CCPSysMessage *message = [notification object]; NSString *title = [[NSString alloc] initWithData:message.title encoding:NSUTF8StringEncoding]; NSString *body = [[NSString alloc] initWithData:message.body encoding:NSUTF8StringEncoding]; NSLog(@"Receive message title: %@, content: %@.", title, body); }申请推送通知的权限
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; if (@available(iOS 10, *)) { center = [UNUserNotificationCenter currentNotificationCenter]; [center requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) { if (granted) { // granted NSLog(@"User authored notification."); dispatch_async(dispatch_get_main_queue(), ^{ [application registerForRemoteNotifications]; }); } else { // not granted NSLog(@"User denied notification."); } }]; }绑定用户和推送设备(推送机制推送的目标是设备,如果需求要求推送到用户,需要将设备和用户绑定)
[CloudPushSDK bindAccount:account withCallback:^(CloudPushCallbackResult *res) { NSLog(@"CloudPushSDK--绑定用户成功"); }]; [CloudPushSDK unbindAccount:^(CloudPushCallbackResult *res) { NSLog(@"CloudPushSDK--解除绑定成功"); }];解决用户在运行时无法显示通知,iOS在前台运行时,收到的通知默认是不会显示通知栏,需要手动处理实现接受远程通知的代理,在接收到通知后转为本地通知并弹出显示
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{ //[self showLocalNotification]; completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge); } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { self.remoteNotificationUserInfo = userInfo; if (application.applicationState == UIApplicationStateActive) { [self showLocalNotification]; } } -(void)showLocalNotification { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; content.body = self.remoteNotificationUserInfo[@"aps"][@"alert"][@"body"]; content.title = self.remoteNotificationUserInfo[@"aps"][@"alert"][@"title"]; content.userInfo = self.remoteNotificationUserInfo; content.sound = [UNNotificationSound defaultSound]; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"Notif" content:content trigger:nil]; [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { }]; }iOS 原生端的移动推送介绍基本结束,更详细的介绍可以参考阿里官方的推送文档https://help.aliyun.com/document_detail/30072.html
Android可以使用Gradle maven库集成阿里推送SDK,同iOS,需要先在阿里推送管理平台注册应用,填写包名
在Project根目录下build.gradle文件中配置maven库URL:
allprojects { repositories { jcenter() maven { url 'http://maven.aliyun.com/nexus/content/repositories/releases/' } } }在对应的module下的build.gradle文件中添加对应依赖
android { ...... defaultConfig { applicationId "com.xxx.xxx" //包名 ...... ndk { //选择要添加的对应cpu类型的.so库。 abiFilters 'armeabi', 'x86' } ...... } ...... } dependencies { ...... compile 'com.aliyun.ams:alicloud-android-push:3.2.1@aar' compile 'com.aliyun.ams:alicloud-android-utdid:2.5.1-proguard' compile 'com.aliyun.ams:alicloud-android-utils:1.1.6.4' compile 'com.aliyun.ams:alicloud-android-ut:5.4.3' compile 'com.aliyun.ams:alicloud-android-beacon:1.0.4.3' compile 'com.aliyun.ams:alicloud-android-agoo:1.0.0' compile 'com.taobao.android:accs_sdk_taobao:3.3.7.6-emas' compile 'com.taobao.android:networksdk:3.5.5.2-open' compile 'com.taobao.android:tnet4android:3.1.14.7-all' // 或 compile 'com.aliyun.ams:alicloud-android-push:3.2.1' ...... }注意⚠️,compile在Gradle3.0中被弃用,需要使用implementation替代
集成阿里推送后还需要在其他厂商注册APP才能支持Huawei、XiaoMi等推送,因此还需要修改我们的依赖,增加对其他厂商的推送支持
dependencies { ... implementation 'com.aliyun.ams:alicloud-android-third-push:3.1.0@aar' implementation 'com.aliyun.ams:huawei-push:2.6.3.305' implementation 'com.aliyun.ams:huawei-push-base:2.6.3.305' }在 小米开放平台 注册你的App, 得到相应的小米AppID,小米AppKey,小米AppSecert。在控制台应用配置设置你的小米AppSecert。(注意:最新的小米开放平台是分开 push 功能的,需要在 push 功能区 开通/启用 推送功能)
在 华为开发者联盟 注册 App,应用审核通过后,能够得到华为的AppID和AppSecert。在控制台应用配置中设置你的应用 AppID 和 AppSecert。(注意:最新的华为开放平台是分开push功能的,需要在push功能区 开通/启用 推送功能),华为后台添加消息回执回调地址,https://agoo-ack.m.taobao.com/hw/
在阿里云移动推送控制台配置辅助通道,具体操作查看官方文档https://help.aliyun.com/document_detail/30067.html
在AndroidMainfest.xml文件中application标签下添加对应厂商的APPSecret等配置,具体操作需要查看对应厂商给出的官方文档例如华为:
<meta-data android:name="com.huawei.hms.client.appid" android:value="appid=你的appid" />创建消息接收Receiver,继承自com.alibaba.sdk.android.push.MessageReceiver,并在对应回调中添加业务处理逻辑,可参考以下代码:
public class MyMessageReceiver extends MessageReceiver { // 消息接收部分的LOG_TAG public static final String REC_TAG = "receiver"; @Override public void onNotification(Context context, String title, String summary, Map<String, String> extraMap) { // TODO 处理推送通知 Log.e("MyMessageReceiver", "Receive notification, title: " + title + ", summary: " + summary + ", extraMap: " + extraMap); } @Override public void onMessage(Context context, CPushMessage cPushMessage) { Log.e("MyMessageReceiver", "onMessage, messageId: " + cPushMessage.getMessageId() + ", title: " + cPushMessage.getTitle() + ", content:" + cPushMessage.getContent()); } @Override public void onNotificationOpened(Context context, String title, String summary, String extraMap) { Log.e("MyMessageReceiver", "onNotificationOpened, title: " + title + ", summary: " + summary + ", extraMap:" + extraMap); } @Override protected void onNotificationClickedWithNoAction(Context context, String title, String summary, String extraMap) { Log.e("MyMessageReceiver", "onNotificationClickedWithNoAction, title: " + title + ", summary: " + summary + ", extraMap:" + extraMap); } @Override protected void onNotificationReceivedInApp(Context context, String title, String summary, Map<String, String> extraMap, int openType, String openActivity, String openUrl) { Log.e("MyMessageReceiver", "onNotificationReceivedInApp, title: " + title + ", summary: " + summary + ", extraMap:" + extraMap + ", openType:" + openType + ", openActivity:" + openActivity + ", openUrl:" + openUrl); } @Override protected void onNotificationRemoved(Context context, String messageId) { Log.e("MyMessageReceiver", "onNotificationRemoved"); } }将该receiver添加到AndroidManifest.xml中
<!-- 消息接收监听器 (用户可自主扩展) --> <receiver android:name=".MyMessageReceiver" android:exported="false"> <!-- 为保证receiver安全,建议设置不可导出,如需对其他应用开放可通过android:permission进行限制 --> <intent-filter> <action android:name="com.alibaba.push2.action.NOTIFICATION_OPENED" /> </intent-filter> <intent-filter> <action android:name="com.alibaba.push2.action.NOTIFICATION_REMOVED" /> </intent-filter> <intent-filter> <action android:name="com.alibaba.sdk.android.push.RECEIVE" /> </intent-filter> </receiver>首先通过PushServiceFactory获取到CloudPushService,然后调用register()初始化并注册云推送通道,并确保Application上下文中进行初始化工作。
import android.app.Application; import android.content.Context; import android.util.Log; import com.alibaba.sdk.android.push.CloudPushService; import com.alibaba.sdk.android.push.CommonCallback; import com.alibaba.sdk.android.push.noonesdk.PushServiceFactory; public class MainApplication extends Application { private static final String TAG = "Init"; @Override public void onCreate() { super.onCreate(); initCloudChannel(this); } /** * 初始化云推送通道 * @param applicationContext */ private void initCloudChannel(Context applicationContext) { PushServiceFactory.init(applicationContext); CloudPushService pushService = PushServiceFactory.getCloudPushService(); pushService.register(applicationContext, new CommonCallback() { @Override public void onSuccess(String response) { Log.d(TAG, "init cloudchannel success"); } @Override public void onFailed(String errorCode, String errorMessage) { Log.d(TAG, "init cloudchannel failed -- errorcode:" + errorCode + " -- errorMessage:" + errorMessage); } }); } } 移动推送的初始化必须在Application主线程中,不能放到Activity中执行,也不能异步初始化。移动推送在初始化过程中将启动后台进程channel,必须保证应用进程和channel进程都执行到推送初始化代码。如果设备成功注册,将回调callback.onSuccess()方法。但如果注册服务器连接失败,则调用callback.onFailed方法,并且自动进行重新注册,直到onSuccess为止。(重试规则会由网络切换等时间自动触发。)Android 阿里推送配置官方文档 https://help.aliyun.com/document_detail/51056.html
