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;
}

​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];
  __weak NSUAAAIOSAppDelegate* 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;
         });
     }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • IOS 时间和时间戳之间转化示例

    以毫秒为整数值的时间戳转换 时间戳转化为时间NSDate - (NSString *)timeWithTimeIntervalString:(NSString *)timeString { // 格式化时间 NSDateFormatter* formatter = [[NSDateFormatter alloc] init]; formatter.timeZone = [NSTimeZone timeZoneWithName:@"shanghai"]; [formatter setDa

  • iOS获取当前时间和当前时间戳的方法

    //获取当前的时间 +(NSString*)getCurrentTimes{ NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; // ----------设置你想要的格式,hh与HH的区别:分别表示12小时制,24小时制 [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; //现在时间,你可以输出来看下是什么格式 NSDate *datenow = [NSDate d

  • Android开发中实现IOS风格底部选择器(支持时间 日期 自定义)

    本文Github代码链接 https://github.com/AndroidMsky/AndoirdIOSPicker 先上图吧: 这是笔者最近一个项目一直再用的一个选择器库,自己也在其中做了修改,并决定持续维护下去. 先看使用方法: 日期选择: private void showDateDialog(List<Integer> date) { DatePickerDialog.Builder builder = new DatePickerDialog.Builder(this); bui

  • iOS中使用UIDatePicker制作时间选择器的实例教程

    UIDatePicker的创建 UIDatePicker是一个可以用来选择或者设置日期的控件,不过它是像转轮一样的控件,而且是苹果专门为日历做好的控件,如下图所示: 除了UIDatePicker控件,还有一种更通用的转轮形的控件:UIPickerView,只不过UIDatePicker控件显示的就是日 历,而UIPickerView控件中显示的内容需要我们自己用代码设置.本篇文章简单介绍UIDatePicker控件,后边的文章会介绍 UIPickerView. 1.运行Xcode ,新建一个Si

  • 详解iOS时间选择框

    本文实例为大家介绍了iOS时间选择框的示例代码,供大家参考,具体内容如下 代码: 一.头文件 #import <UIKit/UIKit.h> @class ITTPickView; @protocol ITTPickViewDelegate <NSObject> @optional -(void)toobarDonBtnHaveClick:(ITTPickView *)pickView resultString:(NSString *)resultString; @end @int

  • iOS 获得现在的时间代码

    一, 代码. - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //获得现在的时间 [self currentTime]; } #pragma -mark -functions //计算现在的时间 - (void)currentTime { //时间格式 NSDateFormatter *dateFormatter

  • iOS如何保持程序在后台长时间运行

    iOS 为了让设备尽量省电,减少不必要的开销,保持系统流畅,因而对后台机制采用墓碑式的"假后台".除了系统官方极少数程序可以真后台,一般开发者开发出来的应用程序后台受到以下限制: 1.用户按Home之后,App转入后台进行运行,此时拥有180s后台时间(iOS7)或者600s(iOS6)运行时间可以处理后台操作 2.当180S或者600S时间过去之后,可以告知系统未完成任务,需要申请继续完成,系统批准申请之后,可以继续运行,但总时间不会超过10分钟. 3.当10分钟时间到之后,无论怎么

  • iOS实现后台长时间运行

    前言 一般APP在按下Home键被挂起后,这时APP的 backgroundTimeRemaining 也就是后台运行时间大约只有3分钟,如果在退出APP后,过十几二十二分钟或者更长时间再回到APP,APP就会回到刚打开时的状态,也就是首页:有的项目在被挂起后需要在后台运行一段时间,使有足够的时间来完成与服务器对接的操作,或者需要一直运行的需求:如果需要,则在APP被挂起后,申请后台,来延长后台运行时间. APP申请后台运行的方式有几种: 播放音乐 定位 Newsstand downloads

  • pytorch程序异常后删除占用的显存操作

    1-删除模型变量 del model_define 2-清空CUDA cache torch.cuda.empty_cache() 3-步骤2(异步)需要一定时间,设置时延 time.sleep(5) 完整代码如下: del styler torch.cuda.empty_cache() time.sleep(5) 以上这篇pytorch程序异常后删除占用的显存操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • 微信小程序使用picker实现时间和日期选择框功能【附源码下载】

    本文实例讲述了微信小程序使用picker实现时间和日期选择框功能.分享给大家供大家参考,具体如下: 1.效果展示 2.关键代码 ① index.wxml <picker value="{{picker1Value}}" range="{{picker1Range}}" bindchange="normalPickerBindchange"> 当前城市选择:{{picker1Range[picker1Value]}} </pick

  • C++ 程序抛出异常后执行顺序说明

    1 析构函数中是否可以抛出异常 首先我们看一个常见的问题,析构函数中是否可以抛出异常.答案是C++标准指明析构函数不能.也不应该抛出异常! C++异常处理模型是为C++语言量身设计的,更进一步的说,它实际上也是为C++语言中面向对象而服务的. C++异常处理模型最大的特点和优势就是对C++中的面向对象提供了最强大的无缝支持. 那么如果对象在运行期间出现了异常,C++异常处理模型有责任清除那些由于出现异常所导致的已经失效了的对象(也即对象超出了它原来的作用域),并释放对象原来所分配的资源, 这就是

  • 如何在Linux中的特定时间运行命令

    有一天,我使用 rsync 将大文件传输到局域网上的另一个系统.由于它是非常大的文件,大约需要 20 分钟才能完成.我不想再等了,我也不想按 CTRL+C 来终止这个过程.我只是想知道在 Linux 操作系统中是否有简单的方法可以在特定的时间运行一个命令,并且一旦超时就自动杀死它 -- 因此有了这篇文章.请继续阅读. 在 Linux 中在特定时间运行命令 我们可以用两种方法做到这一点. 方法 1 - 使用 timeout 命令 最常用的方法是使用 timeout 命令.对于那些不知道的人来说,t

  • 使用winrar打包air程序为exe(包含air运行环境)

    Adobe AIR安装率实在太低了.所以很必要对你的air程序进行打包.那个Shu我是没用过,广告打得很猛,但是完全可以用winrar就达到打包air的目的. 1.准备好你需要打包的文件.去你的flex sdk的bin中找到adl.exe,拷贝过来:然后在把flex sdk中的runtimes文件夹也拷贝出来.这些和你需要打包的文件放在同一目录下.如图: adl.exe和runtimes主要是用来创建Adobe air运行环境,让你的程序能在没有安装Adobe air的机器上也能运行起来. 2.

  • Oracle数据库新装之后出现的监听程序无法正常启动和运行(Oracle-12514)问题

    修改安装目录下的配置文件 比如:F:\app\admin-PC\product\11.2.0\dbhome_1\network\admin\ 修改这个目录下的listener.ora和tnsnames.ora ************************************************************************************** listener.ora: SID_LIST_LISTENER = (SID_LIST = (SID_DESC =

  • 易语言检测程序是不是在虚拟机中运行的代码

    汇编检测虚拟机的代码 .版本 2 .程序集 窗口程序集1 .子程序 __启动窗口_创建完毕, , , .子程序 检测虚拟机, 逻辑型 .局部变量 a, 整数型 .如果 (try_ ()) a = VMWare检测 () try_out () .否则 .如果结束 .如果真 (a ≠ 0) 返回 (真) .如果真结束 .如果 (try_ ()) a = VPC检测 () try_out () .否则 .如果结束 返回 (a ≠ 0) .子程序 VMWare检测, 整数型, , 此函数不能独立在实体机

  • 微信小程序加载机制及运行机制图解

    这篇文章主要介绍了微信小程序加载机制及运行机制图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.运行机制 冷启动指的是重新启动,热启动指的是5分钟内从后台切换到前台,只有冷启动才能加载最新的包. 小程序什么时候会关闭? 5min后台运行,连续收到两次(2s)系统告警. 二.加载机制 三.小程序的应用生命周期 四.小程序页面的生命周期 分类: 小程序以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们.

随机推荐