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

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 <CoreLocation/CoreLocation.h>
2.在AppDelegate.m中定义CLLocationManager * locationManager;作为全局变量方便控制
3.在程序启动初期对定位服务进行初始化:

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

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

 __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/46820195

时间: 2024-12-19 19:36:21

保持程序在后台长时间运行-b的相关文章

iOS开发:后台运行以及保持程序在后台长时间运行

第一部分 1.先说说iOS 应用程序5个状态: 停止运行-应用程序已经终止,或者还未启动. 不活动-应用程序处于前台但不再接收事件(例如,用户在app处于活动时锁住了设备). 活动-app处于“使用中”的状态. 后台-app不再屏幕上显示,但它仍然执行代码. 挂起-app仍然驻留内存但不再执行代码. 按下Home键时,app从活动状态转入后台,绝大部分app通常在几秒内就从后台变成了挂起. 在内存吃紧的时候,iphone会首先关闭那些挂起的app. 从 iOS 4 开始,应用就可以在退到后台后,

关于ios中后台长时间下载任务的实现与走过的坑

先做一个简单的笔记 1.ios7之前实现后台长时间的任务是不可能完成的事情,即使使用timer监测一直索取的方式也不会打破ios系统最多允许应用后台运行10min的限定. 2.ios7之后出现了NSURLSession,这是一个福音.我们进行相应的配置,它就可以实现后台的长时间下载,并且超过系统的允许时间之后,程序被ios系统杀死,这个后台任务又由ios系统接手,并继续进行后台任务. 这是苹果的官方文档介绍:https://developer.apple.com/library/ios/docu

Android 应用程序退出后不在运行列表中显示的方法

使应用信息不在运行列表中显示的方法需要修改配置文件中activity标签的两个值 <span style="font-size:14px;">android:noHistory="true" android:excludeFromRecents="true"></span> 将这两个值置为true android:noHistory 这个属性用于设置在用户离开该Activity,并且它在屏幕上不再可见的时候,它是否应

iphone按home键后,正在运行的程序是否退出了呢?

是否一直有个疑问,当iphone手机正在运行一个APP,如果按Home键后,那么原来正在运行的程序还在运行吗?如果开发过ios程序,可能不是问题,如果没有开发过的,可能会疑惑了,我就简单的说一下.以几个问题的形式来说明,以正在运行QQ为例. 1.为什么按home键后,长时间不操作,再启动qq的话,会重新运行qq(没有回到原来的状态)? 答:iphone实际上是伪后台,按home键后,程序就退出了.所以我们iphone中使用的程序一般都没有退出键. 试想一下,如果程序有退出键的话,那么当程序无响应

程序编译后运行时的内存分配

原文地址不详,我的转载的来源:http://blog.sina.com.cn/s/blog_5420e0000101a0w1.html 一.编译时与运行时的内存情况 1.编译时不分配内存 编译时是不分配内存的.此时只是根据声明时的类型进行占位,到以后程序执行时分配内存才会正确.所以声明是给编译器看的,聪明的编译器能根据声明帮你识别错误. 2.运行时必分配内存 运行时程序是必须调到"内存"的.因为CPU(其中有多个寄存器)只与内存打交道的.程序在进入实际内存之前要首先分配物理内存. 3.

android开发之Intent.setFlags()_让Android点击通知栏信息后返回正在运行的程序

android开发之Intent.setFlags()_让Android点击通知栏信息后返回正在运行的程序 在应用里使用了后台服务,并且在通知栏推送了消息,希望点击这个消息回到activity, 结果总是存在好几个同样的activity,就算要返回的activity正在前台,点击消息后也会重新打开一个一样的activity,返回好几次才能退出, 而不能像qq之类的点击通知栏消息回到之前存在的activity,如果存在就不再新建一个activity 说的有点绕,如果是遇到此类问题的肯定能懂,没遇到

Linux中长时间运行程序的方法

一.场景: 如果临时有一个命令需要长时间运行,比如 python hello.py ,什么方法能最简便的保证它在后台稳定运行呢?解决方法:      当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程.因此,我们的解决办法就有两种途径:要么让进程忽略 HUP 信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程. 二.nohup nohup 无疑是我们首先想到的办法.顾名思义,nohup 的用途就是让提交的命令忽略 hangup 信号.只

结合程序崩溃后的core文件分析bug

结合程序崩溃后的core文件分析bug 引言 在<I/O的效率比较>中,我们在修改图1程序的BUF_SIZE为8388608时,运行程序出现崩溃,如下图1: 图1. 段错误 一般而言,导致程序段错误的原因如下: 内存访问出错,这类问题的典型代表就是数组越界. 非法内存访问,出现这类问题主要是程序试图访问内核段内存而产生的错误. 栈溢出, Linux默认给一个进程分配的栈空间大小为8M,因此你的数组开得过大的话会出现这种问题. 首先我们先看一下系统默认分配的资源: $ ulimit -acore

程序自启动及后台持续运行的研究(voip和GPS方式)

1,voip方式    1)首先要修改应用的plist配置,添加下面的设置:        Application does not run in background: NO        Required background modes: VOIP    注:添加这些配置之后,程序重启解锁就会自动运行application:                                  didFinishLaunchingWithOptions方法.    2)主要测试代码:- (BO