后台执行

iOS 在后台下载

2017-02-14  本文已影响676人  zziazm

文档
app在后台时会被暂停,暂停的apps会提高电池的使用寿命,并且会让系统将重要的系统资源投入到引起用户注意的前台应用中。
大部分apps可以容易地进入到挂起的状态,但是也有一些app需要在后台是保持运行状态。运动的app 想要跟踪用户在一段时间内的位置以便于他可以在地图上显示用户的路径;音乐app在锁屏时需要继续播放音乐;其他的app可能需要在后台做下载或上传操作。当你发现你的app需要在后台运行时,iOS可以帮助你高效地那样做:

执行有限长度的任务

进入后台的app希望尽快地进入到静止的状态以便于系统可以将它挂起,如果app正在处理任务并且需要一些额外的时间来在后台完成这个任务,可以调用- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithName:(nullable NSString *)taskName expirationHandler:(void(^ __nullable)(void))handler方法或者- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void(^ __nullable)(void))handler方法来请求一些额外的时间来处理任务。调用这两个方法中的任意一个会临时地推迟app的挂起,从而给app一些额外的时间处理任务。处理完任务后,你的app必须调用- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)identifier方法来让系统知道app已经完成了任务并且可以被挂起。
每一次调用- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithName:(nullable NSString *)taskName expirationHandler:(void(^ __nullable)(void))handler方法或者- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void(^ __nullable)(void))handler方法会生成唯一的token关联到对应的任务里。当app结束任务时,它必须调用- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)identifier使用对应的token让系统知道任务完成了。对于一个后台任务来说,调用- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)identifier失败将导致应用程序的终止。如果在启动任务时提供了一个过期处理,系统将调用该处理程序并且给予你做后的机会来结束这个任务并且避免app被终结。
下面的代码显示了当你的app进入后台时如何开启一个后台任务。
在这个例子中,开启后台任务的请求包括了一个过期的处理以免这个任务耗时过长。之后把这个任务提交到了异步执行的并发队列里以保证applicationDidEnterBackground:方法可以正常返回。block的使用简化了维持变量的引用的代码,例如后台任务的标示。bgTask变量是一个存储了指向当下后台任务标示的指针的类的成员变量,并且在方法中使用之前已经初始化。

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{
        // 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;
    });
}

在后台下载

在下载文件时,我们应该使用NSURLSession来开启下载。因为它可以让系统来控制下载的进程,即使app被挂起后或在后台被系统杀掉也不会受到影响。当你配置了一个NSURLSession对象用于后台下载时,系统会在一个独立的进程里管理那些下载,并且会把下载的状态反馈到你的app里。如果在下载过程中你的app在后台被系统杀掉了,系统会继续在后台里下载,并且在下载完成时系统会重新启动你的app。
为了支持后台下载,你必须适当地配置你的NSURLSession对象。
首先要创建一个NSURLSessionConfiguration对象并且设置一些属性:

实现常运行的任务

对于那些需要更多执行时间来实现的任务,你必须请求特定的权限来使app在后台运行而不会被挂起。在iOS里,只有特定的应用类型被允许在后台运行。

Declaring Your App’s Supported Background Tasks

支持某些后台运行的类型必须在使用它们的app里提前声明。在xcode5及更高的版本中,你从你的工程设置的Capabilities tab里声明你的app支持的后台模式。使能后台模式的选项会把UIBackgroundModes key添加到你的app的Info.plist文件里。下表列出了你可以指定的后台模式以及在你的Info.plist文件里xcode分配给UIBackgroundModes key的值。

Xcode background mode UIBackgroundModes values Description
Audio and AirPlay audio app在后台为用户播放语音或录制音频(这个内容包括了使用AirPlay的流式音频或视频)。在第一次使用之前用户必须授予app使用麦克风的权限,有关详情,请参阅 Supporting User Privacy
Location updates location 即使app在后台运行时,也会一直通知用户地理位置
Voice over IP voip app提供给了用户使用网络打电话的能力
Newsstand downloads newsstand-content 该应用是一个在后台下载和处理杂志或报纸内容的报亭应用。
External accessory communication external-accesory app同硬件配件一起工作,该配件需要通过External Accessory framework来定期地提供更新
Uses Bluetooth LE accessories bluetooth-central app同蓝牙配件一同工作,该配件需要通过Core Bluetooth framework定期地提供更新
Acts as a Bluetooth LE accessory bluetooth-periphral app支持通过Core Bluetooth framework在外设模式下的蓝牙通信。使用这个模式需要用户授权,详细信息,请参阅Supporting User Privacy
Background fetch fetch app定期从网络下载和处理少量的内容
Remote notifications remote-notification 当一个推送通知到达时,app想要开始下载内容。使用这个通知来最小化显示推送相关内容的延迟

每个模式都让系统知道你的app再合适的时间被唤醒或启动,用来响应相关的事件。例如,一个开始播放音乐然后移动到后台app仍然需要时间来填充音频的缓冲区。启用Audio模式来告诉系统框架仍它们将继续以适当的间隔对app进行适当的回调。如果app没有选择这个模式,当app移动到后台时,任何被app播放或录制的音频会被停止。
追踪用户的位置
有几种方法可以再后台里追踪用户的位置,大部分不需要app在后台里一直运行。

对于不需要高精度定位数据的app强烈推荐使用The significant-change location service,使用这个服务,只有当用户的位置发生重大改变时位置更新才会被生成。因此,对于社交的app或者对于用户来说位置信息不重要的app这种服务是理想的。当位置更新发生时,如果app处于挂起状态,系统会在后台唤醒app来处理更新。如果app开启了这个服务但是被杀掉了,当新位置更新时系统会自动地重启app。这个服务在iOS4及更高版本中可用,并且仅仅在包含了蜂窝无线电的设备上可以使用。
仅限前台的定位服务和后台的定位服务都使用了标准的Core Location service来检索位置数据。唯一的不同即使当app挂其时,仅在前台服务会停止传输位置更新。仅在前台定位服务适用于那些仅需要在前台定位的app。
你可以从xcode工程的Capabilities tab里的Background modes里打开定位支持。你也可以打开这个支持从app的Info.plist文件里使用UIBackgroundModes key和位置值。打开这个模式不会阻止系统挂起app,但它会告诉系统无论什么时候有新的位置更新时它都会唤醒app。因此,这个key高效地让app在后台运行来来处理位置更新。
鼓励您使用标准的服务或者The significant-change location service。定位服务需要主动使用iOS设备的无线电硬件。运行这个硬件可能连续不断地消耗大量的电力。如果你的app不需要为用户提供连续不断的和精准的位置信息,最好减少定位服务的使用。
在app中如何使用哪种定位服务,请参阅Location and Maps Programming Guide

播放和录制后台音频
持续播放或者录制音频的app(即使这个app运行在后台)可以注册用来在后台做那些任务。可以再Xcode工程的Capabilities tab里的Background modes打开音频支持。你也可以打开这个支持通过在info.plist文件里添加UIBackgroundModes key和音频值。在后台播放音频内容的app必须播放听得见的音频。
后台音频app的典型示例包括:

当UIBackgroundModes key包含了音频值的时候,系统的媒体框架自动地阻止app在后台时被挂起。只要app正在播放音频、视频或者正在录制音频,app在后台就能继续运。然而,如果播放或者录制音频停止了,系统会挂起app。
你可以使用任何系统音频框架来处理后台的音频内容。并且使用这些框架的过程不会改变。对于通过AirPlay的视频播放,你可以使用Media Player或AV Foundation框架来呈现你的视频。由于在播放媒体文件时你的app不会被挂起,因此当你的app在后台时毁掉也能正常地运行。但是在你的回调中,你仅仅需要做的必要的工作时提供回放的数据。例如,流式音频app需要从服务器下载音乐流数据并且并将当下的语音流推出做回放。app不应该做任何与播放无关的任务。

实现VoIP app
VoIP app 可以让用户通过网络连接打电话来代替蜂窝服务。这样的app需要稳固的网络连接来使它可以收到呼入和其他相关的数据。并不是保持VoIP设备一直运行,系统允许他们被挂起并且提供了设备来监听它们的socket。当检测到传入流量时,系统会唤醒VoIP app并且把socket的控制权返回给他。
要配置VoIP app,你必须做下列的事情:

在UIBackgroundModes key中包含VoIP的值让系统知道它将允许app在后台运行以管理它的网络socket。使用了这个key的app也会在系统启动后立即在后台重新启动以确保VoIp服务总是可用的。
大部分的VoIP app也需要被配置成后台音频app用来在后台时传输音频。因此,你因该在UIBackgroundModes里包含audio和voip的值。如果你不这样做,你的app在后台时不能播放和录制音频。更多的关于UIBackgroundmodes key的信息,请参阅nformation Property List Key Reference
关于实现VoiP应用需要采取的步骤的一些特殊的信息,请参阅Tips for Developing a VoIP App

在后台获取用户的注意力

通知是一种获取用户注意的方式对于那些被挂起,在后台或者没有运行的app。app可以使用本地通知来显示alerts,播放声音,icon的badge,或者是这三个的组合。例如,一个闹钟app可以使用本地通知来显示一个alert并且播放闹钟的声音。当一个通知被传递给用户时,用户必须确定这个信息是否授权把app带回到前台。如果app已经运行在前台,本地通知会静静地传递给app。
为了安排本地通知的传递,需要创建UILocalNotification的实例,配置这个通知的参数,并且使用UIApplication的方法安排它。本地通知对象包含了关于要传输的通知的类型(声音,alert,badge)和传输它的时间。UIApplication的方法提供了立即传递通知或者在计划的时间传递。
下面显示了一个使用用户的设定的日期和时间来安排一个闹钟。这个例子配置了唯一的一个闹钟,并且在安排新的之前取消了之前的闹钟。你自己的app在任意的时间内可以有不超过128个本地通知处于活跃状态。其中的任何一个都可以在指定的间隔里重复。在闹钟出发时如果app不在运行或者在后台时,这个通知会由警告框和语音文件组成。如果app是活跃的,这个app的代理方法- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)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];
    }
}

被用于本地通知的音频文件和用于推送通知的那些有一样的要求。自定义的音频文件必须被放置在app的main bundle里并且支持下列格式中的一种:Linear PCM, MA4, µ-Law, or a-Law。你也可以指定UILocalNotificationDefaultSoundName来播放默认的alert声音。当一个通知被发送并且播放声音时,系统也会出发设备震动。
你可以取消安排的通知或者获取一个通知的列表通过使用UIApplication的方法。关于那些方法的信息,请参阅 UIApplication Class Reference。关于配置本地通知的额外信息,请参阅Local and Remote Notification Programming Guide

理解你应用程序在后台启动

支持后台运行的app可能会被系统重启来处理收到的事件,如果系统被某些原因杀掉了但不是用户强制的,当下列的事件发生时app会被重启:

做可靠地后台app

当涉及到系统资源和硬件时,前台的app总是优先于后台的app。在后台运行的APP需要为这个差异做好准备并调整行为。具体来说,在后台的app应当遵守下面的准则:

上一篇 下一篇

猜你喜欢

热点阅读