在iOS開發(fā)中,當程序進入后臺或者鎖屏時,若程序是用socket進行連接,就有可能與服務器斷開連接。這是因為iOS設備為了讓設備盡量省電,減少不必要的用電時,對app后臺采用了墓碑機制的后臺。
墓碑機制簡單的理解,就是當任務被中斷(進入后臺)時,系統(tǒng)記錄下當前程序的當前狀態(tài)后,再中止程序,當程序再次啟用的時候,恢復到原來的狀態(tài)。
這就導致了才要socket進行連接的程序在進入后臺時會斷開連接,進入前臺需要重新連接。所以,根據(jù)需要,開發(fā)時需要保持后臺是“真后臺”。也就是程序在后臺依然保持運行狀態(tài)。
實現(xiàn)“真后臺”通常使用以下兩種方法:
(1)在后臺一直放歌(歌要為無聲)
(2)在后臺進行定位服務
比較兩種方法,個人認為,雖然兩種方法都是投機的方法,但是第二種比第一種更加合理。因為若程序一直在運行,若用戶在聽歌、接聽電話或者其他需要用到喇叭的操作時可能會收到影響。而定位可以一直不顯示,也不影響其他操作。
在此,著重介紹第二種方法在OC中的方法。
因為進入后臺等操作都屬于APP的生命周期,所以需要在AppDelegate中完成操作。
首先,在AppDelegate的.m文件中導入<CoreLocation/CoreLocation.h>,并加入CLLocationManagerDelegate的代理,并設置所需的東西,示例如下:
#import "AppDelegate.h"
#import <CoreLocation/CoreLocation.h>
@interface AppDelegate ()<CLLocationManagerDelegate>
//定位
@property (strong,nonatomic)CLLocationManager *locationManager;
//后臺任務標識符
@property (assign,nonatomic)UIBackgroundTaskIdentifier bgTask;
//終結處理程序的Block
@property (strong,nonatomic)dispatch_block_t expirationHandler;
//是否到達工作期限
@property (assign,nonatomic)BOOL jobExpired;
//是否在后臺
@property (assign,nonatomic)BOOL background;
@end
在didFinishLaunchingWithOptions方法中初始化locationManager并設置自動喚醒程序和綁定協(xié)議
UIApplication *app = [UIApplication sharedApplication];
__weak AppDelegate *selfRef = self;
self.expirationHandler = ^{ //創(chuàng)建后臺自喚醒
[app endBackgroundTask:selfRef.bgTask];
// 后臺任務無效
selfRef.bgTask = UIBackgroundTaskInvalid;
// 進入后臺任務終結處理
selfRef.bgTask = [app beginBackgroundTaskWithExpirationHandler:selfRef.expirationHandler];
selfRef.jobExpired = YES;
while(selfRef.jobExpired)
{
// 線程睡眠1s
[NSThread sleepForTimeInterval:1];
}
// 開始后臺任務類型
[selfRef startBackgroundTask];
};
// 檢測后臺的狀態(tài)
[self monitorBatteryStateInBackground];
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
上述中的相關方法
#pragma mark 檢測后臺的狀態(tài)
- (void)monitorBatteryStateInBackground
{
self.background = YES;
[self startBackgroundTask];
}
#pragma mark 開始后臺任務
- (void)startBackgroundTask
{
if([Api sharedApi].loginedUserId)//當?shù)顷憼顟B(tài)才進入后臺循環(huán)(此處需要自己設定,判斷用戶是否登錄)
{
// 開啟異步線程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSInteger count=0;
BOOL NoticeNoBackground=false;//只通知一次標志位
BOOL FlushBackgroundTime=false;//只通知一次標志位
self.locationManager.distanceFilter = kCLDistanceFilterNone;//任何運動均接受,任何運動將會觸發(fā)定位更新
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;//定位精度
while(self.background && !self.jobExpired){
[NSThread sleepForTimeInterval:1];
count++;
if(count>60)//每60s進行一次開啟定位,刷新后臺時間
{
count=0;
// 開始定位服務
[self.locationManager startUpdatingLocation];
[NSThread sleepForTimeInterval:1];
// 停止定位服務
[self.locationManager stopUpdatingLocation];
FlushBackgroundTime=false;
}
if(![Api sharedApi].loginedUserId)//未登錄或者掉線狀態(tài)下關閉后臺(此處需要自己設定,判斷用戶是否登錄)
{
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
return;//退出循環(huán)
}
NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining];
if(backgroundTimeRemaining<30&&NoticeNoBackground==false)
{
NoticeNoBackground=true;
}
//測試后臺時間刷新
if(backgroundTimeRemaining>200&&FlushBackgroundTime==false)
{
[[NSNotificationCenter defaultCenter]postNotificationName:@"MessageUpdate" object:@"刷新后臺時間成功\n"];
FlushBackgroundTime=true;
}
}
self.jobExpired = NO;
});
}
}
在生命周期的applicationDidBecomeActive方法中寫入
[UIApplication sharedApplication].applicationIconBadgeNumber=0;
// 停止定位服務
[self.locationManager stopUpdatingLocation];
self.background = NO;
通過定位協(xié)議的didFailWithError方法來判斷定位服務出錯的原因,并通過didUpdateLocations方法來獲取所定位的位置(注: didUpdateLocations中一定要獲取位置的改變,否則后臺刷新無用),代碼如下:
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error//當定位服務不可用出錯時,系統(tǒng)會自動調用該函數(shù)
{
// 自由發(fā)揮
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *loc = [locations lastObject];
float latitudeMe = loc.coordinate.latitude;
float longitudeMe = loc.coordinate.longitude;
NSLog(@"%f,%f",latitudeMe,longitudeMe);
}
最后,希望這篇文章對各位有所幫助。
此文參考《關于iOS后臺長時間掛起的方法》