通知與消息機(jī)制_遠(yuǎn)程推送(推送周知)

和本地通知不同脯颜,推送通知(遠(yuǎn)程推送)是由應(yīng)用服務(wù)提供商發(fā)起的,通過蘋果的APNs(Apple Push Notification Server)發(fā)送到應(yīng)用客戶端

屏幕快照 2016-03-26 下午8.43.33.png
  • Provider:就是為指定iOS設(shè)備應(yīng)用程序提供Push的服務(wù)器箱吕,(如果iOS設(shè)備的應(yīng)用程序是客戶端的話,那么Provider可以理解為服務(wù)端[消息的發(fā)起者])柿冲;

  • APNS:Apple Push Notification Service[蘋果消息推送服務(wù)器]茬高;

  • iPhone:用來接收APNS下發(fā)下來的消息;

  • Client App:iOS設(shè)備上的應(yīng)用程序假抄,用來接收iphone傳遞APNS下發(fā)的消息到制定的一個(gè)客戶端 app[消息的最終響應(yīng)者]怎栽;

推送通知可以分為三個(gè)階段

階段一:Provider[服務(wù)端]把要發(fā)送的消息,目的iOS設(shè)備標(biāo)識(shí)打包宿饱,發(fā)送給APNS熏瞄;

階段二:APNS在自身的已注冊(cè)Push服務(wù)的iOS設(shè)備列表中,查找有相應(yīng)標(biāo)識(shí)的iOS設(shè)備谬以,并將消息發(fā)送到iOS設(shè)備强饮;

階段三:iOS設(shè)備把發(fā)送的消息傳遞給對(duì)應(yīng)的應(yīng)用程序,并且按照設(shè)定彈出Push通知为黎。

具體過程邮丰,如下圖

屏幕快照 2016-03-26 下午8.43.39.png

1行您、[Client App]注冊(cè)消息推送;

  • 只有注冊(cè)過的應(yīng)用才有可能接收到消息剪廉,程序中通常通過UIApplication的registerUserNotificationSettings:方法注冊(cè)娃循,iOS8中通知注冊(cè)的方法發(fā)生了改變,如果是iOS7及之前版本的iOS請(qǐng)參考其他代碼斗蒋。

  • 注冊(cè)之前有兩個(gè)前提條件必須準(zhǔn)備好:開發(fā)配置文件(provisioning profile捌斧,也就是.mobileprovision后綴的文件)的App ID不能使用通配ID必須使用指定APP ID并且生成配置文件中選擇Push Notifications服務(wù),一般的開發(fā)配置文件無法完成注冊(cè)泉沾;應(yīng)用程序的Bundle Identifier必須和生成配置文件使用的APP ID完全一致捞蚂。

2、[Client App]跟[APNS Service]要deviceToken, Client App接收到APNs 分配的deviceToken

  • 在UIApplication的-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken代理方法中獲取令牌爆哑,此方法發(fā)生在注冊(cè)之后洞难。

  • 如果無法正確獲得device token可以在UIApplication的-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error代理方法中查看詳細(xì)錯(cuò)誤信息舆吮,此方法發(fā)生在獲取device token失敗之后揭朝。

  • 必須真機(jī)調(diào)試,模擬器無法獲取device token色冀。

3潭袱、[Client App]將deviceToken發(fā)送給[Provider]Push服務(wù)端程序,告訴服務(wù)器端當(dāng)前設(shè)備允許接收消息。

  • device token的生成算法只有Apple掌握锋恬,為了確保算法發(fā)生變化后仍然能夠正常接收服務(wù)器端發(fā)送的通知屯换,每次應(yīng)用程序啟動(dòng)都重新獲得device token(device token的獲取不會(huì)造成性能問題,蘋果官方已經(jīng)做過優(yōu)化)与学。

  • 通惩冢可以創(chuàng)建一個(gè)網(wǎng)絡(luò)連接發(fā)送給應(yīng)用程序提供商的服務(wù)器端, 在這個(gè)過程中最好將上一次獲得的device token存儲(chǔ)起來索守,避免重復(fù)發(fā)送晕窑,一旦發(fā)現(xiàn)device token發(fā)生了變化最好將原有的device token一塊發(fā)送給服務(wù)器端,服務(wù)器端刪除原有令牌存儲(chǔ)新令牌避免服務(wù)器端發(fā)送無效消息卵佛。

4杨赤、當(dāng)Push服務(wù)端程序滿足發(fā)送消息條件了,[Provider]向[APNS Service]發(fā)送消息截汪;

  • 發(fā)送時(shí)指定device token和消息內(nèi)容疾牲,并且完全按照蘋果官方的消息格式組織消息內(nèi)容,通常情況下可以借助其他第三方消息推送框架來完成衙解。

5阳柔、[APNS Service]根據(jù)消息中的device token查找已注冊(cè)的設(shè)備推送消息,將消息發(fā)送給[Client App].

  • 正常情況下可以根據(jù)device token將消息成功推送到客戶端設(shè)備中,但是也不排除用戶卸載程序的情況蚓峦,此時(shí)推送消息失敗舌剂,APNs會(huì)將這個(gè)錯(cuò)誤消息通知服務(wù)器端以避免資源浪費(fèi)(服務(wù)器端此時(shí)可以根據(jù)錯(cuò)誤刪除已經(jīng)存儲(chǔ)的device token医咨,下次不再發(fā)送)

推送通知的流程代碼:

//
//  AppDelegate.m
//  pushnotification
//

#import "AppDelegate.h"
#import "LTYViewController.h"

@interface AppDelegate ()

@end

@implementation AppDelegate

#pragma mark - 應(yīng)用程序代理方法
#pragma mark 應(yīng)用程序啟動(dòng)之后
- (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];
    
    //設(shè)置全局導(dǎo)航條風(fēng)格和顏色
    [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:23/255.0 green:180/255.0 blue:237/255.0 alpha:1]];
    [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
    
    LTYViewController *mainController=[[LTYViewController alloc]init];
    _window.rootViewController=mainController;
    
    [_window makeKeyAndVisible];
    
    //注冊(cè)推送通知(注意iOS8注冊(cè)方法發(fā)生了變化)
    [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
    [application registerForRemoteNotifications];
    
    return YES;
}
#pragma mark 注冊(cè)推送通知之后
//在此接收設(shè)備令牌
-(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 - 私有方法
/**
 *  添加設(shè)備令牌到服務(wù)器端
 *
 *  @param deviceToken 設(shè)備令牌
 */
-(void)addDeviceToken:(NSData *)deviceToken{
    NSString *key=@"DeviceToken";
    NSData *oldToken= [[NSUserDefaults standardUserDefaults] objectForKey:key];
    //如果偏好設(shè)置中的已存儲(chǔ)設(shè)備令牌和新獲取的令牌不同則存儲(chǔ)新令牌并且發(fā)送給服務(wù)器端
    if (![oldToken isEqualToData:deviceToken]) {
        [[NSUserDefaults standardUserDefaults] setObject:deviceToken forKey:key];
        [self sendDeviceTokenWidthOldDeviceToken:oldToken newDeviceToken:deviceToken];
    }
}

-(void)sendDeviceTokenWidthOldDeviceToken:(NSData *)oldToken newDeviceToken:(NSData *)newToken{
    //注意一定確保真機(jī)可以正常訪問下面的地址
    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客戶端代碼的代碼比較簡(jiǎn)單,注冊(cè)推送通知架诞,獲取device token存儲(chǔ)到偏好設(shè)置中拟淮,并且如果新獲取的device token不同于偏好設(shè)置中存儲(chǔ)的數(shù)據(jù)則發(fā)送給服務(wù)器端,更新服務(wù)器端device token列表谴忧。
  • 由于device token需要發(fā)送給服務(wù)器端很泊,這里使用一個(gè)Web應(yīng)用作為服務(wù)器端接收device token,這里使用了ASP.NET程序來處理令牌接收注冊(cè)工作沾谓,當(dāng)然你使用其他技術(shù)同樣沒有問題委造。下面是對(duì)應(yīng)的后臺(tái)代碼:
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 = "";
                //如果傳遞舊的設(shè)備令牌則刪除舊令牌添加新令牌
                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("注冊(cè)成功!");
            }
            catch(Exception ex)
            {
                Response.Write("注冊(cè)失敗均驶,錯(cuò)誤詳情:"+ex.ToString());
            }
        }
    }
}

這個(gè)過程主要就是保存device token到數(shù)據(jù)庫中昏兆,當(dāng)然如果同時(shí)傳遞舊的設(shè)備令牌還需要先刪除就的設(shè)備令牌,這里簡(jiǎn)單的在數(shù)據(jù)庫中創(chuàng)建了一張Device表來保存設(shè)備令牌妇穴,其中記錄了應(yīng)用程序Id和設(shè)備令牌爬虱。

  • 第三步就是服務(wù)器端發(fā)送消息,如果要給APNs發(fā)送消息就必須按照Apple的標(biāo)準(zhǔn)消息格式組織消息內(nèi)容腾它。但是好在目前已經(jīng)有很多開源的第三方類庫供我們使用跑筝,具體消息如何包裝完全不用自己組織,這里使用一個(gè)開源的類庫Push Sharp來給APNs發(fā)送消息 ,除了可以給Apple設(shè)備推送消息瞒滴,Push Sharp還支持Android曲梗、Windows Phone等多種設(shè)備,更多詳細(xì)內(nèi)容大家可以參照官方說明妓忍。前面說過如果要開發(fā)消息推送應(yīng)用不能使用一般的開發(fā)配置文件虏两,這里還需要注意:如果服務(wù)器端要給APNs發(fā)送消息其秘鑰也必須是通過APNs Development iOS類型的證書來導(dǎo)出的,一般的iOS Development 類型的證書導(dǎo)出的秘鑰無法用作服務(wù)器端發(fā)送秘鑰世剖。下面通過在一個(gè)簡(jiǎn)單的WinForm程序中調(diào)用Push Sharp給APNs發(fā)送消息定罢,這里讀取之前Device表中的所有設(shè)備令牌循環(huán)發(fā)送消息:
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<string> deviceTokens = GetDeviceToken();
            SendMessage(deviceTokens, tbMessage.Text);
        }

        #region 發(fā)送消息
        /// <summary>
        /// 取得所有設(shè)備令牌
        /// </summary>
        /// <returns>設(shè)備令牌</returns>
        private List<string> GetDeviceToken()
        {
            List<string> deviceTokens = new List<string>();
            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;
        }
        
        /// <summary>
        /// 發(fā)送消息
        /// </summary>
        /// <param name="deviceToken">設(shè)備令牌</param>
        /// <param name="message">消息內(nèi)容</param>
        private void SendMessage(List<string> deviceToken, string message)
        {
            //創(chuàng)建推送對(duì)象
            var pusher = new PushBroker();
            pusher.OnNotificationSent += pusher_OnNotificationSent;//發(fā)送成功事件
            pusher.OnNotificationFailed += pusher_OnNotificationFailed;//發(fā)送失敗事件
            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;
            //注冊(cè)推送服務(wù)
            byte[] certificateData = File.ReadAllBytes(@"E:\KenshinCui_Push.p12");
            pusher.RegisterAppleService(new ApplePushChannelSettings(certificateData, "123"));
            foreach (string token in deviceToken)
            {
                //給指定設(shè)備發(fā)送消息
                pusher.QueueNotification(new AppleNotification()
                    .ForDeviceToken(token)
                    .WithAlert(message) 
                    .WithBadge(1)
                    .WithSound("default"));
            }
        }

        void pusher_OnServiceException(object sender, Exception error)
        {
            Console.WriteLine("消息發(fā)送失敗,錯(cuò)誤詳情:" + error.ToString());
            PersonalizeMessageBox.Show(this, "消息發(fā)送失敗搁廓,錯(cuò)誤詳情:" + error.ToString(), "系統(tǒng)提示");
        }

        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("消息發(fā)送失敗引颈,錯(cuò)誤詳情:" + error.ToString());
            PersonalizeMessageBox.Show(this, "消息發(fā)送失敗,錯(cuò)誤詳情:" + error.ToString(), "系統(tǒng)提示");
        }

        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("消息發(fā)送失敗境蜕,錯(cuò)誤詳情:" + error.ToString());
            PersonalizeMessageBox.Show(this, "消息發(fā)送失敗蝙场,錯(cuò)誤詳情:"+error.ToString(), "系統(tǒng)提示");
        }

        void pusher_OnNotificationSent(object sender, PushSharp.Core.INotification notification)
        {
            Console.WriteLine("消息發(fā)送成功!");
            PersonalizeMessageBox.Show(this, "消息發(fā)送成功粱年!", "系統(tǒng)提示");
        }
        #endregion
    }
}

服務(wù)器端消息發(fā)送應(yīng)用運(yùn)行效果:

170829115313261.png

iOS客戶端接收的消息的效果:

170829135941093.png

到目前為止通過服務(wù)器端應(yīng)用可以順利發(fā)送消息給APNs并且iOS應(yīng)用已經(jīng)成功接收推送消息售滤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子完箩,更是在濱河造成了極大的恐慌赐俗,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弊知,死亡現(xiàn)場(chǎng)離奇詭異阻逮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)秩彤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門叔扼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人漫雷,你說我怎么就攤上這事瓜富。” “怎么了降盹?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵与柑,是天一觀的道長。 經(jīng)常有香客問我蓄坏,道長价捧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任剑辫,我火速辦了婚禮干旧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妹蔽。我一直安慰自己,他們只是感情好挠将,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布胳岂。 她就那樣靜靜地躺著,像睡著了一般舔稀。 火紅的嫁衣襯著肌膚如雪乳丰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天内贮,我揣著相機(jī)與錄音产园,去河邊找鬼。 笑死夜郁,一個(gè)胖子當(dāng)著我的面吹牛什燕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播竞端,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼屎即,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起技俐,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤乘陪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后雕擂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體啡邑,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年井赌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谣拣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡族展,死狀恐怖森缠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仪缸,我是刑警寧澤贵涵,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站恰画,受9級(jí)特大地震影響宾茂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拴还,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一跨晴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧片林,春花似錦端盆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至弓摘,卻和暖如春焚鹊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背韧献。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國打工末患, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锤窑。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓璧针,卻偏偏與公主長得像,于是被迫代替她去往敵國和親果复。 傳聞我的和親對(duì)象是個(gè)殘疾皇子陈莽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 概述 在多數(shù)移動(dòng)應(yīng)用中任何時(shí)候都只能有一個(gè)應(yīng)用程序處于活躍狀態(tài),如果其他應(yīng)用此刻發(fā)生了一些用戶感興趣的那么通過通知...
    莫離_焱閱讀 6,511評(píng)論 1 8
  • 來源:崔江濤的博客 概述在多數(shù)移動(dòng)應(yīng)用中任何時(shí)候都只能有一個(gè)應(yīng)用程序處于活躍狀態(tài),如果其他應(yīng)用此刻發(fā)生了一些用戶感...
    李棲桐閱讀 924評(píng)論 0 0
  • ![ 1.應(yīng)用程序注冊(cè)APNs推送消息走搁。說明:a.只有注冊(cè)過的應(yīng)用才有可能接收到消息独柑,程序中通常通過UIAppli...
    Crazy2015閱讀 507評(píng)論 0 2
  • 說在前面 iOS中的推送分為:Local Notification(本地消息通知)和Remote Notifica...
    UncleChen閱讀 1,767評(píng)論 0 5
  • 明天你是否會(huì)想起 昨天你寫的日記 明天你是否會(huì)想起 曾經(jīng)年少的你 走過學(xué)校旁的老街道,熟悉的歌詞再次抨擊著我那脆弱...
    何小白子閱讀 255評(píng)論 0 4