iOS中后台运行

来源:互联网 发布:erp生产管理系统源码 编辑:程序博客网 时间:2024/06/05 07:31
     在iOS 4及以后的系统中,多任务机制允许应用程序即使在用户切换到其他应用程序之后继续在后台执行,并且仍然会尽可能地节省电量。大多数应用程序在进入后台之后就会被移到挂起状态。只有那些为用户提供重要服务的应用程序被允许继续执行更多的时间。
     当然,你被鼓励尽量避免在后台执行并且让你的应用程序进入挂起状态。如果你发现你需要执行一些后台任务,这里有一些指导:
  • 你需要实现至少一种用户服务。
  • 你需要执行一个有限时间长度的任务。
  • 你需要通过通知机制告诉用户一些信息你的程序什么时候停止运行。
     操作系统尽可能久地保持挂起的应用程序在内存中一段时间,仅仅在空闲内存不足的时候才移除他们。保留在内存中意味着接下来你的程序会以更快的速度启动,同时,处于挂起状态意味着你的程序不会过快地消耗电量。

检查多任务是否支持 

     应用程序必须准备好应对多任务机制(因此,后台执行)不可用的情况。即使运行在iOS 4及以后的系统中,设备依然可能不支持多任务机制,而且iOS 3及更早的系统不支持多任务机制。如果你的应用程序需要兼容运行在这些更早的iOS系统中,必须准备好在没有多任务的情况下运行。
     如果多任务机制的存在会影响到你的应用程序的行为,在执行相关任务前,检查UIDevice类的multitaskingSupported属性来判断系统是否支持多任务。当然,你的系统必须编译在iOS SDK 4以以上,相关代码如下所示。检查这个属性来允许你的应用程序继续运行在不支持多任务的更早的系统中。

Listing 3-2  Checking for background support in earlier versions of iOS

UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)])
   backgroundSupported = device.multitaskingSupported;
在后台执行有限时间长度的任务
    
     被移到后台的应用程序可以请求一个额外的时间来完成一些重要的"最后一分钟"任务。请求后台执行时间,需要调用UIApplication类的beginBackgroundTaskWithExpirationHandler: 方法。如果你的应用程序在任务执行过程中被移到后台,或者你的应用程序已经在后台,这个方法会延时应用程序的挂起。这对正在执行重要任务,如向磁盘写用户数据或者从网络服务器下载一个重要文件。
     使用beginBackgroundTaskWithExpirationHandler:方法的方式是在开始你需要保护的任务之前。每一次调用这个方法都必须对应地调用endBackgroundTask:方法来标记任务结束的地方。因为应用程序仅被给予有限的时间来完成后台任务,你必须在时间结束前调用这个方法;否则操作系统会终止你的应用程序。为了避免被终止,你也可以在开始任务时提供一个到期处理回掉并在其中调用 endBackgroundTask:方法。(你可以使用UIApplication类的backgroundTimeRemaining这个属性来知道还有多少剩余时间。)

重要: 一个应用可以有任意数量的任务在同一时间运行。每次你开始一个任务,beginBackgroundTaskWithExpirationHandler: 方法会为该任务返回一个唯一的标示符。你必须在任务时间结束是传递同一个标示符给endBackgroundTask:方法。

 

     下面一段代码显示了当你的应用程序被移到后台的时候如何开始一个long-running任务。在这个例子中,请求开始一个后台任务包括了一个到期处理回调来避免该任务运行太久。这个任务接下来被提交到一个异步执行的调度队列中,这样,applicationDidEnterBackground: 方法可以正常返回。这里使用blocks简化了需要维持一些变量的代码,如后台任务的标示符。这里的bgTask变量是一个类成员变量。

Listing 3-3  Starting a background task at quit time

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
        // Clean up any unfinished task business by marking where you
        // stopped or ending the task outright.
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
 
    // Start the long-running task and return immediately.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 
        // Do the work associated with the task, preferably in chunks.
 
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    });
}

注意: 在开始一个任务时总是提供一个过期回调,但是如果你想知道你的应用程序还剩多少时间来运行,获取UIApplication类的属性backgroundTimeRemaining.

     在你自己的过期处理函数中,你可以包含其它附加的代码来关闭你的任务。然而,任何你包换的代码都必须不能运行过长时间,因为在你的过期处理函数被调用的时候,你的应用程序已经非常接近时间限制,由于这个原因,在任务结束时仅仅执行最少的状态信息清除工作吧。
计划和发送本地通知 
     通知是一种后台挂起应用程序来获取用户注意的一种方式。应用程序使用本地通知来显示警告,播放声音,包含应用程序图标的通知条,或者三者的组合。例如,一个闹钟程序可能使用本地通知来播放一个闹铃并显示一个警告框用来关闭闹铃。当一个通知被穿递给用户时,用户必须决定这个通知信息是否值得将应用程序切会前台。(如果应用程序已经在前台运行了,那么本地通知会被安静地传递给应用程序而非用户。) 
     打算发起一个本地通知,创建一个UILocalNotification类的实例,配置通知参数,并且使用UIApplication类的方法将它列入计划。本地通知对象包括关于通知的类型(声音,警告或者通知条)和什么时候发送的时间。UIApplication类的方法提供立即发送通知或者在一个计划时间的选项。
     下面一段代码显示了用用户设置的时间和日期计划一个闹钟的例子。这个例子仅仅配置了一个闹钟在一个时间并取消了前一个闹钟在计划一个新闹钟的时候。(你自己的应用程序可以有不多于128个本地通知同时激活,任何一个都可以被配置为在一个间隔重复。)如果这个应用程序处于激活状态并在前台运行。那么app delegate的application:didReceiveLocalNotification: 这个方法会被调用而不会向用户发送通知。

Listing 3-4  Scheduling an alarm notification

- (void)scheduleAlarmForDate:(NSDate*)theDate
{
    UIApplication* app = [UIApplication sharedApplication];
    NSArray*    oldNotifications = [app scheduledLocalNotifications];
 
    // Clear out the old notification before scheduling a new one.
    if ([oldNotifications count] > 0)
        [app cancelAllLocalNotifications];
 
    // Create a new notification.
    UILocalNotification* alarm = [[UILocalNotification alloc] init];
    if (alarm)
    {
        alarm.fireDate = theDate;
        alarm.timeZone = [NSTimeZone defaultTimeZone];
        alarm.repeatInterval = 0;
        alarm.soundName = @"alarmsound.caf";
        alarm.alertBody = @"Time to wake up!";
 
        [app scheduleLocalNotification:alarm];
    }
}
     被用于本地通知的声音文件和用于推送通知的有相同的要求,自定义的声音文件必须在应用程序的main bundle中,支持如下的格式:Linear PCM, MA4, μ-Law, or a-Law.你也可以指定soundName为 default来播放默认的设备警告声音。当通知被发送并且声音被播放,系统还可能会触发一个震动.
     更多关于通知的信息可以了解UIApplication Class Reference. 和 Local and Push Notification Programming Guide.

实现 Long-Running 的后台任务 

     对于需要更多执行时间的任务,你必须请求特殊的权限来使他们在后台运行而不会被挂起。在iOS中,只有特殊的程序类型可以被允许在后台运行:
  • 在后台播放听觉内容给用户的应用程序,如一个音乐播放程序。
  • 需要一直保持用户地理信息的应用程序,如一个导航程序。
  • 支持VoIP协议的应用程序。
  • 新闻类应用程序,需要下载和推送最新的新闻内容。
  • 从附属接收常规更新的应用程序。
     实现了这些服务的应用程序必须声明他们支持的这些服务并且使用系统的框架来实现这些服务的相关细节。声明这些服务让系统知道哪些服务你需要使用,但同时也是让系统框架防止你的程序被挂起。

声明你的应用程序支持后台任务 

      为了支持某些类型的后台执行的操作,应用程序必须预先声明相关的服务。应用程序通过使用Info.plist文件来声明服务。添加UIBackgroundModes字段到Info.plist文件中并设置他的值为一个包含一下一个或者多个字符串的数组:
  • audio—应用程序在后台播放听觉内容给用户。(内容包括流媒体声音或者使用AirPlay播放的视频内容)
  • location需要一直保持用户地理信息的应用程序,即使程序在后台运行
  • newsstand-content支持VoIP协议的应用程序,提供互联网电话服务
  • external-accessory—应用程序和一些附属硬件打交道并需要通过External Accessory framework传递一些计划的常规更新。
  • bluetooth-central—应用程序和蓝牙设备打交道并需要通过Core Bluetooth framework传递一些计划的常规更新。
  • bluetooth-peripheral—应用程序支持通过Core Bluetooth framework进行一些外围的蓝牙通信。
     上述每一个值都使系统知道你的应用程序应该在合适的时候被唤醒来响应相关的时间。例如,一个正在播放音乐的播放器程序被移动后台执行,仍然需要运行时间来填充audio输出缓冲区。包含 audio关键字告诉系统框架,他应该继续播放,并且在合适的时机发起一些回调。如果应用程序没有包含这个关键字,任何正在播放的声音会在应用程序移动到后台之后被停止。
跟踪用户的地理位置
     这里有几种方式来在后台跟踪用户的位置,其中的大部分实际上不需要你的应用程序继续在后台运行:
  • significant-change location服务(推荐)
  • Foreground-only location服务
  • Background location服务
     对于那些不需要高精度位置数据的应用程序,significant-change location服务被强烈推荐使用。使用这个服务,地理位置的更新仅仅在用户的位置变化明显时才会产生;因此,这对社交程序或者给用户提供非重要位置相关信息的程序来说是个不错的选择。如果一个更新产生时程序被挂起,系统会在后台唤醒该程序来处理这个更新。如果应用程序启动了这项服务然后被终止了,在一个新的地理信息可用时系统会自动重启该应用。这项服务在iOS4 及之后的系统中被支持。
     Foreground-only location服务Background location服务都使用standard location Core Location service来接受地理数据。两者的不同仅仅在于Foreground-only location服务在程序被挂起后停止传递更新,这很有可能是在程序没有支持其他后台服务和任务时发生。Foreground-only location服务适合那些只在前台时需要地理信息的程序使用。
     
     连续提供地理位置更新给用户的程序(即使在后台)可以通过包含UIBackgroundModes字段(带上location值)在他的Info.plist文件中

重要: 开发者被鼓励使用标准服务或者 significant-location change服务。Location服务需要iOS设备上的无线硬件设备(GPS或者网络)一直处于激活状态。 连续运行这些设备会显著地消耗一些电量。如果你的应用不需要向用户提供精确而连续的地理信息,最好的选择是最小化location服务的使用。

     了解更多关于各种Location服务的信息,Location Awareness Programming Guide。

后台音频播放


     连续播放声音的应用程序(即使程序运行在后台)可以通过包含UIBackgroundModes字段(带上audio值)在他的Info.plist文件中来注册成为一个后台音频播放程序。包含了这个关键字的应用程序在后台时必须播放声音内容给用户,典型的后台声音播放程序包括:
  • 音乐播放程序
  • 支持视频或音频内容通过AirPlay播放的程序

  • VoIP程序

     当 UIBackgroundModes 字段中包含 audio 值时,系统的media frameworks会自动阻止相应的程序在进入后台时被挂起。 只要它一直播放声音或者视频内容,程序会继续在后台运行,而一旦程序停止播放,系统就会挂起它。

     你可以任何系统的audio frameworks来初始化后台音频的播放,使用这些框架的过程没有发生变化。(对于通过AirPlay播放的视频,你可以使用Media Player or AV Foundation framework来显示你的视频。)由于你的应用程序在播放媒体文件是没有被挂起,当它在后台运行时,回调操作会正常的被调用。因此,你的回调函数中应该仅仅做一些必要的工作来提供播放数据。例如,一个流媒体音乐程序需要下载音乐流数据并推送当前的音乐采样给播放装置,而不应该做一些和播放无关的额外操作。
     
     因为可能有多个程序支持音频,系统在一个时刻限制具体那个可以程序播放音频。前台程序总是有权限来播放音频。此外,一个或多个后台程序由他们的audio session对象的配置决定是否被允许播放声音内容。你应该合理地配置你的audio session对象并且仔细地处理系统框架产生的中断和其他声音相关的通知。更多关于audio session对象的内容,见 Audio Session Programming Guide.

实现一个VoIP程序

互联网电话协议 (VoIP) 应用程序允许用户通过互联网而不是移动电话服务来打电话。这种应用程序需要和相关服务器维持一个持久的网络链接 来接收来电或者其他相关数据。系统会允许这些程序被挂起,并替他们监听相关的socket,而不是让他们一直处于唤醒状态。当来电被监测到,系统会唤醒VoIP程序并将socket的控制权返回给它。

配置一个VoIP程序,你需要如下几步:

  1. 包含UIBackgroundModes字段(带上voip值)在他的Info.plist文件中
  2. 为VoIP设置一个socket

  3. 在被移至后台之前,调用 setKeepAliveTimeout:handler: 方法method来设置一个会被定期执行的处理函数。你的应用程序可以使用这个回调函数来处理和服务的通信。

  4. 配置audio session来处理传输和接受用户的通话。

包含UIBackgroundModes字段(带上voip值)会让系统知道应用程序需要在后台管理网络套接字。同时可以在系统启动后并确保VoIP服务可用时重启应用程序。
大多数VoIP应用程序同时也需要被设置为后台音频播放程序。更多关于关键字UIBackgroundModes的内容,见Information Property List Key Reference
更多关于VoIP程序的内容见 “Tips for Developing a VoIP App.”

后台下载新闻内容

     一个需要下载新的杂志或者报刊的新闻类应用程序可以注册后台进行这些下载。当你的服务器发送一个 推送通知指示有新的内容可用时,系统回去检查你的应用程序是否包含

UIBackgroundModes字段(带上

 

newsstand-content

 

值)。

 

如果是,系统会启动程序。

     当你使用Newsstand Kit framework来初始化下载时,系统会为你处理下载过程,在你的程序被挂起或者终止时,系统会继续下载文件。当下载完成时,系统会将文件移动到你的应用程序沙盒中并通知你的程序。如果程序没有运行,系统会唤醒程序并给它一个机会来处理新下载的文件。如果下载过程出错,你的程序也会被唤醒来处理出错事件。
     
     更多关于使用 

Newsstand Kit framework下载的内容参考

 

 Newsstand Kit Framework Reference.

  • 禁用后台运行


        如果完全不想让你的程序在后台运行,你可以通过添加UIApplicationExitsOnSuspend 字段 (带上YES的属性值) 到你程序的Info.plist文件中. 当一个应用程序禁用后台运行时,它的运行周期在not-running, inactive和active三种状态中循环,不会进入background或者suspended状态中。当用户按下Home键退出程序时,app delegate的 

    • applicationWillTerminate: 方法会被调用,并且应用程序有大约5秒的时间来完成清理和退出工作。

      •     iOS十分不推荐禁用后台运行,但是这可能是在一些特性情况下最好的选择。

    注意: 后台运行只有在 iOS SDK 4和之后的系统中被支持。


参考文献:     
iPhoneOSProgrammingGuide
读后:
   beginBackgroundTaskWithExpirationHandler:方法系统默认分配的有限时间为10分钟(从获知),但是如果时间到了依然有系统资源而任务还没有执行完毕会继续执行而不会触发到期回调函数。
   调用beginBackgroundTaskWithExpirationHandler:方法之后需要对应调用endBackgroundTask:调用方法标记任务结束的地方,否则容易被系统干掉。

原创粉丝点击