iOS如何保持程序在后台长时间运⾏
iOS 为了让设备尽量省电,减少不必要的开销,保持系统流畅,因⽽对后台机制采⽤墓碑式的“假后台”。除了系统官⽅极少数程序可以真后台,⼀般开发者开发出来的应⽤程序后台受到以下限制:
1.⽤户按Home之后,App转⼊后台进⾏运⾏,此时拥有180s后台时间(iOS7)或者600s(iOS6)运⾏时间可以处理后台操作
2.当180S或者600S时间过去之后,可以告知系统未完成任务,需要申请继续完成,系统批准申请之后,可以继续运⾏,但总时间不会超过10分钟。
3.当10分钟时间到之后,⽆论怎么向系统申请继续后台,系统会强制挂起App,挂起所有后台操作、线程,直到⽤户再次点击App之后才会继续运⾏。
当然iOS为了特殊应⽤也保留了⼀些可以实现“真后台”的⽅法,摘取⽐较常⽤的:
1.VOIP
2.定位服务
3.后台下载
4.在后台⼀直播放⽆声⾳乐(容易受到电话或者其他程序影响,所以暂未考虑)
5….更多
其中VOIP需要绑定⼀个Socket链接并申明给系统,系统将会在后台接管这个连接,⼀旦远端数据过来,你的App将会被唤醒10s(或者更少)的时间来处理数据,超过时间或者处理完毕,程序继续休眠。
后台现在是iOS7引⼊的新API,⽹上实现的代码⽐较少,博主也没有细⼼去。
由于博主要做的App需要在后台⼀直运⾏,每隔⼀段时间给服务器主动发送消息来保持帐号登陆状态,因⽽必须确保App不被系统墓碑限制。
博主最先尝试了很多⽅法,包括朋友发来的⼀个Demo,每180s后台时间过期就销毁⾃⼰然后再创建⼀个后台任务,但是实际测试只有10分钟时间。最后因为考虑到VOIP对服务端改动太⼤,时间⼜太紧,所以选择了定位服务的⽅法来保持后台。
要启动定位服务:
1.需要引⼊头⽂件:#import
在线代码运行器2.在AppDelegate.m中定义CLLocationManager * locationManager;作为全局变量⽅便控制
3.在程序启动初期对定位服务进⾏初始化:
locationManager = [[CLLocationManager alloc] init];locationManager.delegate =self;
//or whatever class you have for managing location
4.在程序转⼊后台的时候,启动定位服务
[locationManager startUpdatingLocation];(第⼀次运⾏这个⽅法的时候,如果之前⽤户没有使⽤过App,则会弹出是否允许位置服务,关于⽤户是否允许,后⾯代码中有判断)
这样在定位服务可⽤的时候,程序会不断刷新后台时间,实际测试,发现后台180s时间不断被刷新,达到长久后台的⽬的。
但是这样使⽤也有⼀些问题,在部分机器上⾯,定位服务即使打开也可能不能刷新后台时间,需要完全结束程序再运⾏。稳定性不知道是因为代码原因还是系统某些机制原因。
下⾯贴上代码:
注意:代码中包含朋友给的demo中,180s时间后销毁⾃⼰再创建⾃⼰的后台⽅法,我⾃⼰实现过程中加⼊了定位服务来确保后台能够⼀直在线。
源码参考部分来⾃⽹上,因为翻了Google,了很多英⽂⽅⾯的博⽂,在此感谢原作者分享。
判断⽤户是否打开了定位服务,是否禁⽤了该程序的定位权限:
if(![CLLocationManager locationServicesEnabled] || ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied))//判断定位服务是否打开
{
[InterfaceFuncation ShowAlertWithMessage:@"错误"AlertMessage:@"定位服务未打开\n保持在线需要后台定位服务\n请到设置-隐私中打开定位服务"ButtonTitle:@"我错了"];
return;
}
A ppDelegate.m源码:
@property(assign, nonatomic) UIBackgroundTaskIdentifier bgTask;
@property(strong, nonatomic) dispatch_block_t expirationHandler;
@property(assign, nonatomic)BOOL jobExpired;
@property(assign, nonatomic)BOOL background;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
UIApplication* app = [UIApplication sharedApplication];
__weak NSUAAAIOSAppDelegate* selfRef =self;
[app endBackgroundTask:selfRef.bgTask];
selfRef.bgTask = UIBackgroundTaskInvalid;
selfRef.bgTask = [app pirationHandler];
NSLog(@"Expired");
selfRef.jobExpired =YES;
while(selfRef.jobExpired)
{
// spin while we wait for the task to actually end.
NSLog(@"等待180s循环进程的结束");
[NSThreadsleepForTimeInterval:1];
}
// Restart the background task so we can run forever.
[selfRef startBackgroundTask];
};
// Assume that we're in background at first since we get no notification from device that we're in background when
// app launches immediately into background (i.e. when powering on the device or when the app is killed and restarted)
[selfmonitorBatteryStateInBackground];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate =self;
//[locationManager startUpdatingLocation];
returnYES;
}
- (void)monitorBatteryStateInBackground
{
self.background =YES;
[selfstartBackgroundTask];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
NSLog(@"App is active");
[UIApplication sharedApplication].applicationIconBadgeNumber=0;//取消应⽤程序通知脚标
[locationManager stopUpdatingLocation];
self.background =NO;
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
//if([self bgTask])
if(isLogined)//当登陆状态才启动后台操作
{
self.bgTask = [[UIApplication sharedApplication] pirationHandler];
NSLog(@"Entered background");
[selfmonitorBatteryStateInBackground];
}
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError*)error//当定位服务不可⽤出错时,系统会⾃动调⽤该函数
{
NSLog(@"定位服务出错");
if([error code]==kCLErrorDenied)//通过error的code来判断错误类型
{
//Access denied by user
NSLog(@"定位服务未打开");
[InterfaceFuncation ShowAlertWithMessage:@"错误"AlertMessage:@"未开启定位服务\n客户端保持后台功能需要调⽤系统的位置服务\n请到设置中打开位置服务"ButtonTitle:@"好"];
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray*)locations//当⽤户位置改变时,系统会⾃动调⽤,这⾥必须写⼀点⼉代码,否则后台时间刷新不管⽤
{
NSLog(@"位置改变,必须做点⼉事情才能刷新后台时间");
CLLocation *loc = [locations lastObject];
//NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining];
//NSLog(@"Background Time Remaining = %.02f Seconds",backgroundTimeRemaining);
/
/ Lat/Lon
floatlatitudeMe = dinate.latitude;
floatlongitudeMe = dinate.longitude;
}
- (void)startBackgroundTask
{
NSLog(@"Restarting task");
if(isLogined)//当登陆状态才进⼊后台循环
{
// Start the long-running task.
NSLog(@"登录状态后台进程开启");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// When the job expires it still keeps running since we never exited it. Thus have the expiration handler
// set a flag that the job expired and use that to exit the while loop and end the task.
NSIntegercount=0;
BOOLNoticeNoBackground=false;//只通知⼀次标志位
BOOLFlushBackgroundTime=false;//只通知⼀次标志位
locationManager.distanceFilter = kCLDistanceFilterNone;//任何运动均接受,任何运动将会触发定位更新
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;//定位精度
while(self.background && !self.jobExpired)
{
NSLog(@"进⼊后台进程循环");
[NSThreadsleepForTimeInterval:1];
count++;
if(count>60)//每60s进⾏⼀次开启定位,刷新后台时间
{
count=0;
[locationManager startUpdatingLocation];
NSLog(@"开始位置服务");
[NSThreadsleepForTimeInterval:1];
[locationManager stopUpdatingLocation];
NSLog(@"停⽌位置服务");
FlushBackgroundTime=false;
}
if(!isLogined)//未登录或者掉线状态下关闭后台
{
NSLog(@"保持在线进程失效,退出后台进程");
[InterfaceFuncation ShowLocalNotification:@"保持在线失效,登录已被注销,请重新登录"];
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
return;//退出循环
}
NSTimeIntervalbackgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining];
NSLog(@"Background Time Remaining = %.02f Seconds",backgroundTimeRemaining);
if(backgroundTimeRemaining<30&&NoticeNoBackground==false)
{
[InterfaceFuncation ShowLocalNotification:@"向系统申请长时间保持后台失败,请结束客户端重新登录"];
NoticeNoBackground=true;
}
//测试后台时间刷新
if(backgroundTimeRemaining>200&&FlushBackgroundTime==false)
{
[[NSNotificationCenterdefaultCenter] postNotificationName:@"MessageUpdate"object:@"刷新后台时间成功\n"];
FlushBackgroundTime=true;
//[InterfaceFuncation ShowLocalNotification:@"刷新后台时间成功"];
}
}
self.jobExpired =NO;
});
}
}
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论