Background Fetch
當我們的 App 不在前景執行時,需要在背景狀態時運行我們的 code 稱為背景執行。
背景執行可以概括兩種使用情境
App Request:使用者無需在使用 App 時執行的任務,例如清空過期資料,或確保使用者開啟 App 時能得到最新資料,在背景執行下載任務,又或者使用者使用功能後,便會離開 App,期許回來時可以看到任務執行完畢,例如傳送訊息。
Event Trigger:收到外界的通知 App 進而同步響應。例如收到背景推播、來電或新的健康數據。
基於不同的使用情境,Apple 建議使用不同的更新策略。
繼續執行任務:即使使用者收起 App 也不能被中斷,需繼續執行完畢,例如使用者按下寄送訊息後離開訊息視窗。beginBackgroundTask(withName:expirationHandler:)
遞延繁重的工作:App 需要執行繁重的工作,會耗電但無需在使用 App 時進行,可以遞延到充電時,例如訓練機器學習模型或進行資料庫整理。BGProcessingTask
更新最新資料:App 需要週期性更新資料或自身狀態,例如更新天氣,系統會給予 30 秒執行時間。BGAppRefreshTask
當收到背景推播但無需通知使用者:接受外部通知進而在背景啟動 App 響應,將不會對使用者進行任何通知,例如收到新的 e-mail。系統會給予 30 秒執行時間,惟需注意若一小時內頻繁更新超過三次,就會被系統限制。application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
當收到背景推播但需要通知使用者:接受外部通知在背景啟動 App 響應,當執行完畢後,通知使用者,例如收到新的 e-mail 並下載完畢後,通知使用者有新郵件,需使用 Notification Service Extension。UNNotificationServiceExtension
根據 Apple 建議實作背景執行前,需考慮三個面向來提升用戶體驗
Power:當在背景執行的時間越長越頻繁,將會越消耗電量。可以在任務提早結束時告知系統已經執行完畢,這樣便能節省電量。
Performance:可能同時有很多 App 在進行運作,需要考慮內存限制或者是 API 使用的限制,進而最小影響到前景 App 的運作,否則你的 App 會被系統強制終止,變成 terminate 狀態,影響下次開啟效率。
Privacy:不同 API 有各自對應可存取的資料,當你需要使用時,須向使用者揭露你會使用哪些訊息。
Background Task Completion
當 App 進到背景狀態時,爭取額外時間運行好完成任務,通常系統給予 5 秒時間。
主要處理兩種狀況
- 當任務結束時,需要告訴系統任務結束以釋放資源,若沒有執行
endBackgroundTask(_:)
,則系統會 terminate App。
2. 當背景執行超過給予時間時,要做的響應(例如推播)
beginBackgroundTask(expirationHandler:)
Discretionary Background URL Session
讓裝置可以在閒置或充電時執行任務。只要 App 是被系統自然掛起或終止,都可以再被重新喚醒執行任務,但若 App 是被使用者手動終止,則不會觸發任何背景執行。
BackgroundTasks
用戶可能在一天中開啟數次 App,但不想每次進入背景時便執行背景任務,例如清空資料庫或資料庫同步甚至是訓練機器學習模型,想要等到適當的時間(例如充電時)一次執行,就需要 iOS13 新的 framework,可支援到 Mac 平台。
Supported Platform: iOS 13.0+, Mac Catalyst 13.0+, tvOS 13.0+
此 framework 甚至可以在用戶充電時,關掉 CPU 監控,讓你可以充分運用硬體的性能,並確保你有資格執行,只要稍早在 App 或 extension 執行時註冊任務。
每次執行的時間與舊 API 一樣,30 seconds / runtime。
BGTaskScheduler 可能會根據情況一次給予多個任務,注意多個任務仍要在 30 秒內完成。且處理任務仍是在 main App 中執行,而非給予 extension。
背景執行的確切時間由 BGTaskScheduler 決定,會根據用戶使用 App 的習慣有關,例如用戶可能在固定時間會使用 App,則系統會預測的時間前進行背景執行,以確保用戶在開啟時就能享有最新內容。但另一方面來說,若用戶不常開啟 App,則背景執行的頻率會跟著降低。
執行步驟
- 註冊 BGAppFreshTask 給 BackgroundScheduler,並寫上欲完成的任務。
- 創建 BGAppRefreshTaskRequest 並 submit 給 BackgroundScheduler。
- 等待 BackgroundScheduler 由其調度執行。
- App 被喚醒時依據不同 task 執行任務,並在結果出現時在 task 上標注完成通知系統
程式撰寫
- App 啟用 background fetch 並至 Info.plist 填上欲註冊的 task identifier (唯一識別符)
- 註冊要背景執行的 task,call back 為接到任務後,要執行的事項
- 撰寫要執行的事項,並在執行完畢後,向系統說明任務結果
setTaskCompleted(success:)
與任務過期的處置expirationHandler: (() -> Void)
。也可以在此處提交下一次任務給 BackgroundScheduler,達到持續更新的效果。
- 向 BackgroundScheduler 提交任務。
任務有兩種類型,此處使用的是 BGAppRefreshTaskRequest,因是做輕量更新。
Developer Tool
Background Tasks framework 有提供指令可供開發者測試用。
- 模擬任務執行:
在執行submit(_:)
後下斷點。程式到斷點後,在 lldb 輸入
e -l objc — (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@”TASK_IDENTIFIER”]
TASK_IDENTIFIER 為註冊背景任務的識別符,接著繼續執行,即可在 console 視窗看到執行狀況。
- 模擬任務超出期限:
斷點下在任務執行途中,到斷點時於 console 輸入
e -l objc — (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@”TASK_IDENTIFIER”]
TASK_IDENTIFIER 為註冊背景任務的識別符,接著繼續執行,即可在 console 視窗看到執行狀況。