Background Modes詳細(xì)解析(一) —— 幾種Mode使用示例(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2022.11.06 星期日

前言

Background Modes我們?cè)诔绦蛑锌倳?huì)用到,包括語(yǔ)音携龟、定位更新均函、后臺(tái)任務(wù)以及遠(yuǎn)程通知等,這個(gè)模塊我們就一起來(lái)學(xué)習(xí)下俄烁。

開(kāi)始

Background Modes是我們常用的模式,比如語(yǔ)音级野、定位更新页屠、后臺(tái)任務(wù)以及遠(yuǎn)程通知等。Xcode里的后臺(tái)模式如下所示:

在本教程中勺阐,您將創(chuàng)建一個(gè)使用音頻播放、位置更新矛双、關(guān)鍵任務(wù)和后臺(tái)拉取的應(yīng)用程序渊抽,以了解最常見(jiàn)的后臺(tái)模式。本文來(lái)自翻譯议忽。

2010年懒闷,隨著iOS 4的發(fā)布,蘋果開(kāi)始允許應(yīng)用程序在后臺(tái)工作,并從那時(shí)起不斷發(fā)展和改進(jìn)后臺(tái)模式愤估。iOS限制使用后臺(tái)操作來(lái)改善用戶體驗(yàn)和延長(zhǎng)電池壽命帮辟。你的應(yīng)用可以在后臺(tái)運(yùn)行特定的情況,包括:播放音頻玩焰,更新位置和從服務(wù)器獲取最新的內(nèi)容由驹。

如果你的任務(wù)不屬于允許的類別,后臺(tái)模式可能不適合你昔园。如果你試圖使用超出其作用范圍的后臺(tái)模式來(lái)操縱系統(tǒng)蔓榄,你可能會(huì)面臨App Store的拒絕。

在本后臺(tái)模式教程中默刚,你將了解你的應(yīng)用程序可以在后臺(tái)做的四件事:

  • Play audio - 播放音頻:允許應(yīng)用程序在后臺(tái)繼續(xù)播放音頻甥郑。
  • Receive location updates - 接收位置更新:允許應(yīng)用程序在后臺(tái)接收位置更改。
  • Complete finite-length critical tasks - 完成有限長(zhǎng)度的關(guān)鍵任務(wù):允許應(yīng)用程序在移動(dòng)到后臺(tái)后繼續(xù)完成關(guān)鍵任務(wù)荤西。
  • Background Fetch - 后臺(tái)獲取:在iOS調(diào)度的時(shí)間表上執(zhí)行后臺(tái)更新澜搅。

在深入研究之前,我們先來(lái)快速瀏覽一下iOS的基本后臺(tái)模式:

  • Audio, AirPlay, and Picture in Picture - 音頻邪锌,AirPlay勉躺,和圖片中的圖片:當(dāng)應(yīng)用程序在后臺(tái)時(shí)播放音頻和視頻。
  • Location Updates - 位置更新:在后臺(tái)時(shí)繼續(xù)接收位置更新秃流。
  • Voice over IP - IP語(yǔ)音:通過(guò)因特網(wǎng)發(fā)送和接收語(yǔ)音赂蕴。
  • External accessory communication - 外部配件通信:通過(guò)lightning接口與外部配件通信。
  • Using Bluetooth LE accessories - 使用藍(lán)牙LE配件:在后臺(tái)與藍(lán)牙LE配件通信舶胀。
  • Acting as a Bluetooth LE accessory - 充當(dāng)藍(lán)牙LE配件:允許應(yīng)用程序?yàn)榕浼峁┧{(lán)牙LE信息概说。
  • Background fetch - 后臺(tái)拉取:執(zhí)行數(shù)據(jù)刷新。
  • Remote notitifications - 遠(yuǎn)程通知:發(fā)送和接收遠(yuǎn)程通知嚣伐。
  • Background processing - 后臺(tái)處理:執(zhí)行較長(zhǎng)的關(guān)鍵進(jìn)程糖赔。

您將向示例應(yīng)用程序添加上述模式中的四種——音頻、定位轩端、后臺(tái)處理和后臺(tái)獲取(audio, location, background processing and background fetches)放典。如果你只對(duì)其中的一些模式感興趣,可以隨意跳過(guò)基茵,只玩你感興趣的模式奋构。

注意:要獲得完整的效果,您應(yīng)該在真實(shí)的設(shè)備上進(jìn)行操作拱层。在模擬器中弥臼,當(dāng)你忘記一個(gè)步驟時(shí),應(yīng)用程序可能會(huì)在后臺(tái)運(yùn)行根灯。然后當(dāng)你切換到真正的設(shè)備時(shí)径缅,它可能根本無(wú)法工作掺栅。

在您可以在物理設(shè)備上運(yùn)行項(xiàng)目之前,您必須設(shè)置您的development team纳猪,如下所示:

構(gòu)建并運(yùn)行示例項(xiàng)目來(lái)感受一下Sleepless氧卧,這是一個(gè)從不休息的應(yīng)用程序,因?yàn)樗诤笈_(tái)做事情氏堤。有四個(gè)tab —— 每個(gè)覆蓋一個(gè)模式:

您要添加的第一個(gè)capabilitybackground audio沙绝。


Playing Audio

在物理設(shè)備上構(gòu)建并運(yùn)行Sleepless。導(dǎo)航到audio tab丽猬,播放音樂(lè)宿饱,然后通過(guò)返回主屏幕把應(yīng)用程序放在后臺(tái)。音樂(lè)將停止播放脚祟。

打開(kāi) AudioModel.swift

該應(yīng)用程序利用AVQueuePlayer對(duì)歌曲進(jìn)行排隊(duì)并按順序播放谬以。模型觀察播放器的currentItem值以提供視圖的更新。

1. Giving Credit Where Credit Is Due

最初的項(xiàng)目包括來(lái)自incompetech.com的音頻文件由桌,這是一個(gè)流行的免版稅音樂(lè)網(wǎng)站为黎。你可以免費(fèi)使用帶有版權(quán)的音樂(lè)。這三首歌都是Kevin MacLeod寫的:

“Feelin Good” Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0 License
http://creativecommons.org/licenses/by/3.0/

“Iron Bacon” Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0 License
http://creativecommons.org/licenses/by/3.0/

“What You Want” Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0 License
http://creativecommons.org/licenses/by/3.0/

謝謝你美妙的音樂(lè)行您,Kevin!

注意:在蘋果的UIKit文檔中查看Execution States for Apps铭乾,了解更多關(guān)于active state和其他的信息。

2. Testing Audio in the Background

為什么當(dāng)應(yīng)用程序進(jìn)入后臺(tái)時(shí)音樂(lè)停止了娃循?好吧炕檩,缺了一個(gè)關(guān)鍵的部分!

大多數(shù)后臺(tái)模式都不能工作,除非你啟用特定的功能捌斧,表明應(yīng)用程序想要在后臺(tái)運(yùn)行代碼笛质。特例是關(guān)鍵任務(wù)完成,任何應(yīng)用程序都可以執(zhí)行捞蚂。

當(dāng)激活時(shí)妇押,音頻后臺(tái)模式告訴iOS繼續(xù)播放音頻,即使應(yīng)用程序在后臺(tái)姓迅。沒(méi)錯(cuò)敲霍,音頻后臺(tái)模式實(shí)際上是自動(dòng)的。你只需要激活它丁存。

返回Xcode肩杈,執(zhí)行以下操作:

接下來(lái),雙擊Background Modes以添加此功能解寝。展開(kāi)Background Modes功能扩然,然后勾選Audio, AirPlay, and Picture in Picture以啟用background audio

在物理設(shè)備上構(gòu)建并運(yùn)行應(yīng)用程序编丘。像以前一樣啟動(dòng)音樂(lè)与学,然后離開(kāi)應(yīng)用程序。這一次音頻將繼續(xù)嘉抓。就這么簡(jiǎn)單!

接下來(lái)索守,你將使用Location updates后臺(tái)模式繼續(xù)接收位置更新,即使應(yīng)用程序是在后臺(tái)抑片。


Receiving Location Updates

首先卵佛,構(gòu)建并運(yùn)行應(yīng)用程序。選擇Location tab并點(diǎn)擊Start敞斋。什么也沒(méi)有發(fā)生截汪,因?yàn)槟沐e(cuò)過(guò)了一些重要的步驟。你現(xiàn)在要改變了植捎。

1. Enabling Location Updates

打開(kāi)LocationModel.swift衙解。這是為LocationView提供位置數(shù)據(jù)的代碼。您將對(duì)init()做一個(gè)簡(jiǎn)單的更改焰枢。替換以下兩行:

  mgr.requestWhenInUseAuthorization()
  mgr.allowsBackgroundLocationUpdates = false

 mgr.requestAlwaysAuthorization()
 mgr.allowsBackgroundLocationUpdates = true

第一行請(qǐng)求位置更新蚓峦,即使應(yīng)用程序沒(méi)有在使用。第二個(gè)請(qǐng)求甚至在后臺(tái)進(jìn)行更新济锄。

回到Signing & Capabilities界面暑椰,勾選Location updates框,讓iOS知道你的應(yīng)用程序想在后臺(tái)接收位置更新荐绝。

除了勾選這個(gè)框一汽,iOS還要求你在Info.plist中設(shè)置一個(gè)鍵向用戶解釋為什么你需要后臺(tái)更新。如果不包含這一點(diǎn)低滩,位置請(qǐng)求將會(huì)無(wú)聲地失敗召夹。

打開(kāi)Info.plist。并添加Privacy — Location Always and When In Use Usage DescriptionPrivacy — Location When In Use Usage Description的鍵委造。然后輸入The app will show your location on a map作為兩個(gè)鍵的value戳鹅。

現(xiàn)在,構(gòu)建并運(yùn)行昏兆,切換到Location tab枫虏,點(diǎn)擊Start

當(dāng)它第一次加載時(shí)爬虱,你會(huì)看到你寫進(jìn)你的位置隱私原因的消息隶债。

點(diǎn)擊Allow while using app,在外面或大樓周圍散步——盡量不要因?yàn)樽タ诖侄中摹?/p>

位置更新應(yīng)該開(kāi)始出現(xiàn)跑筝。如果沒(méi)有死讹,將應(yīng)用再次發(fā)送到后臺(tái),以觸發(fā)Always提示進(jìn)行位置跟蹤曲梗。你也可以使用Settings應(yīng)用程序赞警,在Privacy ? Location Services ? Sleepless設(shè)置中啟用Sleepless應(yīng)用程序始終跟蹤妓忍。

如果你將應(yīng)用程序發(fā)送到后臺(tái),你仍然會(huì)看到控制臺(tái)中發(fā)生的位置更新愧旦。

一段時(shí)間后世剖,你應(yīng)該會(huì)看到如下內(nèi)容:

2. Testing Location Mode in the Background

如果你退出應(yīng)用程序,你應(yīng)該看到應(yīng)用程序更新了控制臺(tái)日志中的位置笤虫。再次打開(kāi)它旁瘫,可以看到地圖上所有的大頭針,顯示你在步行過(guò)程中去過(guò)的地方琼蚯。

如果你正在使用模擬器酬凳,你也可以使用它來(lái)模擬移動(dòng)!點(diǎn)擊Features ? Location菜單:

非常簡(jiǎn)單遭庶,對(duì)吧宁仔?打開(kāi)第三個(gè)選項(xiàng)卡和第三個(gè)后臺(tái)模式!


Completing Critical Tasks Upon Moving to the Background

下一個(gè)后臺(tái)模式的正式名稱是Extending Your App’s Background Execution Time,任務(wù)完成說(shuō)起來(lái)容易一點(diǎn)!

從技術(shù)上講峦睡,這根本不是后臺(tái)模式台诗。你不需要在Capabilities中聲明你的應(yīng)用程序使用它。它是一個(gè)API赐俗,當(dāng)你的應(yīng)用程序在后臺(tái)時(shí)拉队,允許你在有限的時(shí)間內(nèi)運(yùn)行任意代碼,給你更多的時(shí)間來(lái)完成關(guān)鍵任務(wù)阻逮,如保存數(shù)據(jù)粱快。

1. When to Use Task Completion

Completion后臺(tái)模式的一個(gè)有效用例是完成一些關(guān)鍵任務(wù),例如保存用戶的輸入或發(fā)布一個(gè)事務(wù)叔扼。有很多可能性事哭。

由于代碼是任意的,你可以使用這個(gè)API做幾乎任何事情:執(zhí)行冗長(zhǎng)的計(jì)算瓜富,對(duì)圖像應(yīng)用過(guò)濾器鳍咱,渲染一個(gè)復(fù)雜的3D網(wǎng)格 —— 任何!你的想象力是極限与柑,只要你記住你只有一些時(shí)間谤辜,而不是無(wú)限的時(shí)間。稍后价捧,您將設(shè)置一個(gè)在后臺(tái)運(yùn)行的冗長(zhǎng)計(jì)算丑念,因此您可以看到這個(gè)API是如何工作的。

iOS決定了你的應(yīng)用程序移到后臺(tái)后的時(shí)間结蟋。你被授予的時(shí)間沒(méi)有保證脯倚,但你總是可以檢查UIApplication.shared.backgroundTimeRemaining。這會(huì)告訴你還剩下多少時(shí)間嵌屎。

一般的推正,基于觀察的共識(shí)是你大約有30秒恍涂。同樣,沒(méi)有保證植榕,API文檔甚至沒(méi)有給出一個(gè)估計(jì)——所以不要依賴這個(gè)數(shù)字乳丰。你可能有5分鐘或5秒鐘的時(shí)間,所以你的應(yīng)用程序需要為中斷做好準(zhǔn)備内贮。當(dāng)你的時(shí)間快到的時(shí)候,iOS會(huì)給你回調(diào)信號(hào)汞斧。

2. Setting Up a Completion Task

這里有一個(gè)每個(gè)計(jì)算機(jī)科學(xué)專業(yè)的學(xué)生都應(yīng)該熟悉的常見(jiàn)任務(wù):計(jì)算 Fibonacci Sequence中的數(shù)字夜郁。這里的扭轉(zhuǎn)是,你將應(yīng)用程序移動(dòng)到后臺(tái)后計(jì)算這些數(shù)字粘勒。

打開(kāi)CompleteTaskModel.swift竞端,看看已經(jīng)有什么了。按照目前的情況庙睡,該視圖將按順序計(jì)算斐波那契數(shù)列并顯示結(jié)果事富。

如果你現(xiàn)在掛起一個(gè)實(shí)際設(shè)備上的應(yīng)用程序,計(jì)算將停止乘陪,并在應(yīng)用程序再次激活時(shí)恢復(fù)到原來(lái)的位置统台。你的任務(wù)是創(chuàng)建一個(gè)后臺(tái)任務(wù),這樣計(jì)算就可以一直運(yùn)行啡邑,直到iOS說(shuō)“時(shí)間到贱勃!”

你首先需要添加以下內(nèi)容到CompleteTaskModel:

var backgroundTask: UIBackgroundTaskIdentifier = .invalid

此屬性標(biāo)識(shí)要在后臺(tái)運(yùn)行的任務(wù)請(qǐng)求。

接下來(lái)谤逼,在resetcalculation()之前向CompleteTaskModel添加以下方法:

func registerBackgroundTask() {
  backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
    print("iOS has signaled time has expired")
    self?.endBackgroundTaskIfActive()
  }
}

registerBackgroundTask()告訴iOS贵扰,當(dāng)應(yīng)用移動(dòng)到后臺(tái)時(shí),你需要更多的時(shí)間來(lái)完成你正在做的事情流部。返回的值是這個(gè)任務(wù)的標(biāo)識(shí)符戚绕,這樣你就可以告訴iOS你什么時(shí)候完成了。在這個(gè)調(diào)用之后枝冀,如果你的應(yīng)用程序移動(dòng)到后臺(tái)舞丛,它仍然會(huì)得到CPU時(shí)間,直到你調(diào)用endBackgroundTask(_:)果漾。

好吧瓷马,至少有一些CPU時(shí)間。

3. Ending the Completion Task

如果你在后臺(tái)一段時(shí)間后沒(méi)有調(diào)用endBackgroundTask(_:)跨晴, iOS將調(diào)用當(dāng)你調(diào)用beginBackgroundTask(expirationHandler:)時(shí)定義的閉包欧聘。這使您有機(jī)會(huì)停止執(zhí)行代碼。

因此端盆,調(diào)用endBackgroundTask(_:)來(lái)告訴系統(tǒng)您已經(jīng)完成是一個(gè)好主意怀骤。如果你不調(diào)用它并在這個(gè)塊運(yùn)行后繼續(xù)執(zhí)行代碼费封,iOS將終止你的應(yīng)用程序!

將這個(gè)方法添加到registerBackgroundTask()下面:

func endBackgroundTaskIfActive() {
  let isBackgroundTaskActive = backgroundTask != .invalid
  if isBackgroundTaskActive {
    print("Background task ended.")
    UIApplication.shared.endBackgroundTask(backgroundTask)
    backgroundTask = .invalid
  }
}

這將結(jié)束后臺(tái)任務(wù),如果它是主動(dòng)注冊(cè)的蒋伦,并將其ID重置為invalid弓摘。

4. Registering and Ending Background Tasks

現(xiàn)在,對(duì)于重要的部分:更新onChangeOfScenePhase(_:)來(lái)注冊(cè)和結(jié)束后臺(tái)任務(wù)痕届,這取決于應(yīng)用程序是移動(dòng)到后臺(tái)還是活動(dòng)狀態(tài)韧献。

用以下語(yǔ)句替換這兩個(gè)case語(yǔ)句:

case .background:
  let isTimerRunning = updateTimer != nil
  let isTaskUnregistered = backgroundTask == .invalid

  if isTimerRunning && isTaskUnregistered {
    registerBackgroundTask()
  }
case .active:
  endBackgroundTaskIfActive()

當(dāng)切換到后臺(tái)background狀態(tài)時(shí),這將在任務(wù)正在運(yùn)行但未注冊(cè)時(shí)注冊(cè)它研叫。當(dāng)切換到活動(dòng)active狀態(tài)時(shí)锤窑,它將結(jié)束后臺(tái)任務(wù)。

beginPauseTask()中嚷炉,在updateTimer = nil之后添加這一行:

endBackgroundTaskIfActive()

現(xiàn)在渊啰,當(dāng)用戶停止計(jì)算時(shí),你調(diào)用endBackgroundTask(_:)來(lái)告訴iOS你不需要任何額外的CPU時(shí)間申屹。

注意:每次調(diào)用beginBackgroundTask(expirationHandler:)時(shí)調(diào)用endBackgroundTask(_:)是很重要的绘证。如果你調(diào)用beginBackgroundTask(expirationHandler:)兩次,并且只對(duì)其中一個(gè)任務(wù)調(diào)用endBackgroundTask(_:)哗讥,你仍然會(huì)得到CPU時(shí)間嚷那,直到你使用第二個(gè)后臺(tái)任務(wù)的標(biāo)識(shí)符第二次調(diào)用endBackgroundTask(_:)

構(gòu)建并運(yùn)行杆煞,然后切換到第三個(gè)選項(xiàng)卡车酣。

點(diǎn)擊Play并觀看應(yīng)用程序計(jì)算這些甜蜜的斐波那契值。將應(yīng)用發(fā)送到后臺(tái)索绪,但要觀察Xcode控制臺(tái)的輸出湖员。當(dāng)剩下的時(shí)間減少時(shí),你的應(yīng)用程序應(yīng)該繼續(xù)更新數(shù)字瑞驱。

在大多數(shù)情況下娘摔,這個(gè)時(shí)間從30秒開(kāi)始,一直到5秒唤反。如果你在達(dá)到5秒時(shí)等待時(shí)間過(guò)期——或者你看到的任何值——iOS就會(huì)調(diào)用過(guò)期block凳寺。

你的應(yīng)用程序應(yīng)該很快停止產(chǎn)生輸出。然后彤侍,如果你回到應(yīng)用程序肠缨,計(jì)時(shí)器應(yīng)該會(huì)再次啟動(dòng),斐波那契瘋狂將繼續(xù)盏阶。

在前臺(tái)和后臺(tái)之間切換晒奕,看看如何通過(guò)每次切換獲得額外的時(shí)間塊。

下面是本教程的最后一個(gè)主題:background fetch


Background Fetch

Background fetch是在iOS 7中引入的。它可以讓你的應(yīng)用程序顯示最新的同時(shí)最小化對(duì)電池壽命的影響脑慧。從iOS 13開(kāi)始魄眉,蘋果引入了一個(gè)新的后臺(tái)任務(wù)調(diào)度程序API,提供了顯著的改進(jìn)闷袒。

例如坑律,假設(shè)你正在應(yīng)用程序中實(shí)現(xiàn)一個(gè)新聞feed。在后臺(tái)獲取之前囊骤,你將在應(yīng)用程序每次啟動(dòng)時(shí)刷新feed晃择。

遺憾的是,當(dāng)刷新時(shí)也物,用戶會(huì)看到幾秒鐘的舊標(biāo)題宫屠。你知道,有些人會(huì)試圖挖掘一個(gè)故事焦除,結(jié)果卻發(fā)現(xiàn)它消失了,取而代之的是一個(gè)不相關(guān)的故事作彤”炱牵看起來(lái)不太好。

如果當(dāng)用戶打開(kāi)你的應(yīng)用時(shí)竭讳,最新的標(biāo)題就會(huì)神奇地出現(xiàn)在那里创葡,不是更好嗎?這是后臺(tái)獲取給你的能力绢慢。

當(dāng)啟用時(shí)灿渴,系統(tǒng)利用使用模式來(lái)確定何時(shí)觸發(fā)后臺(tái)獲取。例如胰舆,如果用戶在上午9點(diǎn)打開(kāi)你的新聞應(yīng)用骚露,background fetch可能會(huì)在上午9點(diǎn)之前發(fā)生。系統(tǒng)決定發(fā)出background fetch的最佳時(shí)間缚窿,由于這個(gè)原因棘幸,它不適合進(jìn)行關(guān)鍵更新。

1. Understanding Background Fetch

Background fetchBGTaskScheduler控制倦零,這是一個(gè)復(fù)雜的系統(tǒng)误续,用于平衡所有影響用戶體驗(yàn)的因素,如性能扫茅、使用模式蹋嵌、電池壽命等蹦魔。

Background fetch通常涉及從外部來(lái)源(如網(wǎng)絡(luò)服務(wù))獲取信息炫隶。在本后臺(tái)模式教程中,您將獲取當(dāng)前時(shí)間延窜,而不使用網(wǎng)絡(luò)。

為了實(shí)現(xiàn)background fetch愕鼓,你需要完成這些任務(wù)——但現(xiàn)在不要做:

  • 在你的應(yīng)用程序的CapabilitiesBackground Modes中勾選Background fetch钙态。
  • Info.plist添加標(biāo)識(shí)符。請(qǐng)為您的刷新任務(wù)菇晃。
  • 在你的應(yīng)用程序代理中調(diào)用BGTaskScheduler.register(forTaskWithIdentifier:using:launchHandler:)來(lái)處理后臺(tái)獲取册倒。
  • 創(chuàng)建一個(gè)BGAppRefreshTaskRequest,為何時(shí)執(zhí)行指定一個(gè)earliestBeginDate磺送。
  • 使用BGTaskScheduler.submit(_:)提交請(qǐng)求驻子。

與后臺(tái)完成任務(wù)類似,您有一個(gè)很短但不確定的時(shí)間框架來(lái)執(zhí)行background fetch估灿。共識(shí)的數(shù)字是最大30秒崇呵,但計(jì)劃更少。如果您需要下載大型資源作為獲取的一部分馅袁,請(qǐng)使用URLSession的后臺(tái)傳輸服務(wù)域慷。

2. Implementing Background Fetch

是時(shí)候開(kāi)始了。首先汗销,簡(jiǎn)單的部分:在Signing & Capabilities下選中Background fetch能力犹褒。

接下來(lái),打開(kāi)Info.plist弛针。并點(diǎn)擊+來(lái)添加一個(gè)新的標(biāo)識(shí)符叠骑。

向下滾動(dòng)并選擇Permitted background task scheduler identifiers。展開(kāi)項(xiàng)目削茁,然后點(diǎn)擊新標(biāo)識(shí)符旁邊的+以添加條目宙枷。

輸入com.mycompany.myapp.task.refresh獲取標(biāo)識(shí)符的值。

注意:在您的實(shí)際項(xiàng)目中茧跋,您將反向使用您公司的URL作為標(biāo)識(shí)符的根慰丛,添加您的應(yīng)用程序名稱和描述性元素,如task.refresh瘾杭¤档郏可以定義多種類型的刷新任務(wù),每種任務(wù)都有自己的標(biāo)識(shí)符富寿。

接下來(lái)睬隶,你需要一個(gè)AppDelegate類,因?yàn)閕OS希望在application(_:didFinishLaunchingWithOptions:)任務(wù)之間注冊(cè)你的獲取task页徐。

App文件夾中苏潜,添加一個(gè)新的Swift文件AppDelegate.swift。然后將現(xiàn)有代碼替換為:

import UIKit
import BackgroundTasks

class AppDelegate: UIResponder, UIApplicationDelegate {
  static var dateFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateStyle = .short
    formatter.timeStyle = .long
    return formatter
  }()

  var window: UIWindow?

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    return true
  }
}

這段代碼為刷新時(shí)間戳定義了一個(gè)日期格式化器变勇。它還包括一個(gè)application(_:didFinishLaunchingWithOptions:)空方法恤左,這是您將注冊(cè)后臺(tái)獲取任務(wù)的地方贴唇。

現(xiàn)在向AppDelegate添加以下函數(shù):

func refresh() {
  // to simulate a refresh, just update the last refresh date
  // to current date/time
  let formattedDate = Self.dateFormatter.string(from: Date())
  UserDefaults.standard.set(
    formattedDate,
    forKey: UserDefaultsKeys.lastRefreshDateKey)
  print("refresh occurred")
}

這個(gè)函數(shù)模擬了一次刷新。

在您創(chuàng)建的應(yīng)用程序中飞袋,您可能會(huì)從網(wǎng)絡(luò)獲取數(shù)據(jù)戳气。對(duì)于本教程,您將把一個(gè)格式化的時(shí)間戳保存到UserDefaults中巧鸭,以顯示刷新執(zhí)行的時(shí)間瓶您。

仍然在AppDelegate.swift中,向AppDelegate添加以下函數(shù):

func scheduleAppRefresh() {
  let request = BGAppRefreshTaskRequest(
    identifier: AppConstants.backgroundTaskIdentifier)
  request.earliestBeginDate = Date(timeIntervalSinceNow: 1 * 60)
  do {
    try BGTaskScheduler.shared.submit(request)
    print("background refresh scheduled")
  } catch {
    print("Couldn't schedule app refresh \(error.localizedDescription)")
  }
}

這里你創(chuàng)建了一個(gè)BGAppRefreshTaskRequest纲仍,然后從當(dāng)前時(shí)間開(kāi)始分配一個(gè)earliestBeginDate呀袱。然后使用BGTaskScheduler.submit(_:)提交請(qǐng)求。

現(xiàn)在郑叠,將application(_:didFinishLaunchingWithOptions:)替換為:

BGTaskScheduler.shared.register(
  forTaskWithIdentifier: AppConstants.backgroundTaskIdentifier,
  using: nil) { task in
    self.refresh() // 1
    task.setTaskCompleted(success: true) // 2
    self.scheduleAppRefresh() // 3
}

scheduleAppRefresh()
return true

當(dāng)iOS完成啟動(dòng)應(yīng)用程序時(shí)夜赵,這段代碼向任務(wù)調(diào)度器注冊(cè)任務(wù)并調(diào)度第一次刷新。任務(wù)本身乡革,當(dāng)執(zhí)行時(shí)寇僧,將:

現(xiàn)在需要將AppDelegate連接到AppMain。打開(kāi)AppMain.swift沸版。在body之前加上這一行:

@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

這就是iOS調(diào)用AppDelegate所需要的一切嘁傀。

在物理設(shè)備上構(gòu)建并運(yùn)行應(yīng)用程序。檢查Xcode控制臺(tái)的消息推穷,以確認(rèn)后臺(tái)刷新是預(yù)定調(diào)度的心包。

3. Testing Background Fetch

測(cè)試background fetch的一種方法是坐等系統(tǒng)決定執(zhí)行它类咧。但你可能要坐很長(zhǎng)時(shí)間等待這一切發(fā)生馒铃。

iOS無(wú)法保證何時(shí)執(zhí)行刷新。該系統(tǒng)使用多種因素來(lái)決定何時(shí)執(zhí)行痕惋,如應(yīng)用程序使用模式区宇、電池充電等。幸運(yùn)的是值戳,Xcode提供了一種使用調(diào)試器命令觸發(fā)后臺(tái)獲取的方法议谷。

打開(kāi)RefreshView.swift并在print("moved to background")處設(shè)置斷點(diǎn)。

然后將應(yīng)用發(fā)送到后臺(tái)堕虹,Xcode應(yīng)該在新的斷點(diǎn)處中斷卧晓。在lldb提示符下,輸入以下命令(或者赴捞,因?yàn)樗浅?fù)雜逼裆,復(fù)制和粘貼!)

e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.mycompany.myapp.task.refresh"]

這將指示調(diào)試器立即執(zhí)行后臺(tái)刷新。

恢復(fù)應(yīng)用程序的執(zhí)行赦政∈び睿控制臺(tái)應(yīng)該顯示刷新已發(fā)生,然后調(diào)度計(jì)劃的后臺(tái)刷新。每次刷新都為未來(lái)安排另一次刷新桐愉。您還可能看到來(lái)自后臺(tái)任務(wù)調(diào)度器的調(diào)試消息财破,指示其活動(dòng)。

接下來(lái)从诲,重新打開(kāi)應(yīng)用程序左痢。 Refresh tab將顯示刷新發(fā)生的時(shí)間和日期。

如果你把這款應(yīng)用留在你的設(shè)備上盏求,在接下來(lái)的幾天里查看抖锥,你會(huì)不時(shí)看到時(shí)間戳的更新。iOS根據(jù)最佳刷新時(shí)間的計(jì)算調(diào)用刷新碎罚。

對(duì)于需要很多分鐘才能完成的長(zhǎng)時(shí)間運(yùn)行的后臺(tái)任務(wù)磅废,了解更多關(guān)于 background processing tasks的信息。后臺(tái)處理任務(wù)Background processing類似于后臺(tái)獲取(background fetch)荆烈,但用于更嚴(yán)格的任務(wù)拯勉,如數(shù)據(jù)處理和維護(hù)。

還有兩個(gè)與后臺(tái)模式(background mode)相關(guān)的很棒的WWDC演講:

最后,您可以在Configuring Background Execution Modes 中了解所有的后臺(tái)執(zhí)行模式玫鸟。

后記

本篇主要講述了Background Modes幾種Mode使用示例导绷,感興趣的給個(gè)贊或者關(guān)注~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市屎飘,隨后出現(xiàn)的幾起案子妥曲,更是在濱河造成了極大的恐慌,老刑警劉巖钦购,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件檐盟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡押桃,警方通過(guò)查閱死者的電腦和手機(jī)葵萎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)唱凯,“玉大人羡忘,你說(shuō)我怎么就攤上這事】闹纾” “怎么了卷雕?”我有些...
    開(kāi)封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)掰烟。 經(jīng)常有香客問(wèn)我爽蝴,道長(zhǎng)沐批,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任蝎亚,我火速辦了婚禮九孩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘发框。我一直安慰自己躺彬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布梅惯。 她就那樣靜靜地躺著宪拥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铣减。 梳的紋絲不亂的頭發(fā)上她君,一...
    開(kāi)封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音葫哗,去河邊找鬼缔刹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛劣针,可吹牛的內(nèi)容都是我干的校镐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼捺典,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鸟廓!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起襟己,我...
    開(kāi)封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤引谜,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后稀蟋,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體煌张,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡呐赡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年退客,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片链嘀。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萌狂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出怀泊,到底是詐尸還是另有隱情茫藏,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布霹琼,位于F島的核電站务傲,受9級(jí)特大地震影響凉当,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜售葡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一看杭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挟伙,春花似錦楼雹、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至介却,卻和暖如春谴供,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背齿坷。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工憔鬼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胃夏。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓轴或,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親仰禀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子照雁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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