APP后臺(tái)下載

iOS的多任務(wù)是在iOS4的時(shí)候被引入的精耐,在此之前iOS的app都是按下Home鍵就被干掉了狼速。iOS4雖然引入了后臺(tái)和多任務(wù),但是實(shí)際上是偽多任務(wù)卦停,一般的app后臺(tái)并不能執(zhí)行自己的代碼向胡,只有少數(shù)幾類(lèi)服務(wù)在通過(guò)注冊(cè)后可以真正在后臺(tái)運(yùn)行,并且在提交到AppStore的時(shí)候也會(huì)被嚴(yán)格審核是否有越權(quán)行為惊完,這種限制主要是出于對(duì)于設(shè)備的續(xù)航和安全兩方面進(jìn)行的考慮僵芹。之后經(jīng)過(guò)iOS5和6的逐漸發(fā)展,后臺(tái)能運(yùn)行的服務(wù)的種類(lèi)雖然出現(xiàn)了增加小槐,但是iOS后臺(tái)的本質(zhì)并沒(méi)有變化拇派。在iOS7之前,系統(tǒng)所接受的應(yīng)用多任務(wù)可以大致分為幾種:

后臺(tái)完成某些花費(fèi)時(shí)間的特定任務(wù)

后臺(tái)播放音樂(lè)等

位置服務(wù)

IP電話(huà)(VoIP)

Newsstand

在WWDC 2013的keynote上,iOS7的后臺(tái)多任務(wù)改進(jìn)被專(zhuān)門(mén)拿出來(lái)向開(kāi)發(fā)者進(jìn)行了介紹件豌,到底iOS7里多任務(wù)方面有什么新的特性可以利用疮方,如何使用呢?簡(jiǎn)單來(lái)說(shuō)茧彤,iOS7在后臺(tái)特性方面有很大改進(jìn)骡显,不僅改變了以往的一些后臺(tái)任務(wù)處理方式,還加入了全新的后臺(tái)模式棘街,本文將針對(duì)iOS7中新的后臺(tái)特性進(jìn)行一些學(xué)習(xí)和記錄蟆盐。大體來(lái)說(shuō),iOS7后臺(tái)的變化在于以下四點(diǎn):

改變了后臺(tái)任務(wù)的運(yùn)行方式

增加了后臺(tái)獲仍庋场(Background Fetch)

增加了推送喚醒(靜默推送石挂,Silent Remote Notifications)

增加了后臺(tái)傳輸(Background Transfer Service)

iOS7的多任務(wù)

后臺(tái)任務(wù)

首先看看后臺(tái)任務(wù)的變化,先說(shuō)這方面的改變险污,而不是直接介紹新的API痹愚,是因?yàn)檫@個(gè)改變很典型地代表了iOS7在后臺(tái)任務(wù)管理和能耗控制上的大體思路。從上古時(shí)期開(kāi)始(其實(shí)也就4.0)蛔糯,UIApplication提供了-beginBackgroundTaskWithExpirationHandler:方法來(lái)使app在被切到后臺(tái)后仍然能保持運(yùn)行一段時(shí)間拯腮,app可以用這個(gè)方法來(lái)確保一些很重很慢的工作可以在急不可耐的用戶(hù)將你的應(yīng)用扔到后臺(tái)后還能完成,比如編碼視頻蚁飒,上傳下載某些重要文件或者是完成某些數(shù)據(jù)庫(kù)操作等动壤,雖然時(shí)間不長(zhǎng),但在大多數(shù)情況下勉強(qiáng)夠用淮逻。如果你之前沒(méi)有使用過(guò)這個(gè)API的話(huà)琼懊,它使用起來(lái)大概是長(zhǎng)這個(gè)樣子的:

- (void) doUpdate

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[self beginBackgroundUpdateTask];

NSURLResponse * response = nil;

NSError? * error = nil;

NSData * responseData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];

// Do something with the result

[self endBackgroundUpdateTask];

});

}

- (void) beginBackgroundUpdateTask

{

self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

[self endBackgroundUpdateTask];

}];

}

- (void) endBackgroundUpdateTask

{

[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];

self.backgroundUpdateTask = UIBackgroundTaskInvalid;

}

在beginBackgroundTaskWithExpirationHandler:里寫(xiě)一個(gè)超時(shí)處理(系統(tǒng)只給app分配了一定時(shí)間來(lái)進(jìn)行后臺(tái)任務(wù),超時(shí)之前會(huì)調(diào)用這個(gè)block)爬早,然后進(jìn)行開(kāi)始進(jìn)行后臺(tái)任務(wù)處理哼丈,在任務(wù)結(jié)束或者過(guò)期的時(shí)候call一下endBackgroundTask:使之與begin方法配對(duì)(否則你的app在后臺(tái)任務(wù)超時(shí)的時(shí)候會(huì)被殺掉)。同時(shí)筛严,你可以使用UIApplication實(shí)例的backgroundTimeRemaining屬性來(lái)獲取剩余的后臺(tái)執(zhí)行時(shí)間醉旦。

具體的執(zhí)行時(shí)間來(lái)說(shuō),在iOS6和之前的系統(tǒng)中桨啃,系統(tǒng)在用戶(hù)退出應(yīng)用后车胡,如果應(yīng)用正在執(zhí)行后臺(tái)任務(wù)的話(huà),系統(tǒng)會(huì)保持活躍狀態(tài)直到后臺(tái)任務(wù)完成或者是超時(shí)以后照瘾,才會(huì)進(jìn)入真正的低功耗休眠狀態(tài)吨拍。

iOS6之前的后臺(tái)任務(wù)處理

而在iOS7中,后臺(tái)任務(wù)的處理方式發(fā)生了改變网杆。系統(tǒng)將在用戶(hù)鎖屏后盡快讓設(shè)備進(jìn)入休眠狀態(tài)羹饰,以節(jié)省電力伊滋,這時(shí)后臺(tái)任務(wù)是被暫停的。之后在設(shè)備在特定時(shí)間進(jìn)行系統(tǒng)應(yīng)用的操作被喚醒(比如檢查郵件或者接到來(lái)電等)時(shí)队秩,之前暫停的后臺(tái)任務(wù)將一起進(jìn)行笑旺。就是說(shuō),系統(tǒng)不會(huì)專(zhuān)門(mén)為第三方的應(yīng)用保持設(shè)備處于活動(dòng)狀態(tài)馍资。如下圖示

iOS7的后臺(tái)任務(wù)處理

這個(gè)變化在不減少應(yīng)用的后臺(tái)任務(wù)時(shí)間長(zhǎng)度的情況下筒主,給設(shè)備帶來(lái)了更多的休眠時(shí)間,從而延長(zhǎng)了續(xù)航鸟蟹。對(duì)于開(kāi)發(fā)者來(lái)說(shuō)乌妙,這個(gè)改變更多的是系統(tǒng)層級(jí)的變化,對(duì)于非網(wǎng)絡(luò)傳輸?shù)娜蝿?wù)來(lái)說(shuō)建钥,保持原來(lái)的用法即可藤韵,新系統(tǒng)將會(huì)按照新的喚醒方式進(jìn)行處理;而對(duì)于原來(lái)在后臺(tái)做網(wǎng)絡(luò)傳輸?shù)膽?yīng)用來(lái)說(shuō)熊经,蘋(píng)果建議在iOS7中使用NSURLSession泽艘,創(chuàng)建后臺(tái)的session并進(jìn)行網(wǎng)絡(luò)傳輸,這樣可以很容易地利用更好的后臺(tái)傳輸API镐依,而不必受限于原來(lái)的時(shí)長(zhǎng)匹涮,關(guān)于這個(gè)具體的我們一會(huì)兒再說(shuō)。

后臺(tái)獲然笨恰(Background Fetch)

現(xiàn)在的應(yīng)用無(wú)法在后臺(tái)獲取信息然低,比如社交類(lèi)應(yīng)用,用戶(hù)一定需要在打開(kāi)應(yīng)用之后才能進(jìn)行網(wǎng)絡(luò)連接务唐,獲取新的消息條目雳攘,然后才能將新內(nèi)容呈現(xiàn)給用戶(hù)。說(shuō)實(shí)話(huà)這個(gè)體驗(yàn)并不是很好绍哎,用戶(hù)在打開(kāi)應(yīng)用后必定會(huì)有一段時(shí)間的等待来农,每次皆是如此鞋真。iOS7中新加入的后臺(tái)獲取就是用來(lái)解決這個(gè)不足的:后臺(tái)獲取干的事情就是在用戶(hù)打開(kāi)應(yīng)用之前就使app有機(jī)會(huì)執(zhí)行代碼來(lái)獲取數(shù)據(jù)崇堰,刷新UI。這樣在用戶(hù)打開(kāi)應(yīng)用的時(shí)候涩咖,最新的內(nèi)容將已然呈現(xiàn)在用戶(hù)眼前海诲,而省去了所有的加載過(guò)程。想想看檩互,沒(méi)有加載的網(wǎng)絡(luò)體驗(yàn)的世界特幔,會(huì)是怎樣一種感覺(jué)。這已經(jīng)不是smooth闸昨,而是真的amazing了蚯斯。

那具體應(yīng)該怎么做呢薄风?一步一步來(lái):

啟用后臺(tái)獲取

首先是修改應(yīng)用的Info.plist,在UIBackgroundModes中加入fetch拍嵌,即可告訴系統(tǒng)應(yīng)用需要后臺(tái)獲取的權(quán)限遭赂。另外一種更簡(jiǎn)單的方式,得益于Xcode5的Capabilities特性(參見(jiàn)可以參見(jiàn)我之前的一篇WWDC2013筆記 Xcode5和ObjC新特性)横辆,現(xiàn)在甚至都不需要去手動(dòng)修改Info.plist來(lái)進(jìn)行添加了撇他,打開(kāi)Capabilities頁(yè)面下的Background Modes選項(xiàng),并勾選Background fetch選項(xiàng)即可(如下圖)狈蚤。

在Capabilities中開(kāi)啟Background Modes

筆者寫(xiě)這篇文章的時(shí)候iOS7還沒(méi)有上市困肩,也沒(méi)有相關(guān)的審核資料,所以不知道如果只是在這里打開(kāi)了fetch選項(xiàng),但卻沒(méi)有實(shí)現(xiàn)的話(huà)枷畏,應(yīng)用會(huì)不會(huì)無(wú)法通過(guò)審核叉跛。但是依照蘋(píng)果一貫的做法來(lái)看,如果聲明了需要某項(xiàng)后臺(tái)權(quán)限蹋绽,但是結(jié)果卻沒(méi)有相關(guān)實(shí)現(xiàn)的話(huà),被拒掉的可能性還是比較大的筋蓖。因此大家盡量不要拿上線產(chǎn)品進(jìn)行實(shí)驗(yàn)卸耘,而應(yīng)當(dāng)是在demo項(xiàng)目里研究明白以后再到上線產(chǎn)品中進(jìn)行實(shí)裝。

設(shè)定獲取間隔

對(duì)應(yīng)用的UIApplication實(shí)例設(shè)置獲取間隔粘咖,一般在應(yīng)用啟動(dòng)的時(shí)候調(diào)用以下代碼即可:

[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];

如果不對(duì)最小后臺(tái)獲取間隔進(jìn)行設(shè)定的話(huà)蚣抗,系統(tǒng)將使用默認(rèn)值UIApplicationBackgroundFetchIntervalNever,也就是永遠(yuǎn)不進(jìn)行后臺(tái)獲取瓮下。當(dāng)然翰铡,-setMinimumBackgroundFetchInterval:方法接受的是NSTimeInterval,因此你也可以手動(dòng)指定一個(gè)以秒為單位的最小獲取間隔讽坏。需要注意的是锭魔,我們都已經(jīng)知道iOS是一個(gè)非常霸道為我獨(dú)尊的系統(tǒng),因此自然也不可能讓一介區(qū)區(qū)第三方應(yīng)用來(lái)控制系統(tǒng)行為路呜。這里所指定的時(shí)間間隔只是代表了“在上一次獲取或者關(guān)閉應(yīng)用之后迷捧,在這一段時(shí)間內(nèi)一定不會(huì)去做后臺(tái)獲取”,而真正具體到什么時(shí)候會(huì)進(jìn)行后臺(tái)獲取胀葱,那~完全是要看系統(tǒng)娘的心情的~我們是無(wú)從得知的漠秋。系統(tǒng)將根據(jù)你的設(shè)定,選擇比如接收郵件的時(shí)候順便為你的應(yīng)用獲取一下抵屿,或者也有可能專(zhuān)門(mén)為你的應(yīng)用喚醒一下設(shè)備庆锦。作為開(kāi)發(fā)者,我們應(yīng)該做的是為用戶(hù)的電池考慮轧葛,盡可能地選擇合適自己應(yīng)用的后臺(tái)獲取間隔搂抒。設(shè)置為UIApplicationBackgroundFetchIntervalMinimum的話(huà)艇搀,系統(tǒng)會(huì)盡可能多盡可能快地為你的應(yīng)用進(jìn)行后臺(tái)獲取,但是比如對(duì)于一個(gè)天氣應(yīng)用求晶,可能對(duì)實(shí)時(shí)的數(shù)據(jù)并不會(huì)那么關(guān)心中符,就完全不必設(shè)置為UIApplicationBackgroundFetchIntervalMinimum,也許1小時(shí)會(huì)是一個(gè)更好的選擇誉帅。新的Mac OSX 10.9上已經(jīng)出現(xiàn)了功耗監(jiān)測(cè)淀散,用于讓用戶(hù)確定什么應(yīng)用是能耗大戶(hù),有理由相信同樣的東西也可能出現(xiàn)在iOS上蚜锨。如果不想讓用戶(hù)因?yàn)槟愕膽?yīng)用是耗電大戶(hù)而怒刪的話(huà)档插,從現(xiàn)在開(kāi)始注意一下應(yīng)用的能耗還是蠻有必要的(做綠色環(huán)保低碳的iOS app,從今天開(kāi)始~)亚再。

實(shí)現(xiàn)后臺(tái)獲取代碼并通知系統(tǒng)

在完成了前兩步后郭膛,只需要在AppDelegate里實(shí)現(xiàn)-application:performFetchWithCompletionHandler:就行了。系統(tǒng)將會(huì)在執(zhí)行fetch的時(shí)候調(diào)用這個(gè)方法氛悬,然后開(kāi)發(fā)者需要做的是在這個(gè)方法里完成獲取的工作则剃,然后刷新UI,并通知系統(tǒng)獲取結(jié)束如捅,以便系統(tǒng)盡快回到休眠狀態(tài)棍现。獲取數(shù)據(jù)這是應(yīng)用相關(guān)的內(nèi)容,在此不做贅述镜遣,應(yīng)用在前臺(tái)能完成的工作在這里都能做己肮,唯一的限制是系統(tǒng)不會(huì)給你很長(zhǎng)時(shí)間來(lái)做fetch,一般會(huì)小于一分鐘悲关,而且fetch在絕大多數(shù)情況下將和別的應(yīng)用共用網(wǎng)絡(luò)連接谎僻。這些時(shí)間對(duì)于fetch一些簡(jiǎn)單數(shù)據(jù)來(lái)說(shuō)是足夠的了,比如微博的新條目(大圖除外)寓辱,接下來(lái)一小時(shí)的天氣情況等艘绍。如果涉及到較大文件的傳輸?shù)脑?huà),用后臺(tái)獲取的API就不合適了秫筏,而應(yīng)該使用另一個(gè)新的文件傳輸?shù)腁PI诱鞠,我們稍后再說(shuō)。類(lèi)似前面提到的后臺(tái)任務(wù)完成時(shí)必須通知系統(tǒng)一樣跳昼,在在獲取完成后般甲,也必須通知系統(tǒng)獲取完成肋乍,方法是調(diào)用-application:performFetchWithCompletionHandler:的handler鹅颊。這個(gè)CompletionHandler接收一個(gè)UIBackgroundFetchResult作為參數(shù),可供選擇的結(jié)果有UIBackgroundFetchResultNewData,UIBackgroundFetchResultNoData,UIBackgroundFetchResultFailed三種墓造,分別表示獲取到了新數(shù)據(jù)(此時(shí)系統(tǒng)將對(duì)現(xiàn)在的UI狀態(tài)截圖并更新App Switcher中你的應(yīng)用的截屏)堪伍,沒(méi)有新數(shù)據(jù)锚烦,以及獲取失敗。寫(xiě)一個(gè)簡(jiǎn)單的例子吧:

//File: YourAppDelegate.m

-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler

{

UINavigationController *navigationController = (UINavigationController*)self.window.rootViewController;

id fetchViewController = navigationController.topViewController;

if ([fetchViewController respondsToSelector:@selector(fetchDataResult:)]) {

[fetchViewController fetchDataResult:^(NSError *error, NSArray *results){

if (!error) {

if (results.count != 0) {

//Update UI with results.

//Tell system all done.

completionHandler(UIBackgroundFetchResultNewData);

} else {

completionHandler(UIBackgroundFetchResultNoData);

}

} else {

completionHandler(UIBackgroundFetchResultFailed);

}

}];

} else {

completionHandler(UIBackgroundFetchResultFailed);

}

}

當(dāng)然帝雇,實(shí)際情況中會(huì)比這要復(fù)雜得多涮俄,用戶(hù)當(dāng)前的ViewController是否合適做獲取,獲取后的數(shù)據(jù)如何處理都需要考慮尸闸。另外要說(shuō)明的是上面的代碼在獲取成功后直接在appDelegate里更新UI彻亲,這只是為了能在同一處進(jìn)行說(shuō)明,但卻是不正確的結(jié)構(gòu)吮廉。比較好的做法是將獲取和更新UI的業(yè)務(wù)邏輯都放到fetchViewController里苞尝,然后向其發(fā)送獲取消息的時(shí)候?qū)ompletionHandler作為參數(shù)傳入,并在fetchViewController里完成獲取結(jié)束的報(bào)告宦芦。

另一個(gè)比較神奇的地方是系統(tǒng)將追蹤用戶(hù)的使用習(xí)慣宙址,并根據(jù)對(duì)每個(gè)應(yīng)用的使用時(shí)刻給一個(gè)合理的fetch時(shí)間。比如系統(tǒng)將記錄你在每天早上9點(diǎn)上班的電車(chē)上调卑,中午12點(diǎn)半吃飯時(shí)抡砂,以及22點(diǎn)睡覺(jué)前會(huì)刷一下微博,只要這個(gè)習(xí)慣持續(xù)個(gè)三四天恬涧,系統(tǒng)便會(huì)將應(yīng)用的后臺(tái)獲取時(shí)刻調(diào)節(jié)為9點(diǎn)注益,12點(diǎn)和22點(diǎn)前一點(diǎn)。這樣在每次你打開(kāi)應(yīng)用都直接有最新內(nèi)容的同時(shí)溯捆,也節(jié)省了電量和流量聊浅。

后臺(tái)獲取的調(diào)試

既然是系統(tǒng)決定的fetch,那我們要如何測(cè)試寫(xiě)的代碼呢现使?難道是將應(yīng)用退到后臺(tái)低匙,然后安心等待系統(tǒng)進(jìn)行后臺(tái)獲取么?當(dāng)然不是...Xcode5為我們提供了兩種方法來(lái)測(cè)試后臺(tái)獲取的代碼碳锈。一種是從后臺(tái)獲取中啟動(dòng)應(yīng)用顽冶,另一種是當(dāng)應(yīng)用在后臺(tái)時(shí)模擬一次后臺(tái)推送。

對(duì)于前者售碳,我們可以新建一個(gè)Scheme來(lái)專(zhuān)門(mén)調(diào)試從后臺(tái)啟動(dòng)强重。點(diǎn)擊Xcode5的Product->Scheme->Edit Scheme(或者直接使用快捷鍵?<)。在編輯Scheme的窗口中點(diǎn)Duplicate Scheme按鈕復(fù)制一個(gè)當(dāng)前方案贸人,然后在新Scheme的option中將Background Fetch打上勾间景。從這個(gè)Scheme來(lái)運(yùn)行應(yīng)用的時(shí)候,應(yīng)用將不會(huì)直接啟動(dòng)切入前臺(tái)艺智,而是調(diào)用后臺(tái)獲取部分代碼并更新UI倘要,這樣再點(diǎn)擊圖標(biāo)進(jìn)入應(yīng)用時(shí),你應(yīng)該可以看到最新的數(shù)據(jù)和更新好的UI了十拣。

更改Scheme的選項(xiàng)為從后臺(tái)獲取事件中啟動(dòng)

另一種是當(dāng)應(yīng)用在后臺(tái)時(shí)封拧,模擬一次后臺(tái)獲取志鹃。這個(gè)比較簡(jiǎn)單,在app調(diào)試運(yùn)行時(shí)泽西,點(diǎn)擊Xcode5的Debug菜單中的Simulate Background Fetch曹铃,即可模擬完成一次獲取調(diào)用。

推送喚醒(Remote Notifications)

遠(yuǎn)程推送(Remote Push Notifications)可以說(shuō)是增加用戶(hù)留存率的不二法則捧杉,在iOS6和之前陕见,推送的類(lèi)型是很單一的,無(wú)非就是顯示標(biāo)題內(nèi)容味抖,指定聲音等淳玩。用戶(hù)通過(guò)解鎖進(jìn)入你的應(yīng)用后,appDelegate中通過(guò)推送打開(kāi)應(yīng)用的回調(diào)將被調(diào)用非竿,然后你再獲取數(shù)據(jù)蜕着,進(jìn)行顯示。這和沒(méi)有后臺(tái)獲取時(shí)的打開(kāi)應(yīng)用后再獲取數(shù)據(jù)刷新的問(wèn)題是一樣的红柱。在iOS7中這個(gè)行為發(fā)生了一些改變承匣,我們有機(jī)會(huì)使設(shè)備在接收到遠(yuǎn)端推送后讓系統(tǒng)喚醒設(shè)備和我們的后臺(tái)應(yīng)用,并先執(zhí)行一段代碼來(lái)準(zhǔn)備數(shù)據(jù)和UI锤悄,然后再提示用戶(hù)有推送韧骗。這時(shí)用戶(hù)如果解鎖設(shè)備進(jìn)入應(yīng)用后將不會(huì)再有任何加載過(guò)程,新的內(nèi)容將直接得到呈現(xiàn)零聚。

實(shí)裝的方法和剛才的后臺(tái)獲取比較類(lèi)似袍暴,還是一步步來(lái):

啟用推送喚醒

和上面的后臺(tái)獲取類(lèi)似,更改Info.plist隶症,在UIBackgroundModes下加入remote-notification即可開(kāi)啟政模,當(dāng)然同樣的更簡(jiǎn)單直接的辦法是使用Capabilities。

更改推送的payload

在iOS7中蚂会,如果想要使用推送來(lái)喚醒應(yīng)用運(yùn)行代碼的話(huà)淋样,需要在payload中加入content-available,并設(shè)置為1胁住。

aps {

content-available: 1

alert: {...}

}

實(shí)現(xiàn)推送喚醒代碼并通知系統(tǒng)

最后在appDelegate中實(shí)現(xiàn)-application:didReceiveRemoteNotification:fetchCompletionHandle:趁猴。這部分內(nèi)容和上面的后臺(tái)獲取部分完全一樣,在此不再重復(fù)彪见。

一些限制和應(yīng)用的例子

因?yàn)橐坏┩扑统晒芩荆脩?hù)的設(shè)備將被喚醒,因此這類(lèi)推送不可能不受到限制余指。Apple將限制此類(lèi)推送的頻率捕犬,當(dāng)頻率超過(guò)一定限制后,帶有content-available標(biāo)志的推送將會(huì)被阻塞,以保證用戶(hù)設(shè)備不被頻繁喚醒或听。按照Apple的說(shuō)法,這個(gè)頻率在一小時(shí)內(nèi)個(gè)位數(shù)次的推送的話(huà)不會(huì)有太大問(wèn)題笋婿。

Apple給出了幾個(gè)典型的應(yīng)用情景誉裆,比如一個(gè)電視節(jié)目類(lèi)的應(yīng)用,當(dāng)用戶(hù)標(biāo)記某些劇目為喜愛(ài)時(shí)缸濒,當(dāng)這些劇有更新時(shí)足丢,可以給用戶(hù)發(fā)送靜默的喚醒推送通知,客戶(hù)端在接到通知后檢查更新并開(kāi)始后臺(tái)下載(注意后臺(tái)下載的部分絕對(duì)不應(yīng)該在推送回調(diào)中做庇配,而是應(yīng)該使用新的后臺(tái)傳輸服務(wù)斩跌,后面詳細(xì)介紹)。下載完成后發(fā)送一個(gè)本地推送告知用戶(hù)新的內(nèi)容已經(jīng)準(zhǔn)備完畢捞慌。這樣在用戶(hù)注意到推送并打開(kāi)應(yīng)用的時(shí)候耀鸦,所有必要的內(nèi)容已經(jīng)下載完畢,UI也將切換至用戶(hù)喜愛(ài)的劇目啸澡,用戶(hù)只需要點(diǎn)擊播放即可開(kāi)始真正使用應(yīng)用袖订,這絕對(duì)是無(wú)比順暢和優(yōu)秀的體驗(yàn)。另一種應(yīng)用情景是文件同步類(lèi)嗅虏,比如用戶(hù)標(biāo)記了一些文件為需要隨時(shí)同步洛姑,這樣用戶(hù)在其他設(shè)備或網(wǎng)頁(yè)服務(wù)上更改了這些文件時(shí),可以發(fā)送靜默推送然后使用后臺(tái)傳輸來(lái)保持這些文件隨時(shí)是最新皮服。

如果您是一路看下來(lái)的話(huà)楞艾,不難發(fā)現(xiàn)其實(shí)后臺(tái)獲取和靜默推送在很多方面是很類(lèi)似的,特別是實(shí)現(xiàn)和處理的方式龄广,但是它們適用的情景是完全不同的硫眯。后臺(tái)獲取更多地使用在泛數(shù)據(jù)模式下,也即用戶(hù)對(duì)特定數(shù)據(jù)并不是很關(guān)心择同,數(shù)據(jù)應(yīng)該被更新的時(shí)間也不是很確定舟铜,典型的有社交類(lèi)應(yīng)用和天氣類(lèi)應(yīng)用;而靜默推送或者是推送喚醒更多地應(yīng)該是用戶(hù)感興趣的內(nèi)容發(fā)生更新時(shí)被使用奠衔,比如消息類(lèi)應(yīng)用和內(nèi)容型服務(wù)等谆刨。根據(jù)不同的應(yīng)用情景,選擇合適的后臺(tái)策略(或者混合使用兩者)归斤,以帶給用戶(hù)絕佳體驗(yàn)痊夭,這是Apple所期望iOS7開(kāi)發(fā)者做到的。

后臺(tái)傳輸(Background Transfer Service)

iOS6和之前脏里,iOS應(yīng)用在大塊數(shù)據(jù)的下載這一塊限制是比較多的:只有應(yīng)用在前臺(tái)時(shí)能保持下載(用戶(hù)按Home鍵切到后臺(tái)或者是等到設(shè)備自動(dòng)休眠都可能中止下載)她我,在后臺(tái)只有很短的最多十分鐘時(shí)間可以保持網(wǎng)絡(luò)連接。如果想要完成一個(gè)較大數(shù)據(jù)的下載,用戶(hù)將不得不打開(kāi)你的app并且基本無(wú)所事事番舆。很多這種時(shí)候酝碳,用戶(hù)會(huì)想要是在下載的時(shí)候能切到別的應(yīng)用刷刷微博或者玩玩游戲,然后再切回來(lái)的就已經(jīng)下載完成了的話(huà)恨狈,該有多好疏哗。iOS7中,這可以實(shí)現(xiàn)了禾怠。iOS7引入了后臺(tái)傳輸?shù)南嚓P(guān)方式返奉,用來(lái)保證應(yīng)用退出后數(shù)據(jù)下載或者上傳能繼續(xù)進(jìn)行。這種傳輸是由iOS系統(tǒng)進(jìn)行管理的吗氏,沒(méi)有時(shí)間限制芽偏,也不要求應(yīng)用運(yùn)行在前臺(tái)。

想要實(shí)現(xiàn)后臺(tái)傳輸弦讽,就必須使用iOS7的新的網(wǎng)絡(luò)連接的類(lèi)污尉,NSURLSession。這是iOS7中引入用以替代陳舊的NSURLConnection的類(lèi)往产,著名的AFNetworking甚至不惜從底層開(kāi)始完全重寫(xiě)以適配iOS7和NSURLSession(參見(jiàn)這里)十厢,NSURLSession的重要性可見(jiàn)一斑。在這里我主要只介紹NSURLSession在后臺(tái)傳輸中的一些使用捂齐,關(guān)于這個(gè)類(lèi)的其他用法和對(duì)原有NSURLConnection的加強(qiáng)蛮放,只做稍微帶過(guò)而不展開(kāi),有興趣深入挖掘和使用的童鞋可以參看Apple的文檔(或者更簡(jiǎn)單的方式是使用AFNetworking來(lái)處理網(wǎng)絡(luò)相關(guān)內(nèi)容奠宜,而不是直接和CFNetwork框架打交道)包颁。

步驟和例子

后臺(tái)傳輸?shù)牡膶?shí)現(xiàn)也十分簡(jiǎn)單,簡(jiǎn)單說(shuō)分為三個(gè)步驟:創(chuàng)建后臺(tái)傳輸用的NSURLSession對(duì)象压真;向這個(gè)對(duì)象中加入對(duì)應(yīng)的傳輸?shù)腘SURLSessionTask娩嚼,并開(kāi)始傳輸;在實(shí)現(xiàn)appDelegate里實(shí)現(xiàn)-application:handleEventsForBackgroundURLSession:completionHandler:方法滴肿,以刷新UI及通知系統(tǒng)傳輸結(jié)束岳悟。接下來(lái)結(jié)合代碼來(lái)看一看實(shí)際的用法吧~

首先我們需要一個(gè)用于后臺(tái)下載的session:

- (NSURLSession *)backgroundSession

{

//Use dispatch_once_t to create only one background session. If you want more than one session, do with different identifier

static NSURLSession *session = nil;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.yourcompany.appId.BackgroundSession"];

session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];

});

return session;

}

這里創(chuàng)建并配置了NSURLSession,將其指定為后臺(tái)session并設(shè)定delegate泼差。

接下來(lái)向其中加入對(duì)應(yīng)的傳輸用的NSURLSessionTask贵少,并啟動(dòng)下載。

//@property (nonatomic) NSURLSession *session;

//@property (nonatomic) NSURLSessionDownloadTask *downloadTask;

- (NSURLSession *)backgroundSession

{

//...

}

- (void) beginDownload

{

NSURL *downloadURL = [NSURL URLWithString:DownloadURLString];

NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];

self.session = [self backgroundSession];

self.downloadTask = [self.session downloadTaskWithRequest:request];

[self.downloadTask resume];

}

最后一步是在appDelegate中實(shí)現(xiàn)-application:handleEventsForBackgroundURLSession:completionHandler:

//AppDelegate.m

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier

completionHandler:(void (^)())completionHandler

{

//Check if all transfers are done, and update UI

//Then tell system background transfer over, so it can take new snapshot to show in App Switcher

completionHandler();

//You can also pop up a local notification to remind the user

//...

}

NSURLSession和對(duì)應(yīng)的NSURLSessionTask有以下重要的delegate方法可以使用:

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask

didFinishDownloadingToURL:(NSURL *)location;

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task

didCompleteWithError:(NSError *)error;

一旦后臺(tái)傳輸?shù)臓顟B(tài)發(fā)生變化(包括正常結(jié)束和失敹言怠)的時(shí)候滔灶,應(yīng)用將被喚醒并運(yùn)行appDelegate中的回調(diào),接下來(lái)NSURLSessionTask的委托方法將在后臺(tái)被調(diào)用吼肥。雖然上面的例子中直接在appDelegate中call了completionHandler录平,但是實(shí)際上更好的選擇是在appDelegate中暫時(shí)持有completionHandler麻车,然后在NSURLSessionTask的delegate方法中檢查是否確實(shí)完成了傳輸并更新UI后,再調(diào)用completionHandler斗这。另外动猬,你的應(yīng)用到現(xiàn)在為止只是在后臺(tái)運(yùn)行,想要提醒用戶(hù)傳輸完成的話(huà)表箭,也許你還需要在這個(gè)時(shí)候發(fā)送一個(gè)本地推送(記住在這個(gè)時(shí)候你的應(yīng)用是可以執(zhí)行代碼的赁咙,雖然是在后臺(tái)),這樣用戶(hù)可以注意到你的應(yīng)用的變化并回到應(yīng)用燃逻,并開(kāi)始已經(jīng)準(zhǔn)備好數(shù)據(jù)和界面序目。

一些限制

首先臂痕,后臺(tái)傳輸只會(huì)通過(guò)wifi來(lái)進(jìn)行伯襟,用戶(hù)大概也不會(huì)開(kāi)心蜂窩數(shù)據(jù)的流量被后臺(tái)流量用掉。后臺(tái)下載的時(shí)間與以前的關(guān)閉應(yīng)用后X分鐘的模式不一樣握童,而是為了節(jié)省電力變?yōu)殡x散式的下載姆怪,并與其他后臺(tái)任務(wù)并發(fā)(比如接收郵件等)。另外還需要注意的是澡绩,對(duì)于下載后的內(nèi)容不要忘記寫(xiě)到應(yīng)用的目錄下(一般來(lái)說(shuō)這種可以重復(fù)獲得的內(nèi)容應(yīng)該放到cache目錄下)稽揭,否則如果由于應(yīng)用完全退出的情況導(dǎo)致沒(méi)有保存到可再次訪問(wèn)的路徑的話(huà),那可就白做工了肥卡。

后臺(tái)傳輸非常適合用于文件溪掀,照片或者追加游戲內(nèi)容關(guān)卡等的下載,如果配合后臺(tái)獲取或者靜默推送的話(huà)步鉴,相信可以完全很多很有趣揪胃,并且以前被限制而無(wú)法實(shí)現(xiàn)的功能。

最近的文章

貓都能學(xué)會(huì)的Unity3D Shader入門(mén)指南(二)

關(guān)于本系列這是Unity3D Shader入門(mén)指南系列的第二篇氛琢,本系列面向的對(duì)象是新接觸Shader開(kāi)發(fā)的Unity3D使用者喊递,因?yàn)槲冶旧碜约阂彩荢hader初學(xué)者,因此可能會(huì)存在錯(cuò)誤或者疏漏阳似,如果您在Shader開(kāi)發(fā)上有所心得骚勘,很歡迎并懇請(qǐng)您指出文中紕漏,我會(huì)盡快改正撮奏。在之前的開(kāi)篇中介紹了一些Shader的基本知識(shí)俏讹,包括ShaderLab的基本結(jié)構(gòu)和語(yǔ)法,以及簡(jiǎn)單逐句地講解了一個(gè)基本的shader畜吊。在具有這些基礎(chǔ)知識(shí)后藐石,閱讀簡(jiǎn)單的shader應(yīng)該不會(huì)有太大問(wèn)題,在繼續(xù)教程之前簡(jiǎn)單閱讀一下...…

2013-08-31 ? 能工巧匠集繼續(xù)閱讀

更早的文章

貓都能學(xué)會(huì)的Unity3D Shader入門(mén)指南(一)

動(dòng)機(jī)自己使用Unity3D也有一段時(shí)間了定拟,但是很多時(shí)候是流于表面于微,更多地是把這個(gè)引擎簡(jiǎn)單地用作腳本控制逗嫡,而對(duì)更深入一些的層次幾乎沒(méi)有了解。雖然說(shuō)Unity引擎設(shè)計(jì)的初衷就是創(chuàng)建簡(jiǎn)單的不需要開(kāi)發(fā)者操心的誰(shuí)都能用的3D引擎株依,但是只是膚淺的使用驱证,可能是無(wú)法達(dá)到隨心所欲的境地的,因此恋腕,這種狀況必須改變抹锄!從哪里開(kāi)始呢,貌似有句話(huà)叫做會(huì)寫(xiě)Shader的都是高手荠藤,于是伙单,想大概看看從Shader開(kāi)始能不能使自己到達(dá)的層次能再深入一些吧,再于是哈肖,有了這個(gè)系列(希望我能堅(jiān)持寫(xiě)完它吻育,雖然應(yīng)該會(huì)拖個(gè)半年左右)。U...…

原著出處https://onevcat.com/2013/07/shader-tutorial-1/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末淤井,一起剝皮案震驚了整個(gè)濱河市布疼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌币狠,老刑警劉巖游两,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異漩绵,居然都是意外死亡贱案,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)止吐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)宝踪,“玉大人,你說(shuō)我怎么就攤上這事祟印‰饶” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵蕴忆,是天一觀的道長(zhǎng)颤芬。 經(jīng)常有香客問(wèn)我,道長(zhǎng)套鹅,這世上最難降的妖魔是什么站蝠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮卓鹿,結(jié)果婚禮上菱魔,老公的妹妹穿的比我還像新娘。我一直安慰自己吟孙,他們只是感情好澜倦,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布聚蝶。 她就那樣靜靜地躺著,像睡著了一般藻治。 火紅的嫁衣襯著肌膚如雪碘勉。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天桩卵,我揣著相機(jī)與錄音验靡,去河邊找鬼。 笑死雏节,一個(gè)胖子當(dāng)著我的面吹牛胜嗓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钩乍,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼辞州,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了件蚕?” 一聲冷哼從身側(cè)響起孙技,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤产禾,失蹤者是張志新(化名)和其女友劉穎排作,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體亚情,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妄痪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了楞件。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衫生。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖土浸,靈堂內(nèi)的尸體忽然破棺而出罪针,到底是詐尸還是另有隱情,我是刑警寧澤黄伊,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布泪酱,位于F島的核電站,受9級(jí)特大地震影響还最,放射性物質(zhì)發(fā)生泄漏墓阀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一拓轻、第九天 我趴在偏房一處隱蔽的房頂上張望斯撮。 院中可真熱鬧,春花似錦扶叉、人聲如沸勿锅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)溢十。三九已至泳叠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茶宵,已是汗流浹背危纫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乌庶,地道東北人种蝶。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瞒大,于是被迫代替她去往敵國(guó)和親螃征。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

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