遠程通知APNs(Apple Push Notification Server)

推送通知是由應用服務提供商發起的,經過蘋果的APNs(Apple Push Notification Server)發送到應用客戶端。下面是蘋果官方關於推送通知的過程示意圖:算法

PushNotification_FlowChart

推送通知的過程能夠分爲如下幾步:服務器

  1. 應用服務提供商從服務器端把要發送的消息和設備令牌(device token)發送給蘋果的消息推送服務器APNs。 
  2. APNs根據設備令牌在已註冊的設備(iPhone、iPad、iTouch、mac等)查找對應的設備,將消息發送給相應的設備。 
  3. 客戶端設備接將接收到的消息傳遞給相應的應用程序,應用程序根據用戶設置彈出通知消息。

固然,這只是一個簡單的流程,有了這個流程咱們還無從下手編寫程序,將上面的流程細化能夠獲得以下流程圖(圖片來自互聯網),在這個過程當中會也會提到如何在程序中完成這些步驟:網絡

PushNotification_FlowChartDetail

1.應用程序註冊APNs推送消息。session

說明:app

a.只有註冊過的應用纔有可能接收到消息,程序中一般經過UIApplication的registerUserNotificationSettings:方法註冊,iOS8中通知註冊的方法發生了改變,若是是iOS7及以前版本的iOS請參考其餘代碼。框架

b.註冊以前有兩個前提條件必須準備好:開發配置文件(provisioning profile,也就是.mobileprovision後綴的文件)的App ID不能使用通配ID必須使用指定APP ID而且生成配置文件中選擇Push Notifications服務,通常的開發配置文件沒法完成註冊;應用程序的Bundle Identifier必須和生成配置文件使用的APP ID徹底一致。性能

2.iOS從APNs接收device token,在應用程序獲取device token。優化

說明:ui

a.在UIApplication的-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken代理方法中獲取令牌,此方法發生在註冊以後。url

b.若是沒法正確得到device token能夠在UIApplication的-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error代理方法中查看詳細錯誤信息,此方法發生在獲取device token失敗以後。

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客戶端代碼:

 1 //
 2 //  AppDelegate.m
 3 //  pushnotification
 4 //
 5 //  Created by Kenshin Cui on 14/03/27.
 6 //  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
 7 //
 8 
 9 #import "AppDelegate.h"
10 #import "KCMainViewController.h"
11 
12 @interface AppDelegate ()
13 
14 @end
15 
16 @implementation AppDelegate
17 
18 #pragma mark - 應用程序代理方法
19 #pragma mark 應用程序啓動以後
20 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
21     
22     _window=[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
23     
24     _window.backgroundColor =[UIColor colorWithRed:249/255.0 green:249/255.0 blue:249/255.0 alpha:1];
25     
26     //設置全局導航條風格和顏色
27     [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:23/255.0 green:180/255.0 blue:237/255.0 alpha:1]];
28     [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
29     
30     KCMainViewController *mainController=[[KCMainViewController alloc]init];
31     _window.rootViewController=mainController;
32     
33     [_window makeKeyAndVisible];
34     
35     //註冊推送通知(注意iOS8註冊方法發生了變化)
36     [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
37     [application registerForRemoteNotifications];
38     
39     return YES;
40 }
41 #pragma mark 註冊推送通知以後
42 //在此接收設備令牌
43 -(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
44     [self addDeviceToken:deviceToken];
45     NSLog(@"device token:%@",deviceToken);
46 }
47 
48 #pragma mark 獲取device token失敗後
49 -(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
50     NSLog(@"didFailToRegisterForRemoteNotificationsWithError:%@",error.localizedDescription);
51     [self addDeviceToken:nil];
52 }
53 
54 #pragma mark 接收到推送通知以後
55 -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
56     NSLog(@"receiveRemoteNotification,userInfo is %@",userInfo);
57 }
58 
59 #pragma mark - 私有方法
60 /**
61  *  添加設備令牌到服務器端
62  *
63  *  @param deviceToken 設備令牌
64  */
65 -(void)addDeviceToken:(NSData *)deviceToken{
66     NSString *key=@"DeviceToken";
67     NSData *oldToken= [[NSUserDefaults standardUserDefaults]objectForKey:key];
68     //若是偏好設置中的已存儲設備令牌和新獲取的令牌不一樣則存儲新令牌而且發送給服務器端
69     if (![oldToken isEqualToData:deviceToken]) {
70         [[NSUserDefaults standardUserDefaults] setObject:deviceToken forKey:key];
71         [self sendDeviceTokenWidthOldDeviceToken:oldToken newDeviceToken:deviceToken];
72     }
73 }
74 
75 -(void)sendDeviceTokenWidthOldDeviceToken:(NSData *)oldToken newDeviceToken:(NSData *)newToken{
76     //注意必定確保真機能夠正常訪問下面的地址
77     NSString *urlStr=@"http://192.168.1.101/RegisterDeviceToken.aspx";
78     urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
79     NSURL *url=[NSURL URLWithString:urlStr];
80     NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:10.0];
81     [requestM setHTTPMethod:@"POST"];
82     NSString *bodyStr=[NSString stringWithFormat:@"oldToken=%@&newToken=%@",oldToken,newToken];
83     NSData *body=[bodyStr dataUsingEncoding:NSUTF8StringEncoding];
84     [requestM setHTTPBody:body];
85     NSURLSession *session=[NSURLSession sharedSession];
86     NSURLSessionDataTask *dataTask= [session dataTaskWithRequest:requestM completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
87         if (error) {
88             NSLog(@"Send failure,error is :%@",error.localizedDescription);
89         }else{
90             NSLog(@"Send Success!");
91         }
92         
93     }];
94     [dataTask resume];
95 }
96 @end

 

iOS客戶端代碼的代碼比較簡單,註冊推送通知,獲取device token存儲到偏好設置中,而且若是新獲取的device token不一樣於偏好設置中存儲的數據則發送給服務器端,更新服務器端device token列表。

相關文章
相關標籤/搜索