iOS开发系列--通知与消息机制详解

2020-01-18 17:10:15于丽

c.必须真机调试,模拟器无法获取device token。

3.iOS应用将device token发送给应用程序提供商,告诉服务器端当前设备允许接收消息。

说明:

a.device token的生成算法只有Apple掌握,为了确保算法发生变化后仍然能够正常接收服务器端发送的通知,每次应用程序启动都重新获得device token(注意:device token的获取不会造成性能问题,苹果官方已经做过优化)。

b.通常可以创建一个网络连接发送给应用程序提供商的服务器端, 在这个过程中最好将上一次获得的device token存储起来,避免重复发送,一旦发现device token发生了变化最好将原有的device token一块发送给服务器端,服务器端删除原有令牌存储新令牌避免服务器端发送无效消息。

4.应用程序提供商在服务器端根据前面发送过来的device token组织信息发送给APNs。

说明:

a.发送时指定device token和消息内容,并且完全按照苹果官方的消息格式组织消息内容,通常情况下可以借助其他第三方消息推送框架来完成。

5.APNs根据消息中的device token查找已注册的设备推送消息。

说明:

a.正常情况下可以根据device token将消息成功推送到客户端设备中,但是也不排除用户卸载程序的情况,此时推送消息失败,APNs会将这个错误消息通知服务器端以避免资源浪费(服务器端此时可以根据错误删除已经存储的device token,下次不再发送)。

下面将简单演示一下推送通知的简单流程:

首先,看一下iOS客户端代码:


//
// AppDelegate.m
// pushnotification
//
// Created by Kenshin Cui on 14/03/27.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "AppDelegate.h"
#import "KCMainViewController.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

#pragma mark - 应用程序代理方法
#pragma mark 应用程序启动之后
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  
  _window=[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
  
  _window.backgroundColor =[UIColor colorWithRed:249/255.0 green:249/255.0 blue:249/255.0 alpha:1];
  
  //设置全局导航条风格和颜色
  [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:23/255.0 green:180/255.0 blue:237/255.0 alpha:1]];
  [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
  
  KCMainViewController *mainController=[[KCMainViewController alloc]init];
  _window.rootViewController=mainController;
  
  [_window makeKeyAndVisible];
  
  //注册推送通知(注意iOS8注册方法发生了变化)
  [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
  [application registerForRemoteNotifications];
  
  return YES;
}
#pragma mark 注册推送通知之后
//在此接收设备令牌
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
  [self addDeviceToken:deviceToken];
  NSLog(@"device token:%@",deviceToken);
}

#pragma mark 获取device token失败后
-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
  NSLog(@"didFailToRegisterForRemoteNotificationsWithError:%@",error.localizedDescription);
  [self addDeviceToken:nil];
}

#pragma mark 接收到推送通知之后
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
  NSLog(@"receiveRemoteNotification,userInfo is %@",userInfo);
}

#pragma mark - 私有方法
/**
 * 添加设备令牌到服务器端
 *
 * @param deviceToken 设备令牌
 */
-(void)addDeviceToken:(NSData *)deviceToken{
  NSString *key=@"DeviceToken";
  NSData *oldToken= [[NSUserDefaults standardUserDefaults]objectForKey:key];
  //如果偏好设置中的已存储设备令牌和新获取的令牌不同则存储新令牌并且发送给服务器端
  if (![oldToken isEqualToData:deviceToken]) {
    [[NSUserDefaults standardUserDefaults] setObject:deviceToken forKey:key];
    [self sendDeviceTokenWidthOldDeviceToken:oldToken newDeviceToken:deviceToken];
  }
}

-(void)sendDeviceTokenWidthOldDeviceToken:(NSData *)oldToken newDeviceToken:(NSData *)newToken{
  //注意一定确保真机可以正常访问下面的地址
  NSString *urlStr=@"http://www.easck.com/RegisterDeviceToken.aspx";
  urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  NSURL *url=[NSURL URLWithString:urlStr];
  NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:10.0];
  [requestM setHTTPMethod:@"POST"];
  NSString *bodyStr=[NSString stringWithFormat:@"oldToken=%@&newToken=%@",oldToken,newToken];
  NSData *body=[bodyStr dataUsingEncoding:NSUTF8StringEncoding];
  [requestM setHTTPBody:body];
  NSURLSession *session=[NSURLSession sharedSession];
  NSURLSessionDataTask *dataTask= [session dataTaskWithRequest:requestM completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (error) {
      NSLog(@"Send failure,error is :%@",error.localizedDescription);
    }else{
      NSLog(@"Send Success!");
    }
    
  }];
  [dataTask resume];
}
@end