关于iOS后台运行

关于iOS后台运行  

2012-08-23 23:17:40|  分类: iOS|字号 订阅

1, 首先iOS4.0以后,app有background状态的 ,这个状态一般是系统给应用预留的,让其可以在进入suspend(挂起)状态之前把未完成的任务完成,在app代理函数appDidEnterBackground中有 5s 的时间给你,如果5s不够,可以使用
UIBackgroundTaskIdentifier  bgTask=UIBackgroundTaskInvalid;
if (bgTask==UIBackgroundTaskInvalid){
   bgTask=[ [UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler :^{
     //后台任务到期时执行的代码,一般就是结束后台任务,并置bgTask为Invalid
    [[UIApplication sharedApplication] endBackgroundTask:bgTask];
    bgTask=UIBackgroundTaskInvalid;
  }];
   //只需要调用这一句代码即可,这样系统就会给你10分钟免费的后台时间让你执行代码。
}

但是后台任务只有 10分钟 ,如果你想让程序一直运行下去怎么办?

2,在Info.plist文件中指定或添加 UIBackgroundModes 键(直接输入后系统会将该键变为Required background modes),其类型为一个array,包含子项为字符串,可以为 voip、location、voip (注意这些值在你输入后系统会自动变更为其它的值,例如你输入location,系统会将其变为App registers for location updates,当然你也可以直接选择系统提供的项,只是注意这里写的值和系统给的值字面上是不一样的)

3、所以你如果想一直在后台运行代码,我最初的想法是通过 在后台使用一个[locationManager startUpdateLocation],然后其代理函数什么都不用做,其作用只是为了维持应用为后台状态,或把应用从suspend状态变回background状态 。但是这样状态栏会一直显示定位的标记,而且需要用户允许你定位才可以。而且最重要的是应该很费电吧。
注意: 使用[locationManager startMonitoringSignificantLocationChanges],这个定位服务可以把程序从未运行状态拉回后台状态,而且不需要指定 UIBackgroundModes 键。但是不能让后台任务运行超过10分钟,即使指定了 UIBackgroundModes 建为location,后台任务也不会超过10分钟
也就是说[locationManager startUpdateLocation]和[locationManager startMonitoringSignificantLocationChanges]的区别是一个能让后台任务一直运行,一个能把未运行的App拉回background状态。
可以两个都用,如果你的应用被退出了,那么如果用户移动了一段显著的位置后,App会被自动加载回background状态。


4、但是一直看状态栏显示一个定位的标志很不爽,所以决定试一下VoIP,按照官方说法,voip的程序会开机自动加载到后台(经实验,这个是真的),会在App意外退出(退出值不为0)后自动加载到后台(这个不会实验,代码exit(1),和手动结束程序都不能继续唤醒App,也许系统因为内存吃紧,将吃关闭的时候才可以恢复吧)。不过能在后台运行就差不多了,不要求那么多了。

1) 首先配置 UIBackgroundModes 值加入voip ,且增加了voip之后,即使不再指定audio,也能在后台播放音乐,但是官方建议还是加上audio。
2) 配置socket:这里我主要是借用voip,而不是真正实现voip,所以只是简单的用NSURLRequest类,创建一个NSMutableURLRequest实例,并设置其networkServiceType属性为NSURLNetworkServiceTypeVoIP
3) 在App代理函数applicationDidEnterBackground:中使用[application setKeepAliveTimeout:handler:]来为应用添加一个Keep-Alive Handler。一旦安装了Handler,系统会在timeout 之前至少调用一次handler,handler有10s的运行时间,否则系统会挂起应用。
timeout值要小一些,如果你打算15分钟唤醒一次,那么你就设置timeout小于15分钟,例如10分钟、12分钟等。因为系统虽然承诺调用handler,但是它会把handler的代码和其它系统周期性的任务放在一起执行,可能会延误一点时间。

注意:timout值不能小于600s

下面是测试及结论:
测试1-1:不指定 UIBackgroundModes 为location,启动startMonitoringSignificantLocationChanges
结果: 后台任务只能维持10分钟
测试1-2:指定 UIBackgroundModes 为location,启动startMonitoringSignificantLocationChanges
结果: 后台任务还是只能维持10分钟

结论1:startMonitoringSignificantLocationChanges不能让后台任务一直运行,即使指定了 UIBackgroundModes 为location,但是他能让App从未运行状态转到后台状态

测试2-1:在指定 UIBackgroundModes 为location时,不启动标准startUpdateLocation
结果:后台任务只能维持10分钟。
测试2-2:在指定 UIBackgroundModes 为location时,启动标准startUpdateLocation,在代理函数中将其停止
结果:后台任务还是只能维持10分钟,但是状态栏中定位的小标记不会消失。。
测试2-3:在指定 UIBackgroundModes 为location时,启动标准startUpdateLocation,然后不停止它
结果:后台任务可以无限跑下去。。。

结论2:即使指定了 UIBackgroundModes 为location,也需要 locationManager startUpdateLocation,并且不能停止才能让 App一直运行下去。


测试3-1:指定 UIBackgroundModes 为voip时,创建一个MSMutableURLRequest并设置属性,如下:
self.myRequest=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
self.myRequest.networkServiceType=NSURLNetworkServiceTypeVoIP;

然后在applicationDidEnterBackground:方法中设置一个handler 块,间隔10分钟多一点
- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [application setKeepAliveTimeout:600.1f handler:^{ [self setMyBgTask];}];  //此处注意timeout值不能少于600,并在Handler中加入启动后台任务的代码,因为超过10分钟,后台任务肯定已经停止了
    [self setMyBgTask];
}

结论:voip可以让app开机自动进入到后台状态,可以让app在后台运行,至于能不能让app从未运行状态到后台状态,还未确定。


================
另:转自其它文章

1、检查设备是否支持多任务

Apple出于性能的考虑,并不是所有的iOS设备升级到iOS4以后都支持多任务,比如iPhone 3G。如果你的应用在没有多任务特性时会出问题,为了保持应用的健壮性,你应该对此进行判断并处理。你可以通过调用UIDevice对象的multitaskingSupported属性来获取当前设备是否支持多任务。

if(![UIDevice currentDevice].multitaskingSupported){

    //不支持多任务时应做的处理

}



LBS是移动应用的一个大热点。很多App都允许(或者说要求)获取用户的地理位置。这篇文章将简要谈谈如何利用CoreLocation来获取地理位置,并且会涉及到如何在iPhone的模拟器进行调试。 

要利用CoreLocation,必须在frameworks里面加入“CoreLocation.framework”。在最新版本的Xcode(4.x)中加入新的framework步骤如下: 

单击项目的target =>在出来的xcodeproj面板中点击“Link Binary With Libraries” =>点击“+”,然后选择需要的framework即可。 

加入“CoreLocation.framework”之后,就可以在类中import ,这样就可以使用所有与CoreLocation相关的类了。 

下面开始在项目里面实现获取当前地理位置: 

第一步: 

先在类中定义两个成员变量: 

view plain 
CLLocationManager *locationManager;  
CLLocation *checkinLocation;  
这两个变量,locationManaager用于获取位置,checkinLocation用于保存获取到的位置信息。 
第二步: 

在类中实现CLLocationManagerDelegate,并实现以下方法: 

view plain 
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation   
                        fromLocation:(CLLocation *)oldLocation {  
    checkinLocation = newLocation;  
    //do something else  
}  

从代码可以看到,运行到这个方法的时候,locationManager已经得到了当前的位置,所以在这个方法中应该把获取到的当前位置保存到变量checkinLocation中。 
第三步: 

在第二步实现的方法其实是被CLLocationManager中的方法startUpdatingLocation调用的,也就是说,当程序运行 locationManager.startUpdatingLocation的时候,第二步实现的方法就会被调用。 

假设点击按钮checkIn的时候,程序会获取当前位置,那么就需要在这个checkIn按钮对应的动作方法(假设是方法名就叫做checkIn)中调用locationManager.startUpdatingLocation。具体如下: 

view plain 
- (void) setupLocationManager {  
    self.locationManager = [[[CLLocationManager alloc] init] autorelease];  
    if ([CLLocationManager locationServicesEnabled]) {  
    NSLog( @"Starting CLLocationManager" );  
    self.locationManager.delegate = self;  
    self.locationManager.distanceFilter = 200;  
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;  
    [self.locationManager startUpdatingLocation];  
    } else {  
        NSLog( @"Cannot Starting CLLocationManager" );  
        /*self.locationManager.delegate = self;  
    self.locationManager.distanceFilter = 200;  
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;  
    [self.locationManager startUpdatingLocation];*/  
    }  
}  

在上面的代码中,程序首先判断机器(iPhone/iPad)是不是开启了地理位置的服务(locationServicesEnabled)。如果开启了,那么就开始进行定位([self.locationManager startUpdatingLocation];)。在定位之前要设置一些有关位置的属性,比如distanceFilter,desiredAccuacy等等。至于这些属性有什么用,可以查看xcode的文档,这里就不在赘述。值得注意的是,一定要设置locationManager的delegate是类本身,这样startUpdatingLocation运行的时候才会调用第二步实现的方法。 
如果机器没有开启地理位置的服务,那么就不需要做多余的动作。注意到上面代码中有一部分代码被注释掉了,这部分代码这是为了能够在模拟器中调试程序。 

下面谈谈如何在模拟器中调试获取当前地理位置的程序。 

如果程序在真机中调试,只要真机开启了定位功能(GPS或者无线定位),那么就可以调试成功的。但是如果在模拟器中调试,就要做点额外功夫。由于在模拟器中是无法开启地理位置的服务(locationServicesEnabled总是等于false),所以需要将当前位置(或者任意位置)hardcode进程序当中,以便调试。 

将下面的代码添加进类中(实现了获取当前位置的代码所在的类文件),添加的位置在import代码段的后面: 

view plain 
@implementation CLLocationManager (TemporaryHack)  
  
- (void)hackLocationFix  
{  
    //CLLocation *location = [[CLLocation alloc] initWithLatitude:42 longitude:-50];  
    float latitude = 26.876812;   
    float longitude = 100.22569199999998;  //这里可以是任意的经纬度值  
    CLLocation *location= [[[CLLocation alloc] initWithLatitude:latitude longitude:longitude] autorelease];  
    [[self delegate] locationManager:self didUpdateToLocation:location fromLocation:nil];       
}  
  
- (void)startUpdatingLocation  
{  
    [self performSelector:@selector(hackLocationFix) withObject:nil afterDelay:0.1];  
}  
  
@end  

添加完这段代码之后,同时将第三步实现的方法中被注释掉的代码恢复,也就是即使locationServicesEnabled等于false也调用startUpdatingLocation方法,这样程序就可以在模拟器中正常运行了。但是注意的是,这时候获取的当前位置就是是上面代码中你自己所设定的经纬度值。 
整个过程都十分简单,因为大部分工作都被封装起来了。 

获得当前位置之后,就可以在地图上显示,下篇文章将会谈谈如何用地图显示地理信息,包括自定义Annotation

你可能感兴趣的:(ios)