核心代碼僅需要4句,來實現(xiàn)ios后臺?;?。
直接上代碼
/// 申請進(jìn)行后臺任務(wù)
/// 這是一個不間斷的申請,在結(jié)束的時候再次進(jìn)行申請
func applyTimeForBackgroundTaskTime() {
var bgTask: UIBackgroundTaskIdentifier!
bgTask = UIApplication.shared.beginBackgroundTask(withName: "bg task") {[weak self] in
UIApplication.shared.endBackgroundTask(bgTask)
self?.applyTimeForBackgroundTaskTime()
}
}
項目設(shè)置中,開啟后臺服務(wù)
這里一定要勾選,不然,根本不會進(jìn)入appdelegate的進(jìn)入后臺的回調(diào)中,也就無法啟用該函數(shù)。

然后,在在appdelegate即將進(jìn)入后臺的時候,調(diào)用這個函數(shù),即可實現(xiàn)后臺?;睢?/p>
原理解析:
上面的函數(shù)applyTimeForBackgroundTaskTime中, 實際就是申請時間執(zhí)行一段后臺任務(wù),并返回一個后臺任務(wù)id。因為該時間是有限制的,一般30秒。該申請的任務(wù)結(jié)束回調(diào)中,必須要結(jié)束該后臺任務(wù)。不然系統(tǒng)就會殺死該App。在這個結(jié)束的回調(diào)中,再次申請執(zhí)行一段后臺任務(wù),則系統(tǒng)又會開始新的申請,并不會真正的掛起。
注意:
這里可能有同學(xué)會有疑問,結(jié)束后臺任務(wù)和殺死App有啥不同?說來還真是不同的,一個App進(jìn)入后臺,系統(tǒng)會將其掛起suspend,殺死是end。掛起的意思是不運行,進(jìn)入前臺仍然從進(jìn)入后臺的時刻開始運行。而殺死后,如果切到該程序,則會重新啟動。 結(jié)束后臺任務(wù)后,系統(tǒng)僅會將App掛起,而不會殺死。但如果不在結(jié)束任務(wù)的回調(diào)中進(jìn)行結(jié)束任務(wù),則系統(tǒng)就會殺死。被掛起的程序,在系統(tǒng)資源不夠的時候也會被殺死,那是另外的事情,不在本文的討論范圍。
這樣做的好處
省去了復(fù)雜的后臺播放音樂的邏輯,也不需要導(dǎo)入AVFoundation框架。
代碼邏輯非常簡化.
完整的示例代碼如下
//
// AppBackgroundTaskManager.swift
// BackgroundTask
//
// Created by wanggang on 2021/9/25.
//
import UIKit
class AppBackgroundTaskManager: NSObject {
static let shared = AppBackgroundTaskManager.init()
func startToApplyTimeForBackgroundTask() {
applyTimeForBackgroundTaskTime()
}
/// 申請進(jìn)行后臺任務(wù)
/// 這是一個不間斷的申請,在結(jié)束的時候再次進(jìn)行申請
func applyTimeForBackgroundTaskTime() {
var bgTask: UIBackgroundTaskIdentifier!
bgTask = UIApplication.shared.beginBackgroundTask(withName: "bg task") {[weak self] in
UIApplication.shared.endBackgroundTask(bgTask)
self?.applyTimeForBackgroundTaskTime()
}
}
}
AppDelegate中的調(diào)用, 以及驗證代碼如下.
//
// AppDelegate.swift
// BackgroundTask
//
// Created by wanggang on 2021/9/25.
//
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var taskTimer: Timer?
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
// 調(diào)用申請后臺保活
AppBackgroundTaskManager.shared.startToApplyTimeForBackgroundTask()
// 測試代碼,打印看一下后臺?;罨蛘吣M后臺干一些其他事情。
// 說明: 后臺?;罹褪巧厦娴腁ppBackgroundTaskManager.shared.startApplyBackgroundTask()
// 而在后臺干事情,不一定非要這個位置寫代碼。可以在項目的任何位置,如在某個頁面發(fā)起一些網(wǎng)絡(luò)請求。
doSomethingInBackground()
}
func doSomethingInBackground() {
// 后臺做一些事情
taskTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { _ in
print("doing some thing:\(UIApplication.shared.backgroundTimeRemaining)")
}
}
func stopDoingSomethingInBackgroud() -> () {
taskTimer?.invalidate()
taskTimer = nil
}
func applicationWillEnterForeground(_ application: UIApplication) {
stopDoingSomethingInBackgroud()
}
}
運行結(jié)果如下:

風(fēng)險: 要上架Appstore的話,還是需要說明后臺播放音樂的目的的。
完整的demo在github:
https://github.com/gerrywg/WGKeepAliveDemo.git
修訂:
在ios15上面好像存在問題,那就還需要播放無聲音樂的方式。
更新的代碼, 在silentMusic分支, 如下圖:
