原文發(fā)表于簡(jiǎn)書(shū):
http://www.reibang.com/p/5446e2580031
作者 zerojian
前段時(shí)間剛上線一款位置提醒軟件防症,為了保證提醒的準(zhǔn)確性懈费,使用了持續(xù)后臺(tái)定位,大家都知道著榴,使用后臺(tái)定位的應(yīng)用添履,電量損耗一直是一個(gè)很棘手的問(wèn)題,如果用戶發(fā)現(xiàn)電量損耗過(guò)大脑又,肯定會(huì)毫不猶豫的卸載你的應(yīng)用暮胧。
因?yàn)?iOS 的后臺(tái)機(jī)制限制,如果需要在后臺(tái)獲取位置问麸,是需要打開(kāi) Background Modes
-Location updates
往衷,但必須確保定位一直處于 updateing
狀態(tài),這會(huì)導(dǎo)致電量損耗明顯严卖,一旦停止定位席舍,系統(tǒng)會(huì)馬上關(guān)閉你的 app 的后臺(tái)權(quán)限。如果不是一個(gè)導(dǎo)航應(yīng)用哮笆,不是時(shí)時(shí)刻刻需要用戶的位置来颤,那么如何才能在后臺(tái)需要位置的時(shí)候獲取到數(shù)據(jù)呢汰扭?也就是說(shuō),我們首先必須保證后臺(tái)的持續(xù)運(yùn)行福铅,才能在這個(gè)基礎(chǔ)上優(yōu)化萝毛。
因?yàn)槿绻?Background Mode
中定位停止,那么系統(tǒng)會(huì)馬上關(guān)閉你的后臺(tái)權(quán)限滑黔,所以我們要做的就是用其它不耗電的后臺(tái)方式替代后臺(tái)定位珊泳,在需要用戶位置的時(shí)候恢復(fù)后臺(tái)定位即可。
iOS 有一個(gè) api BackgroundTask
它可以給程序提供最大大約3分鐘的后臺(tái)運(yùn)行時(shí)間拷沸,它不需要在 Background Modes
申請(qǐng)色查,它一般是在程序退出后撞芍,程序如果還有事務(wù)沒(méi)有處理完秧了,那么可以申請(qǐng)一個(gè)短暫后臺(tái)權(quán)限處理。
我們思考一個(gè)問(wèn)題序无,在后臺(tái)定位模式中帝嗡,如果在后臺(tái)停止定位哟玷,是不是可以理解成普通 app 的退出程序(失去運(yùn)行權(quán)限然后休眠)巢寡,那么我們?cè)诤笈_(tái)停止定位前申請(qǐng)一個(gè) BackgroundTask
在 Task
時(shí)間快到的時(shí)候恢復(fù)定位不就可以了嗎树叽?
既然有這么一個(gè)方法,那么我們可以在后臺(tái)每隔3分鐘定位一次奶甘,而且沒(méi)有任何副作用篷店,因?yàn)?BackgroundTask
只是可以讓系統(tǒng)給你最多3分鐘后臺(tái)權(quán)限祭椰,至于你要做什么臭家,它不關(guān)心疲陕。
要申請(qǐng)一個(gè) BackgroundTask
必須有一個(gè)唯一標(biāo)示符, 在申請(qǐng)前我們需要聲明一個(gè) Task 的 Identifier
:
var taskInvalid = UIBackgroundTaskInvalid
然后我們申請(qǐng)一個(gè) BackgroundTask
:
taskInvalid = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({ [unowned self] in
print("Background Task time over")
self.endBackgroundTask()
})
需要注意的是在申請(qǐng) Task 的閉包,是在 Task 系統(tǒng)給你的最大時(shí)間結(jié)束時(shí)(一般3分鐘左右)調(diào)用的,而不是申請(qǐng)后調(diào)用,所以在這里面我們必須調(diào)用結(jié)束 Task
在 Task 申請(qǐng)后需要處理的方法,我們可以放在 Task 申請(qǐng)后面,如:
taskInvalid = UIApplication.sharedApplication().beginBackgroundTaskWithExpiratio nHandler({ [unowned self] in
print("Background Task time over")
self.endBackgroundTask()
})
LocationService.stopLocation()
也就是說(shuō)我們?cè)谏暾?qǐng) Task 后停止定位,由 Task 代替定位繼續(xù)保持后臺(tái)狀態(tài),我們?cè)?分鐘后重新開(kāi)啟定位,然后結(jié)束 Task ,然后重新申請(qǐng)一個(gè) Task Identifier 下次使用.
func endBackgroundTask() {
UIApplication.sharedApplication().endBackgroundTask(taskInvalid)
taskInvalid = UIBackgroundTaskInvalid
print("Background task ended : \(NSDate())")
}
Task 的開(kāi)啟時(shí)機(jī),我們可以使用定位次數(shù)來(lái)控制,首先我們申請(qǐng)一個(gè) CLLocation
數(shù)組,然后在 didUpdateLocations
方法中每定位10次就開(kāi)啟 Task:
private var backgroundLocations = [CLLocation]() {
didSet{
if backgroundLocations.count == 10 {
begionBackgroundTask(self.timeInterval)
}
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let newLocation = locations.last else { return }
if UIApplication.sharedApplication().applicationState != .Active {
print("background location : \(newLocation.coordinate.latitude), \(newLocation.coordinate.longitude)")
backgroundLocations.append(newLocation)
至于 Task 的持續(xù)時(shí)間,我們可以使用 NSTimer 來(lái)設(shè)置:
private func begionBackgroundTask(time: NSTimeInterval){
initialBackgroundTask()
lastBackgroundLocation?(backgroundLocations.last!)
backgroundLocations.removeAll()
timer = NSTimer.scheduledTimerWithTimeInterval(time, target: self, selector: #selector(againStartLocation), userInfo: nil, repeats: false)
backgroundTask.registerBackgroundTask()
print(" stop location :\(time) seconds")
}
這樣我們就完成了定位每隔 <3分鐘 工作一次,而在休息的時(shí)間內(nèi),因?yàn)闆](méi)有處理任何事物,所以可以理解成休眠3分鐘定位一次的電量?jī)?yōu)化方案,完整的 DEMO 可以到我的 GitHub 下載 : https://github.com/ZeroJian/ZJLocation
轉(zhuǎn)載請(qǐng)注明出處!