苹果原生推送服务
一、APNS的推送机制
二、APNS推送步骤
三、APNS推送的详细工作流程
四、APNS推送开发准备
五、研发与测试
一、APNS的推送机制
首先我们看一下苹果官方给出的对ios推送机制的解释。如下图
Provider:指自己程序的后台服务器。
APNS:指苹果的推送服务器,全称:ApplePush Notification Service的缩写。
二、APNS推送步骤:
根据上图可以分为三个阶段:
第一阶段:自己应用程序的服务器端把要发送的消息、目的iPhone的标识打包,发给APNS。
第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发送到iPhone。
第三阶段:iPhone把发来的消息传递给相应的应用程序,并且按照app内部规则弹出Push通知。
三、APNS推送通知的详细工作流程
下面这张图是说明APNS推送通知的详细工作流程:
根据图片我们可以这么理解:
1、应用程序注册APNS消息推送。
2、iOS从APNS Server获取devicetoken,应用程序接收devicetoken。
3、应用程序将device token发送给程序的PUSH服务端程序。
4、服务端程序向APNS服务发送消息。
5、APNS服务将消息发送给iPhone应用程序。
四、APNS推送开发准备
1、硬件准备:首先要有一台苹果的设备,模拟器是不支持推送的,所以你需要一台iphone,ipod touch或者ipad。
2、证书准备:客户端与苹果服务器之间和我们自己的服务器与苹果服务器之间都需要证书来进行链接。
注:
1)、证书里面其实就是app的包名生成的一串数,其实就是包名来区分的。
2)、因为APNS Server是通过包名来进行推送的比如说我们的app的包名是com.lezyo.lezyo。
3、代码准备:
1)、建立我们的推送的项目(注意BundleIdentifier必须和我们推送应用的Appid一致)
2)、注册token,因为token就是你推送到某一个手机的钥匙。不过需要告诉他们你注册的类型,有没有声音Sound,有没有标记Badge,有没有弹出框Alert
在AppDelegate里didFinishLaunchingWithOptions函数里写
iOS8以前的注册token方法
-(BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
……
//推送的形式:标记,声音,提示
[[UIApplication sharedApplication] registerForRemoteNotificationTypes: UIRemoteNotificationTypeBadge |UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert];
return YES;
}
iOS8以后的注册token方法
[[UIApplicationsharedApplication] registerUserNotificationSettings:[UIUserNotificationSettingssettingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert |UIUserNotificationTypeBadge) categories:nil]];
[[UIApplication sharedApplication]registerForRemoteNotifications];
3、上一步注册token成功,会在下面方法中得到token;
-(void)application:(UIApplication *)applicationdidRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken{
NSLog(@"regisger success:%@",pToken);
//注册成功,将deviceToken保存到应用服务器数据库中
}
4、如果注册失败,application:didFailToRegisterForRemoteNotificationsWithError:方法会被调用,通过NSError参数你可以看到具体的出错信息,例如
-(void)application:(UIApplication *)applicationdidFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
NSLog(@"Registfail%@",error);
}
请注意:注册流程需要在app每次启动时调用,这并不不会带来额外的负担,因为iOS操作系统在第一次获得了有效的device token之后,会本地缓存起来,以后app再调用registerForRemoteNotificationTypes:的时候会立刻返回,并不会再进行网络请求。另外,app层面不应该对device token进行缓存,因为device token也有可能变化——如果用户重装了操作系统,那么APNs再次给出的device token就会和之前的不一样,又或者是,用户restore了原来的backup到新的设备上,那么原来的device token也会失效。
5、如果上面几步注册token成功,并且把token成功上传给服务器的话,那服务器就能正常推送消息给手机了,接收到消息了
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary*)userInfo{
// 处理推送消息
NSLog(@"userinfo:%@",userInfo);
NSLog(@"收到推送消息:%@",[[userInfo objectForKey:@"aps"]objectForKey:@"alert"]);
}
在此,手机即可受到推送消息了;
五、研发与测试
一般手机可以收到推送消息才刚完成推送功能的一般,还有另一半依然重要,那就是你要实现的业务逻辑
而接收到消息的场景有三种,程序未启动、后台和前台
1,在app没有被启动的时候,接收到了消息通知。这时候操作系统会按照默认的方式来展现一个alert消息,在app icon上标记一个数字,甚至播放一段声音。
2,用户看到消息之后,点击了一下action按钮或者点击了应用图标。如果action按钮被点击了,系统会通过调用application:didFinishLaunchingWithOptions:这个代理方法来启动应用,并且会把notification的payload数据传递进去。如果应用图标被点击了,系统也一样会调用application:didFinishLaunchingWithOptions:这个代理方法来启动应用,唯一不同的是这时候启动参数里面不会有任何notification的信息。
一般会在以下两个方法中进行处理,两个方法里面处理的逻辑是相同的:
1、当应用没有启动时,收到推送消息,并在通知栏点击此消息的时候,会走下面方法
代码如下:
-(BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window =[[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]autorelease];
// Override point forcustomization after application launch.
self.viewController =[[[ViewController alloc] init] autorelease];
self.window.rootViewController = self.viewController;
[self.window setBackgroundColor:[UIColorcolorWithPatternImage:[UIImageimageNamed:@"background.png"]]];
[self.windowmakeKeyAndVisible];
/** 注册推送通知功能,*/
[[UIApplicationsharedApplication]registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge| UIRemoteNotificationTypeSound)];
//判断程序是不是由推送服务完成的
if (launchOptions){
NSDictionary* pushNotificationKey = [launchOptionsobjectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (pushNotificationKey) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"推送通知"
message:@"这是通过推送窗口启动的程序,你可以在这里处理推送内容"
delegate:nil
cancelButtonTitle:@"知道了"
otherButtonTitles:nil, nil];
[alert show];
[alert release];
}
}
returnYES;
}
2、当应用程序在前台或者在后台的时候收到推送消息,会走下面的方法
//收到推送消息后的业务逻辑处理
-(void)application:(UIApplication *)applicationdidReceiveRemoteNotification:(NSDictionary *)userInfo{
// 处理推送消息
objectForKey:@"alert"]);
}
常见问题:
1、我能推送长消息吗?
不能,APNs限制了每个notification的payload最大长度是256字节,超长的消息是不能发送的。
2.、推送怎么加声音提醒?
消息推送是可以指定声音的。譬如你可以对正面的反馈使用欢快的声音,对负面的反馈使用低沉一点的声音,都可以达到别出心裁让人眼前一亮的目的。
你需要先放一些aiff、wav或者caf音频文件到app的资源文件中,然后在推送的时候指定不同的音频文件名就可以了。
3、 推送的Badge是怎么回事?
推送并不一定会导致应用图标上红色数字增加,是否显示这一数字,显示成多少,都取决于开发者自己。
如果用户需要点击app的时候,把数字置为0,
其实我们只需要在任何时候设置UIApplication.applicationIconBadgeNumber 属性为0,就可以让这个数字消失掉。
一般我们会选择在应用启动的时候(application:didFinishLaunchingWithOptions:方法中),或者干脆一点,在应用每次被切换到前台的时候(applicationWillEnterForeground:方法中),调用这一行代码,即可立刻清除掉Badge数字了。
4、推送的消息的格式
对于每一条推送消息,都包含一个payload,通常是组成了一个JSON的Dictionary,这其中必不可少的是aps属性,它对应的value也是一个Dictionary,包含下面一些内容:
1)alert消息(文本或Dictionary)
2)应用图标上的红色数字
3)播放的声音文件名
在由推送激活的app打开事件中,application:didFinishLaunchingWithOptions:的options参数就是这个大的Dictionary对象。
{
aps = {
alert = "hello, everyone";
badge = 2;
sound = default;
};
}
这里要注意的时alert部分,它的值可以是一个String(文本消息),也可以是一个JSON的Dictionary。当它是文本消息的时候,系统就会把这些文字显示到一个alertview中;如果它也是由一个JSON Dictionary组成的话,其格式如下:
* body
*action-loc-key
* loc-key
* loc-args
* launch-image
body部分就是alertView中将要展现出来的文本消息,loc-属性主要是用来实现本地化消息,launch-image只是app主bundle里的一个图片文件的名称,一般来说我们不指定这一属性。
5、应用该怎么响应推送消息
上面说的处理流程,只能简单展示一下远程消息,激活用户让他们重新回到app中来。但是有时候,我们希望带给用户更好的使用体验,譬如如果我们告诉用户:张三刚刚评论了你的照片。这时候用户如果点击action按钮进入app,我们是展示具体的评论页面为好,还是展示通常的启动页面然后让用户自己去找张三的评论好?我想负责任的开发者都会选择前者。
要做到灵活响应不同类型的通知消息,我们需要在通知的payload中增加更多信息,而不能仅仅只有alert出来的文字信息。对于AVOS Cloud消息推送平台来讲,就需要开发者使用更高级功能的JSON格式。譬如我们发送这样的json字符串{"action":{"type":4},"alert":"hello,everyone”} 最终在app内会收到这样的UserInfo Dictionary:
{
action = {
type = 4;
};
aps = {
alert = "hello, everyone";
badge = 4;
};
}
“hello,everyone”会显示到alertView中,但是整个Dictionary会通过launchOptions传递给application: didFinishLaunchingWithOptions: 方法,这样我们在程序里面就可以对不同的消息实现不同的跳转了。