我們知道ios 的應(yīng)用,大部分都是進(jìn)入后臺,就不會執(zhí)行任何操作,但是 ,很多時候我們希望程序進(jìn)入后臺,也能執(zhí)行一些檢測操作,比如說,應(yīng)用進(jìn)入后臺,我們?nèi)匀豢梢詫崟r去獲取當(dāng)前的位置信息。下面我們來了解下,ios 獲取后臺時間的幾種方式
根據(jù)蘋果文檔中關(guān)于后臺執(zhí)行的描述,任何app都有3分鐘左右的后臺任務(wù)執(zhí)行時間。 3分鐘后,app會被iOS強(qiáng)行掛起。
但是,有幾類app允許有“無限的”后臺運(yùn)行時間:
- Audio。
- Location/GPS。
- VoIP。
你可以將任何app聲明為上述3種類型以獲得無限的后臺運(yùn)行時間,但當(dāng)你提交app到App Store時,蘋果會審查你的app,一旦發(fā)現(xiàn)你“濫用”了后臺API,你的app將被拒絕。也即是說你在info.plist 設(shè)置這幾種backgroundmode,你的程序必須含有這些功能,你的程序才會有審核通過,你想獲取應(yīng)用進(jìn)入后臺獲取更多的后臺時間,還要看蘋果給不給機(jī)會了。。。
1 上面有一種方式蘋果文檔說到任何應(yīng)用都有3分鐘的后臺執(zhí)行任務(wù)時間。好吧先看下一段代碼
(1)
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplication* app = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (bgTask != UIBackgroundTaskInvalid)
{
bgTask = UIBackgroundTaskInvalid;
}
});
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
if (bgTask != UIBackgroundTaskInvalid)
{
bgTask = UIBackgroundTaskInvalid;
});
});
}
這種方式,是不需要再info.plist 配置就可以在申請到短時間的后臺運(yùn)行時間,蘋果也不會對這個進(jìn)行審核
2 第二種方式,也是今天想著重提到的,就是通過定位獲取更多的后臺使用的時間,這種適合于需要在后臺收集用戶的定位的場景,例如滴滴打車此類應(yīng)用。
上面已經(jīng)提到了這種方式,需要在info.plist 設(shè)置

因為在蘋果在info.plist 進(jìn)行了設(shè)置,蘋果也會對其 進(jìn)行審核。
接下來是在來看下代碼段
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
_task = [BGTask shareBGTask];
UIAlertView *alert;
//判斷定位權(quán)限
if([UIApplication sharedApplication].backgroundRefreshStatus == UIBackgroundRefreshStatusDenied)
{
alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"應(yīng)用沒有不可以定位,需要在在設(shè)置/通用/后臺應(yīng)用刷新開啟" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
[alert show];
}
else if ([UIApplication sharedApplication].backgroundRefreshStatus == UIBackgroundRefreshStatusRestricted)
{
alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"設(shè)備不可以定位" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
[alert show];
}
else
{
_location = [[CLLocationManager alloc]init];
_location.desiredAccuracy = kCLLocationAccuracyBestForNavigation;//導(dǎo)航級別的精確度
_location.allowsBackgroundLocationUpdates = YES; //允許后臺刷新
_location.pausesLocationUpdatesAutomatically = NO; //不允許自動暫停刷新
_location.distanceFilter = kCLDistanceFilterNone; //不需要移動都可以刷新
[_location startUpdatingLocation];
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(log) userInfo:nil repeats:YES];
}
return YES;
}
//進(jìn)入后臺開啟定位
- (void)applicationDidEnterBackground:(UIApplication *)application {
[_location startUpdatingLocation];
}
經(jīng)測試而得只要這樣無論在前臺或者是后臺,log 這個方法都是有打印的。這里必須注意一點(diǎn)就是
_location.distanceFilter = kCLDistanceFilterNone; //不需要移動都可以刷新
之前因為要這樣設(shè)置,不然就各種掉坑,不移動就不會執(zhí)行定位,不定位的話,那么后臺進(jìn)程也就掛起了,那么就不能執(zhí)行任何操作
(2)其實這種后臺刷新是很暴力的,導(dǎo)航儀不斷在定位,你才可以獲取后臺活躍的時間,去執(zhí)行你的一些想要的操作,但是不斷的定位會導(dǎo)致,電量消耗過快,我測試一個小時獎金跑去了10%的電量,那么有沒有一些更優(yōu)的方案,減少定位的次數(shù),然后又能讓后臺活躍呢
下面來講解下思路

這個優(yōu)化面臨的問題,就是當(dāng)程序進(jìn)入了后臺,如果讓定位停止了,那么程序就后臺就無法活躍了,就是把程序進(jìn)入后臺的問題解決了,那就好辦了,看了上圖,也可以看出,思路主要是在當(dāng)前正在定位的時候,10秒后關(guān)閉當(dāng)前的定位,然后此時開啟后臺任務(wù)backgroundtask,那么只要就會有3分鐘的活躍時間,那么在這個后臺任務(wù)有效時間內(nèi)再次開啟定位的話,程序在后臺便依舊可以活躍,那么只要開啟和關(guān)閉循環(huán)進(jìn)行,就可以實現(xiàn)常駐后臺了,那么這個時間間隔可以自定義,在自己需要的范圍內(nèi)即可,但是不能超過3分鐘,那么接下來在看看主要代碼實現(xiàn)
//定位回調(diào)里執(zhí)行重啟定位和關(guān)閉定位
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
NSLog(@"定位收集");
//正在手機(jī)定位不執(zhí)行任何操作
if (isCollect) {
return;
}
[self performSelector:@selector(restartLocation) withObject:nil afterDelay:120];
[self performSelector:@selector(stopLocation) withObject:nil afterDelay:10];
isCollect = YES;
}
-(void)restartLocation
{
NSLog(@"重新啟動定位");
CLLocationManager *locationManager = [BGLogation shareBGLocation];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone; // 不移動也可以后臺刷新回調(diào)
if ([[UIDevice currentDevice].systemVersion floatValue]>= 8.0) {
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
[self.bgTask beginNewBackgroundTask];
}
//停止后臺定位
-(void)stopLocation
{
NSLog(@"停止定位");
isCollect = NO;
CLLocationManager *locationManager = [BGLogation shareBGLocation];
[locationManager stopUpdatingLocation];
}
接下來是demo 的地址
https://github.com/heysunnyboy/locationdemo.git
可以看到下面的代碼 ,在delegate 里設(shè)置的定時器一直都在跑沒有停止就知道這個后臺常駐是有效的
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
_task = [BGTask shareBGTask];
UIAlertView *alert;
//判斷定位權(quán)限
if([UIApplication sharedApplication].backgroundRefreshStatus == UIBackgroundRefreshStatusDenied)
{
alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"應(yīng)用沒有不可以定位,需要在在設(shè)置/通用/后臺應(yīng)用刷新開啟" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
[alert show];
}
else if ([UIApplication sharedApplication].backgroundRefreshStatus == UIBackgroundRefreshStatusRestricted)
{
alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"設(shè)備不可以定位" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
[alert show];
}
else
{
self.bgLocation = [[BGLogation alloc]init];
[self.bgLocation startLocation];
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(log) userInfo:nil repeats:YES];
}
return YES;
}