你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> #iOS開發中的兩種消息通知機制詳解

#iOS開發中的兩種消息通知機制詳解

編輯:IOS開發綜合

iOS中通知機制又叫消息機制,其包括兩類:一類是本地通知;另一類是推送通知,也叫遠程通知。兩種通知在iOS中的表現一致,可以通過橫幅或者彈出提醒兩種形式告訴用戶,並且點擊通知可以會打開應用程序,但是實現原理卻完全不同。

本地通知

1.創建UILocalNotification。

2.設置處理通知的時間fireDate。

3.配置通知的內容:通知主體、通知聲音、圖標數字等。

4.配置通知傳遞的自定義數據參數userInfo(這一步可選)。

5.調用通知,可以使用scheduleLocalNotification:按計劃調度一個通知,也可以使用presentLocalNotificationNow立即調用通知。

下面就以一個程序更新後用戶長期沒有使用的提醒為例對本地通知做一個簡單的了解。在這個過程中並沒有牽扯太多的界面操作,所有的邏輯都在AppDelegate中:進入應用後如果沒有注冊通知,需要首先注冊通知請求用戶允許通知;一旦調用完注冊方法,無論用戶是否選擇允許通知此刻都會調用應用程序的- (void)application:(UIApplication )application didRegisterUserNotificationSettings:(UIUserNotificationSettings )notificationSettings代理方法,在這個方法中根據用戶的選擇:如果是允許通知則會按照前面的步驟創建通知並在一定時間後執行。

#import "AppDelegate.h"
#import "KCMainViewController.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
#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];
    //如果已經獲得發送通知的授權則創建本地通知,否則請求授權(注意:如果不請求授權在設置中是沒有對應的通知設置項的,也就是說如果從來沒有發送過請求,即使通過設置也打不開消息允許設置)
    if ([[UIApplication sharedApplication]currentUserNotificationSettings].types!=UIUserNotificationTypeNone) {
        [self addLocalNotification];
    }else{
        [[UIApplication sharedApplication]registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound  categories:nil]];
    }

    return YES;
}
#pragma mark 調用過用戶注冊通知方法之後執行(也就是調用完registerUserNotificationSettings:方法之後執行)
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{
    if (notificationSettings.types!=UIUserNotificationTypeNone) {
        [self addLocalNotification];
    }
}
#pragma mark 進入前台後設置消息信息
-(void)applicationWillEnterForeground:(UIApplication *)application{
    [[UIApplication sharedApplication]setApplicationIconBadgeNumber:0];//進入前台取消應用消息圖標
}
#pragma mark - 私有方法
#pragma mark 添加本地通知
-(void)addLocalNotification{

    //定義本地通知對象
    UILocalNotification *notification=[[UILocalNotification alloc]init];
    //設置調用時間
    notification.fireDate=[NSDate dateWithTimeIntervalSinceNow:10.0];//通知觸發的時間,10s以後
    notification.repeatInterval=2;//通知重復次數
    //notification.repeatCalendar=[NSCalendar currentCalendar];//當前日歷,使用前最好設置時區等信息以便能夠自動同步時間

    //設置通知屬性
    notification.alertBody=@"最近添加了諸多有趣的特性,是否立即體驗?"; //通知主體
    notification.applicationIconBadgeNumber=1;//應用程序圖標右上角顯示的消息數
    notification.alertAction=@"打開應用"; //待機界面的滑動動作提示
    notification.alertLaunchImage=@"Default";//通過點擊通知打開應用時的啟動圖片,這裡使用程序啟動圖片
    //notification.soundName=UILocalNotificationDefaultSoundName;//收到通知時播放的聲音,默認消息聲音
    notification.soundName=@"msg.caf";//通知聲音(需要真機才能聽到聲音)

    //設置用戶信息
    notification.userInfo=@{@"id":@1,@"user":@"Kenshin Cui"};//綁定到通知上的其他附加信息

    //調用通知
    [[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
#pragma mark 移除本地通知,在不需要此通知時記得移除
-(void)removeNotification{
    [[UIApplication sharedApplication] cancelAllLocalNotifications];
}
@end

這裡寫圖片描述

1.請求獲得用戶允許通知的效果。

2.應用退出到後彈出通知的效果。

3.鎖屏狀態下的通知效果(從這個界面可以看到alertAction配置為“打開應用”)。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxzdHJvbmc+16LS4qO6PC9zdHJvbmc+PC9wPg0KPHA+1NrKudPDzajWqtaux7Cx2NDr16Ky4c2o1qrA4NDNo6zI57n708O7p7K71MrQ7dOm08OzzNDyt6LLzc2o1qqjrNTy0tS6877Nzt63qLeiy83NqNaqo6yz/bfH08O7p8rWtq+1vWlPU8no1sPW0LTyv6rNqNaqoaM8L3A+DQo8cD6xvrXYzajWqsrH09Cy2df3z7XNs82z0ru197bItcSjrNa709DU2tOm08PNy7P2tb2688you/LV37nYsdWyxcTcytW1vc2o1qqhoyjXotLio7rV4tK7teO21NPauvPD5rXEzcbLzc2o1qrSssrHzerIq8rK08O1xKGjICk8L3A+DQo8cD7NqNaqtcTJ+dL0ysfTyWlPU8+1zbOypbfFtcSjrLjxyr2x2NDrysdMaW5lYXIgUENNoaJNQTQoSU1BL0FEUENNKaGiJm11O0xhd6GiYUxhd9bQtcTSu9bWo6yyosfSsqW3xcqxvOSx2NDr1NozMHPE2qOst/HU8r2rsbvPtc2zyfnS9Mzmu7ujrM2syrHX1Lao0uXJ+dL0zsS8/rHY0Ou3xbW9bWFpbiBib3VuZGxl1tChozwvcD4NCjxwPrG+tdjNqNaqtcTK/cG/ysfT0M/e1sa1xKOs1+69/LXEsb612M2o1qrX7rbg1rvE3NPQNjS49qOss6y5/dXiuPbK/cG/vauxu8+1zbO69sLUoaM8L3A+DQo8cD7I57n7z+vSqtLGs/2xvrXYzajWqr/J0tS199PDVUlBcHBsaWNhdGlvbrXEY2FuY2VsTG9jYWxOb3RpZmljYXRpb246u/JjYW5jZWxBbGxMb2NhbE5vdGlmaWNhdGlvbnPSxrP91ri2qM2o1qq78sv509DNqNaqoaM8L3A+DQo8cD6008nPw+a1xLPM0PK/ydLUv7S1vXVzZXJJbmZv1eK49sr00NTO0sPHyejWw8HLss7K/aOsxMfDtNXiuPayzsr9yOe6zr3TytXE2D88L3A+DQo8cD7U2mlPU9bQyOe5+7Xju/fSu7j2ta+z9s2o1qoou/LV38v4xsG958Pmu6y2r7Lpv7TNqNaqKaOsxKzIz7vh19S2r7Tyv6q1scew06bTw6Gj08nT2s2o1qrTyc+1zbO197bIxMfDtLTLyrG9+Mjr06bTw9PQwb3W1sfpv/ajusjnufvTptPDs8zQ8tLRvq3N6sirzcuz9sTHw7S0y8qxu+G199PDLSAoQk9PTClhcHBsaWNhdGlvbjooVUlBcHBsaWNhdGlvbiA8ZW0+KWFwcGxpY2F0aW9uIGRpZEZpbmlzaExhdW5jaGluZ1dpdGhPcHRpb25zOihOU0RpY3Rpb25hcnkgPC9lbT4pbGF1bmNoT3B0aW9uc7e9t6g7yOe5+7TLyrHTptPDs8zQ8ru51NrUy9DQKM7ewtvKx9Tax7DMqLu5ysfU2rrzzKgp1PK74bX308MtKHZvaWQpYXBwbGljYXRpb246KFVJQXBwbGljYXRpb24gPGVtPilhcHBsaWNhdGlvbiBkaWRSZWNlaXZlTG9jYWxOb3RpZmljYXRpb246KFVJTG9jYWxOb3RpZmljYXRpb24gPC9lbT4pbm90aWZpY2F0aW9ut723qL3TytXP+8+iss7K/aGjtbHIu8jnufvKx7rz1d/X1Mi7srux2Lbgy7WjrNLyzqqyzsr91tDS0b6tv8nS1MTDtb1ub3RpZmljYXRpb2621M/zo6zWu9KqtsHIoXVzZXJJbmZvyvTQ1Ly0v8mho8jnufvKx8ew1d+1xLuw1PK/ydLUt8POymxhdW5jaE9wdGlvbnPW0Lz8zqpVSUFwcGxpY2F0aW9uTGF1bmNoT3B0aW9uc0xvY2FsTm90aWZpY2F0aW9uS2V5tcS21M/zo6zV4rj2ttTP877Nyse3osvNtcTNqNaqo6zTybTLttTP89TZyKW3w87KdXNlckluZm+ho86qwcvR3cq+1eK49rn9s8zU2s/Cw+a1xLPM0PLW0L2rdXNlckluZm+1xMTayN3QtMjrzsS8/tLUsePEo8Tiudix1bPM0PK689TZzai5/bXju/fNqNaqtPK/qtOm08O78cihdXNlckluZm+1xLn9s8yhozwvcD4NCjxwcmUgY2xhc3M9"brush:java;"> #import "AppDelegate.h" #import "KCMainViewController.h" @interface AppDelegate () @end @implementation AppDelegate #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]; //添加通知 [self addLocalNotification]; //接收通知參數 UILocalNotification *notification=[launchOptions valueForKey:UIApplicationLaunchOptionsLocalNotificationKey]; NSDictionary *userInfo= notification.userInfo; [userInfo writeToFile:@"/Users/kenshincui/Desktop/didFinishLaunchingWithOptions.txt" atomically:YES]; NSLog(@"didFinishLaunchingWithOptions:The userInfo is %@.",userInfo); return YES; } #pragma mark 接收本地通知時觸發 -(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{ NSDictionary *userInfo=notification.userInfo; [userInfo writeToFile:@"/Users/kenshincui/Desktop/didReceiveLocalNotification.txt" atomically:YES]; NSLog(@"didReceiveLocalNotification:The userInfo is %@",userInfo); } #pragma mark 調用過用戶注冊通知方法之後執行(也就是調用完registerUserNotificationSettings:方法之後執行) -(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{ if (notificationSettings.types!=UIUserNotificationTypeNone) { [self addLocalNotification]; } } #pragma mark 進入前台後設置消息信息 -(void)applicationWillEnterForeground:(UIApplication *)application{ [[UIApplication sharedApplication]setApplicationIconBadgeNumber:0];//進入前台取消應用消息圖標 } #pragma mark - 私有方法 #pragma mark 添加本地通知 -(void)addLocalNotification{ //定義本地通知對象 UILocalNotification *notification=[[UILocalNotification alloc]init]; //設置調用時間 notification.fireDate=[NSDate dateWithTimeIntervalSinceNow:10.0];//通知觸發的時間,10s以後 notification.repeatInterval=2;//通知重復次數 //notification.repeatCalendar=[NSCalendar currentCalendar];//當前日歷,使用前最好設置時區等信息以便能夠自動同步時間 //設置通知屬性 notification.alertBody=@"最近添加了諸多有趣的特性,是否立即體驗?"; //通知主體 notification.applicationIconBadgeNumber=1;//應用程序圖標右上角顯示的消息數 notification.alertAction=@"打開應用"; //待機界面的滑動動作提示 notification.alertLaunchImage=@"Default";//通過點擊通知打開應用時的啟動圖片 //notification.soundName=UILocalNotificationDefaultSoundName;//收到通知時播放的聲音,默認消息聲音 notification.soundName=@"msg.caf";//通知聲音(需要真機) //設置用戶信息 notification.userInfo=@{@"id":@1,@"user":@"Kenshin Cui"};//綁定到通知上的其他額外信息 //調用通知 [[UIApplication sharedApplication] scheduleLocalNotification:notification]; } @end

上面的程序可以分為兩種情況去運行:一種是啟動程序關閉程序,等到接收到通知之後點擊通知重新進入程序;另一種是啟動程序後,進入後台(其實在前台也可以,但是為了明顯的體驗這個過程建議進入後台),接收到通知後點擊通知進入應用。另種情況會分別按照前面說的情況調用不同的方法接收到userInfo寫入本地文件系統。有了userInfo一般來說就可以根據這個信息進行一些處理,例如可以根據不同的參數信息導航到不同的界面,假設是更新的通知則可以導航到更新內容界面等。

推送通知

和本地通知不同,推送通知是由應用服務提供商發起的,通過蘋果的APNs(Apple Push Notification Server)發送到應用客戶端。下面是蘋果官方關於推送通知的過程示意圖:
這裡寫圖片描述

推送通知的過程可以分為以下幾步:

1.應用服務提供商從服務器端把要發送的消息和設備令牌(device token)發送給蘋果的消息推送服務器APNs。

2.APNs根據設備令牌在已注冊的設備(iPhone、iPad、iTouch、mac等)查找對應的設備,將消息發送給相應的設備。

3.客戶端設備接將接收到的消息傳遞給相應的應用程序,應用程序根據用戶設置彈出通知消息。

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

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

說明:

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。

說明:

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

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

#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://192.168.1.101/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

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

其次,由於device token需要發送給服務器端,這裡使用一個Web應用作為服務器端接收device token,這裡使用了ASP.NET程序來處理令牌接收注冊工作,當然你使用其他技術同樣沒有問題。下面是對應的後台代碼:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using CMJ.Framework.Data;
namespace WebServer
{
    public partial class RegisterDeviceToken : System.Web.UI.Page
    {
        private string _appID = @"com.cmjstudio.pushnotification";
        private SqlHelper _helper = new SqlHelper();
        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                string oldToken = Request["oldToken"] + "";
                string newToken = Request["newToken"] + "";
                string sql = "";
                //如果傳遞舊的設備令牌則刪除舊令牌添加新令牌
                if (oldToken != "")
                {
                    sql = string.Format("DELETE FROM dbo.Device WHERE AppID='{0}' AND DeviceToken='{1}';", _appID, oldToken);
                }
                sql += string.Format(@"IF NOT EXISTS (SELECT ID FROM dbo.Device WHERE AppID='{0}' AND DeviceToken='{1}')
                                        INSERT INTO dbo.Device ( AppID, DeviceToken ) VALUES ( N'{0}', N'{1}');", _appID, newToken);
                _helper.ExecuteNonQuery(sql);
                Response.Write("注冊成功!");
            }
            catch(Exception ex)
            {
                Response.Write("注冊失敗,錯誤詳情:"+ex.ToString());
            }
        }
    }
}

這個過程主要就是保存device token到數據庫中,當然如果同時傳遞舊的設備令牌還需要先刪除就的設備令牌,這裡簡單的在數據庫中創建了一張Device表來保存設備令牌,其中記錄了應用程序Id和設備令牌。
第三步就是服務器端發送消息,如果要給APNs發送消息就必須按照Apple的標准消息格式組織消息內容。但是好在目前已經有很多開源的第三方類庫供我們使用,具體消息如何包裝完全不用自己組織,這裡使用一個開源的類庫Push Sharp來給APNs發送消息 ,除了可以給Apple設備推送消息,Push Sharp還支持Android、Windows Phone等多種設備,更多詳細內容大家可以參照官方說明。前面說過如果要開發消息推送應用不能使用一般的開發配置文件,這裡還需要注意:如果服務器端要給APNs發送消息其秘鑰也必須是通過APNs Development iOS類型的證書來導出的,一般的iOS Development 類型的證書導出的秘鑰無法用作服務器端發送秘鑰。下面通過在一個簡單的WinForm程序中調用Push Sharp給APNs發送消息,這裡讀取之前Device表中的所有設備令牌循環發送消息:

using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using PushSharp;
using PushSharp.Apple;
using CMJ.Framework.Data;
using CMJ.Framework.Logging;
using CMJ.Framework.Windows.Forms;
namespace PushNotificationServer
{
    public partial class frmMain : PersonalizeForm
    {
        private string _appID = @"com.cmjstudio.pushnotification";
        private SqlHelper _helper = new SqlHelper();
        public frmMain()
        {
            InitializeComponent();
        }
        private void btnClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        private void btnSend_Click(object sender, EventArgs e)
        {
            List deviceTokens = GetDeviceToken();
            SendMessage(deviceTokens, tbMessage.Text);
        }
        #region 發送消息
        /// 
        /// 取得所有設備令牌
        /// 
        /// 設備令牌
        private List GetDeviceToken()
        {
            List deviceTokens = new List();
            string sql = string.Format("SELECT DeviceToken FROM dbo.Device WHERE AppID='{0}'",_appID);
            DataTable dt = _helper.GetDataTable(sql);
            if(dt.Rows.Count>0)
            {
                foreach(DataRow dr in dt.Rows)
                {
                    deviceTokens.Add((dr["DeviceToken"]+"").TrimStart('<').TrimEnd('>').Replace(" ",""));
                }
            }
            return deviceTokens;
        }

        /// 
        /// 發送消息
        /// 
        ///設備令牌
        ///消息內容
        private void SendMessage(List deviceToken, string message)
        {
            //創建推送對象
            var pusher = new PushBroker();
            pusher.OnNotificationSent += pusher_OnNotificationSent;//發送成功事件
            pusher.OnNotificationFailed += pusher_OnNotificationFailed;//發送失敗事件
            pusher.OnChannelCreated += pusher_OnChannelCreated;
            pusher.OnChannelDestroyed += pusher_OnChannelDestroyed;
            pusher.OnChannelException += pusher_OnChannelException;
            pusher.OnDeviceSubscriptionChanged += pusher_OnDeviceSubscriptionChanged;
            pusher.OnDeviceSubscriptionExpired += pusher_OnDeviceSubscriptionExpired;
            pusher.OnNotificationRequeue += pusher_OnNotificationRequeue;
            pusher.OnServiceException += pusher_OnServiceException;
            //注冊推送服務
            byte[] certificateData = File.ReadAllBytes(@"E:\KenshinCui_Push.p12");
            pusher.RegisterAppleService(new ApplePushChannelSettings(certificateData, "123"));
            foreach (string token in deviceToken)
            {
                //給指定設備發送消息
                pusher.QueueNotification(new AppleNotification()
                    .ForDeviceToken(token)
                    .WithAlert(message) 
                    .WithBadge(1)
                    .WithSound("default"));
            }
        }
        void pusher_OnServiceException(object sender, Exception error)
        {
            Console.WriteLine("消息發送失敗,錯誤詳情:" + error.ToString());
            PersonalizeMessageBox.Show(this, "消息發送失敗,錯誤詳情:" + error.ToString(), "系統提示");
        }
        void pusher_OnNotificationRequeue(object sender, PushSharp.Core.NotificationRequeueEventArgs e)
        {
            Console.WriteLine("pusher_OnNotificationRequeue");
        }
        void pusher_OnDeviceSubscriptionExpired(object sender, string expiredSubscriptionId, DateTime expirationDateUtc, PushSharp.Core.INotification notification)
        {
            Console.WriteLine("pusher_OnDeviceSubscriptionChanged");
        }
        void pusher_OnDeviceSubscriptionChanged(object sender, string oldSubscriptionId, string newSubscriptionId, PushSharp.Core.INotification notification)
        {
            Console.WriteLine("pusher_OnDeviceSubscriptionChanged");
        }
        void pusher_OnChannelException(object sender, PushSharp.Core.IPushChannel pushChannel, Exception error)
        {
            Console.WriteLine("消息發送失敗,錯誤詳情:" + error.ToString());
            PersonalizeMessageBox.Show(this, "消息發送失敗,錯誤詳情:" + error.ToString(), "系統提示");
        }
        void pusher_OnChannelDestroyed(object sender)
        {
            Console.WriteLine("pusher_OnChannelDestroyed");
        }
        void pusher_OnChannelCreated(object sender, PushSharp.Core.IPushChannel pushChannel)
        {
            Console.WriteLine("pusher_OnChannelCreated");
        }
        void pusher_OnNotificationFailed(object sender, PushSharp.Core.INotification notification, Exception error)
        {
            Console.WriteLine("消息發送失敗,錯誤詳情:" + error.ToString());
            PersonalizeMessageBox.Show(this, "消息發送失敗,錯誤詳情:"+error.ToString(), "系統提示");
        }
        void pusher_OnNotificationSent(object sender, PushSharp.Core.INotification notification)
        {
            Console.WriteLine("消息發送成功!");
            PersonalizeMessageBox.Show(this, "消息發送成功!", "系統提示");
        }
        #endregion
    }
}

服務器端消息發送應用運行效果:
這裡寫圖片描述

iOS客戶端接收的消息的效果:
這裡寫圖片描述

到目前為止通過服務器端應用可以順利發送消息給APNs並且iOS應用已經成功接收推送消息。

補充–iOS開發證書、秘鑰

iOS開發過程中如果需要進行真機調試、發布需要注冊申請很多證書,對於初學者往往迷惑不解,再加上今天的文章中會牽扯到一些特殊配置,這裡就簡單的對iOS開發的常用證書和秘鑰等做一說明。

證書

iOS常用的證書包括開發證書和發布證書,無論是真機調試還是最終發布應用到App Store這兩個證書都是必須的,它是iOS開發的基本證書。

a.開發證書:開發證書又分為普通開發證書和推送證書,如果僅僅是一般的應用則前者即可滿足,但是如果開發推送應用則必須使用推送證書。

b.發布證書:發布證書又可以分為普通發布證書、推送證書、Pass Type ID證書、站點發布證書、VoIP服務證書、蘋果支付證書。同樣的,對於需要使用特殊服務的應用則必須選擇對應的證書。

應用標識

App ID,應用程序的唯一標識,對應iOS應用的Bundle Identifier,App ID在蘋果開發者中心中分為通配應用ID和明確的應用ID,前者一般用於普通應用開發,一個ID可以適用於多個不同標識的應用;但是對於使用消息推送、Passbook、站點發布、iCloud等服務的應用必須配置明確的應用ID。

設備標識

UDID,用於標識每一台硬件設備的標示符。注意它不是device token,device token是根據UDID使用一個只有Apple自己才知道的算法生成的一組標示符。

配置簡介

Provisioning Profiles,平時又稱為PP文件。將UDID、App ID、開發證書打包在一起的配置文件,同樣分為開發和發布兩類配置文件。

秘鑰

在申請開發證書時必須要首先提交一個秘鑰請求文件,對於生成秘鑰請求文件的mac,如果要做開發則只需要下載證書和配置簡介即可開發。但是如果要想在其他機器上做開發則必須將證書中的秘鑰導出(導出之後是一個.p12文件),然後導入其他機器。同時對於類似於推送服務器端應用如果要給APNs發送消息,同樣需要使用.p12秘鑰文件,並且這個秘鑰文件需要是推送證書導出的對應秘鑰。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved