保持程序在后台长时间运行-b

iOS为了让设备尽可能省电,减小没必要要的开销,保持系统流畅,于是对后台机制采用墓碑式的“假后台”。除了系统官方极少数程序能够真后台,通常开发者开发出来的应用程序后台受到如下限制:
1.用户按Home以后,App转入后台进行运行,此时拥有180s后台时间(iOS7)或者600s(iOS6)运行时间能够处理后台操做
2.当180S或者600S时间过去以后,能够告知系统未完成任务,须要申请继续完成,系统批准申请以后,能够继续运行,但总时间不会超过10分钟。
3.当10分钟时间到以后,不管怎么向系统申请继续后台,系统会强制挂起App,挂起全部后台操做、线程,直到用户再次点击App以后才会继续运行。git

固然iOS为了特殊应用也保留了一些能够实现“真后台”的方法,摘取比较经常使用的:
1.VOIP
2.定位服务
3.后台下载
4.在后台一直播放无声音乐(容易受到电话或者其余程序影响,因此暂未考虑)
5….更多
其中VOIP须要绑定一个Socket连接并申明给系统,系统将会在后台接管这个链接,一旦远端数据过来,你的App将会被唤醒10s(或者更少)的时间来处理数据,超过期间或者处理完毕,程序继续休眠。
后台如今是iOS7引入的新API,网上实现的代码比较少,博主也没有细心去找。
因为博主要作的App须要在后台一直运行,每隔一段时间给服务器主动发送消息来保持账号登录状态,于是必须确保App不被系统墓碑限制。
博主最早尝试了不少方法,包括朋友发来的一个Demo,每180s后台时间过时就销毁本身而后再建立一个后台任务,可是实际测试只有10分钟时间。最后由于考虑到VOIP对服务端改动太大,时间又太紧,因此选择了定位服务的方法来保持后台。服务器

要启动定位服务:
1.须要引入头文件:#import <CoreLocation/CoreLocation.h>
2.在AppDelegate.m中定义CLLocationManager * locationManager;做为全局变量方便控制
3.在程序启动初期对定位服务进行初始化:app

locationManager = [[CLLocationManager alloc] init];
locationManager.delegate =self;
//or whatever class you have for managing location</pre>

4.在程序转入后台的时候,启动定位服务
[locationManager startUpdatingLocation];(第一次运行这个方法的时候,若是以前用户没有使用过App,则会弹出是否容许位置服务,关于用户是否容许,后面代码中有判断)
这样在定位服务可用的时候,程序会不断刷新后台时间,实际测试,发现后台180s时间不断被刷新,达到长久后台的目的。async

可是这样使用也有一些问题,在部分机器上面,定位服务即便打开也可能不能刷新后台时间,须要彻底结束程序再运行。稳定性不知道是由于代码缘由仍是系统某些机制缘由。函数

下面贴上代码:
注意:代码中包含朋友给的demo中,180s时间后销毁本身再建立本身的后台方法,我本身实现过程当中加入了定位服务来确保后台可以一直在线。
源码参考部分来自网上,由于翻了Google,找了不少英文方面的博文,在此感谢原做者分享。oop

判断用户是否打开了定位服务,是否禁用了该程序的定位权限:post

if(![CLLocationManager locationServicesEnabled] || ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied))//判判定位服务是否打开
    {
        [InterfaceFuncation ShowAlertWithMessage:@"错误"AlertMessage:@"定位服务未打开\n保持在线须要后台定位服务\n请到 设置-隐私 中打开定位服务"ButtonTitle:@"我错了"];
        return;
    }

AppDelegate.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];

 __weakNSUAAAIOSAppDelegate* selfRef =self;

 self.expirationHandler = ^{ //建立后台自唤醒,当180s时间结束的时候系统会调用这里面的方法
 [app endBackgroundTask:selfRef.bgTask];
 selfRef.bgTask = UIBackgroundTaskInvalid;
 selfRef.bgTask = [app beginBackgroundTaskWithExpirationHandler:selfRef.expirationHandler];
 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] beginBackgroundTaskWithExpirationHandler:self.expirationHandler];
 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 = loc.coordinate.latitude;
 floatlongitudeMe = loc.coordinate.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;
    });
 }
}

原文地址:http://blog.csdn.net/x931100537/article/details/46820195ui

相关文章
相关标签/搜索