能源和网络
几乎所有的iOS应用程序都执行某种网络操作,有效地利用网络是至关重要的。这意味着尽可能通过减少和调度事务以及使用高效的api来消除开销成本。
有关网络的详细信息,请参阅Networking Overview.
设备网络开销
每当你的应用程序执行网络操作时,都会涉及大量的开销。默认情况下,网络硬件(如蜂窝式和Wi-Fi收音机)会断电以节省电池。这些资源必须通电才能执行活动。然后,他们在活动期间和一段额外的时间内保持清醒,以期待更多的工作。零星的网络事务会导致高开销,并会迅速耗尽设备的电池。见图8-1。
图8-1重复网络活动引起的开销示例
网络变量对电池能量的影响
各种因素影响在设备上执行网络操作所需的能量
- 蜂窝网络活动比Wi-Fi活动需要更多的能量。
- 信号条件差或波动可能会导致事务速度变慢或出现问题,必须重试。
- 低网络吞吐量(带宽)意味着无线电必须保持更长时间才能执行事务。
- 甚至地理位置和蜂窝提供商的选择也会影响能量消耗,因为信号条件和吞吐量可能不同。
因此,设备的技术规格通常为各种情况提供电池寿命估计。例如,iPhone 6s的规格表明,在3G和LTE上,互联网使用时间最长为10小时,在Wi-Fi上为11小时。应用程序的使用最终决定了网络流量的多少。应用程序使用网络的效率越高,设备的电池寿命就越长。
最小化网络
网络操作可能是不可避免的,对你的应用来说也是必不可少的。然而,在许多情况下,通过遵守一些一般准则,网络可以最小化。
减少数据大小
网络事务应该尽可能小,以减少开销。一定要成批处理您的事务,无论大小。请参阅Batch Transactions.
。
降低媒体质量和大小
如果你的应用上传、下载或流媒体内容,较低的质量和较小的尺寸会减少发送和接收的数据量。有些应用程序允许用户指定质量和大小。例如,在通过电子邮件发送照片时,邮件允许用户以小、中或大尺寸发送图像的缩放版本。最小的尺寸是最节能的
压缩数据
在发送或接收数据之前,使用压缩算法尽可能压缩数据。
避免冗余传输
你的应用不应该重复下载相同的数据。
缓存数据
使用缓存在本地存储不经常更新的数据。仅当数据已更改或用户请求时才重新下载该数据。NSURLCache和NSURLSession api可用于实现URL请求数据的内存和磁盘缓存。
使用可暂停和可恢复事务
网络条件波动,信号丢失可能是经常发生的。准备好恢复中断的事务,以便同一内容不会被多次下载。在某些情况下,让用户暂停下载并在以后继续下载是有意义的。例如,iOS允许用户通过点击部分下载的应用程序图标来暂停应用程序下载。
NSURLSession API允许您实现暂停和恢复功能,而无需实现缓存。
处理错误
当网络不可用时,不要尝试执行网络操作
检查信号条件
如果网络操作失败,请使用SCNetworkReachability API查看主机是否可用。如果出现信号问题,请通知用户或推迟工作,直到主机再次可用。
要确定主机是否可访问,请检查是否缺少kSCNetworkReachabilityFlagsReachable可达性标志,如清单9-1所示。
清单9-1检查主机的可用性
#import "SystemConfiguration/SCNetworkReachability.h"
...
// Create a reachability object for the desired host
NSString *hostName = @"someHostName";
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
// Create a place in memory for reachability flags
SCNetworkReachabilityFlags flags;
// Check the reachability of the host
SCNetworkReachabilityGetFlags(reachability, &flags);
// Release the reachability object
CFRelease(reachability);
// Check to see if the reachable flag is set
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
// The target host is not reachable
// Alert the user or defer the activity
}
NOTE
可以将网络操作限制为仅使用Wi-Fi,从而节省能源并减少遇到信号问题的可能性. 请参考 Restrict Cellular Networking Correctly 中 Networking Overview.
有关网络可达性的详细信息, 请参阅 SCNetworkReachability Reference. 还提供了显示如何监视iOS设备的网络状态的示例代码。参阅 Reachability.
提供逃生路线
不要永远等待服务器的响应。让用户取消长时间运行或暂停的网络操作,并设置适当的超时,这样您的应用程序就不会不必要地保持连接打开。
使用重试策略
如果事务失败,请在网络可用时重试。使用SCNetworkReachability API确定或在网络再次可用时收到通知
执行网络操作的应用程序应批量处理事务并使用适当的api,以尽量减少无线电使用,限制对系统的影响,并提高能效。应用程序还应允许系统在最佳时间延迟非必要的网络活动,例如设备插入或使用Wi-Fi时。
延迟联网
执行网络操作的应用程序应批量处理事务并使用适当的api,以尽量减少无线电使用,限制对系统的影响,并提高能效。应用程序还应允许系统在最佳时间延迟非必要的网络活动,例如设备插入或使用Wi-Fi时。
批处理事务
一次下载一点内容会让无线电通讯几乎连续通电,浪费电能。在可能的情况下,你的应用程序应该一起执行网络操作。例如:
如果你的应用程序流式传输视频,请立即下载整个文件(或文件的很大一部分),而不是以小块形式请求。
如果你的应用提供广告服务,一次下载几个,并在一段时间内显示,而不是根据需要下载。
如果应用程序从服务器下载电子邮件,请一次下载多封邮件。假设用户可能会阅读其中的大部分内容,而不是在用户选择时逐个下载。
延迟可延迟网络操作
对于通过HTTP上传和下载活动,NSURLSession API提供了创建可延迟后台会话的能力。后台会话允许你的应用向系统发送URL请求,系统在最佳时间执行这些请求,并在请求完成时通知你的应用。使用这种方法进行网络活动有几个优点:
- 活动是在进程外执行的。因为网络操作是由系统执行的,所以应用程序保持响应,允许用户继续执行其他工作。您的应用程序也不需要继续运行即可完成活动。
- 通知使您的应用程序保持最新状态。当活动完成并且出现问题时,系统会通知您的应用程序。你的应用程序甚至可以退出、重新启动、重新连接到上一个会话,并继续接收通知。如果您的应用程序在活动完成时未运行,或者需要验证,则系统可以在后台重新启动您的应用程序。
- 有效地执行网络活动。通过慢速连接执行网络活动效率低下。带宽监控允许系统在吞吐量低于某个阈值时延迟活动。
- 活动自我修正。出现错误时,系统可以自动重试URL会话。
如果用户强制终止已启动后台会话的应用程序,则后台活动也将取消。在恢复活动之前,必须手动重新启动应用程序。
配置后台会话选项
首先创建后台会话配置对象。给它一个唯一的会话标识符,并将其标记为一个自主活动。稍后,创建会话后,即使应用已终止并重新启动,也可以使用唯一标识符重新连接到会话。见清单10-1。
// Set up a configuration with a background session ID
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.<YourApp>.<YourBackgroundSessionIdentifier>"];
// Set the configuration to discretionary
[configuration setDiscretionary: YES];
不要忘记将配置标记为可自由选择。这样做可以告诉系统,该活动不需要立即执行,并且可以在电源最佳时间执行。
将后台会话限制为仅Wi-Fi
如果需要,将配置对象的allowCellularAccess属性设置为在连接到Wi-Fi时严格执行活动,如清单所示
configuration.allowsCellularAccess = NO;
对于备份和同步等大型事务,将网络活动限制为Wi-Fi尤其重要。例如,除非用户明确请求,否则不要尝试通过蜂窝连接下载整个全分辨率图像库或高质量音频或视频。这样做会耗尽电池,浪费带宽,甚至可能导致用户的运营商超龄费用。
调整后台会话的调度容限
默认情况下,系统最多允许运行七天的后台会话。这意味着在这个时间范围内的某个时间段内,活动将在功率最佳的时间发生。可以通过更改配置对象的TimeoutiterValForResource属性的值(以秒为单位指定)来调整时间范围。
[configuration setTimeoutIntervalForResource: 18 * 60 * 60];
由于网络状况经常波动,强烈建议至少分配12小时进行后台活动。理想情况下,容限应保持在默认的7天级别,除非有合理的理由更改它。
创建后台会话对象
设置后台会话配置对象后,为配置创建新的NSURLSession对象
NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
向后台会话添加URL请求
最后,创建一个NSURLRequest实例并将其传递给后台会话
// Set up a URL
NSURL *someURLToDownload = [NSURL URLWithString:<YourURLString>];
// Create a URL request
NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:someURLToDownload];
// Add the request to the background session
NSURLSessionDownloadTask *downloadTask = [backgroundSession downloadTaskWithRequest:downloadRequest];
// Initiate the activity
[downloadTask resume];
获得有关后台会话活动的通知
若要在后台会话从服务器接收答复、完成下载或遇到错误时接收回调,请实现相应的委托方法。在下列文档中可以找到可用委托方法的列表
IP语音(VoIP)最佳实践
VoiceoverInternetProtocol(VoIP)应用程序允许用户使用Internet连接而不是设备的蜂窝服务拨打和接收电话。由于VoIP应用程序严重依赖网络,因此打电话会导致高能耗也就不足为奇了。但是,如果不在使用中,VoIP应用程序应该完全空闲以节省能源。
使用VoIP推送通知避免持久连接
在过去,VoIP应用程序必须与服务器保持持久的网络连接才能接收来电和其他数据。这意味着编写复杂的代码,在应用程序和服务器之间来回发送周期性消息,以保持连接的有效性,即使应用程序不在使用中。这种技术导致频繁的设备唤醒,浪费了能量。这也意味着,如果用户退出VoIP应用程序,则无法再接收来自服务器的呼叫.开发人员应该使用PushKit框架api,而不是持久连接,该api允许应用程序从远程服务器接收推送(数据可用时的通知)。每当收到推送时,应用程序都会被调用。例如,VoIP应用程序可以在收到呼叫时显示警报,并提供接受或拒绝呼叫的选项。它甚至可以在用户决定接受的情况下开始采取前兆步骤来启动调用。
使用PushKit接收VoIP推送有许多优点:
- 只有当VoIP推送发生时,设备才会被唤醒,从而节省能源。
- 与标准推送通知(用户必须在应用程序执行操作之前作出响应)不同,> VoIP推送直接进入应用程序进行处理。
- VoIP推送被视为高优先级通知,并且无延迟地传递。
- VoIP推送可以包含比标准推送通知更多的数据。
- 如果接收到VoIP推送时应用程序未运行,则会自动重新启动。
- 即使你的应用程序在后台运行,你的应用程序也会在运行时处理推送。
在iOS 8及更高版本中提供PushKit VoIP支持。
准备接收VoIP推送通知
与所有支持后台操作的应用程序一样,您的VoIP应用程序必须在Xcode项目>功能窗格中启用后台模式。选中Voice over IP复选框,如图11-1所示
您还必须为VoIP应用程序创建证书。每个VoIP应用程序都需要自己的单独VoIP服务证书,该证书映射到唯一的应用程序ID。此证书允许通知服务器连接到VoIP服务。访问Apple Developer Member Center并创建新的VoIP服务证书。见图。下载证书并将其导入Keychain Access应用程序
配置VoIP推送通知
若要将应用程序配置为接收VoIP推送通知,请链接到应用程序委派中的PushKit框架(或应用程序中的其他位置)。然后,创建一个PKPushRegistry对象,将其委托设置为self,并注册以接收VoIP推送。请参见清单11-1。
#import <PushKit/PushKit.h>
// Trigger VoIP registration on launch
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self voipRegistration];
return YES;
}
// Register for VoIP notifications
- (void) voipRegistration {
dispatch_queue_t mainQueue = dispatch_get_main_queue()
// Create a push registry object
PKPushRegistry * voipRegistry = [[PKPushRegistry alloc] initWithQueue: mainQueue];
// Set the registry's delegate to self
voipRegistry.delegate = self;
// Set the push type to VoIP
voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}
接下来,实现一个委托方法来处理更新的推送凭据。如果你的应用同时收到标准推送通知和VoIP推送,那么你的应用将收到两个独立的推送令牌。两个令牌都必须传递到服务器才能接收通知。
// Handle updated push credentials
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials: (PKPushCredentials *)credentials forType:(NSString *)type {
// Register VoIP push token (a property of PKPushCredentials) with server
}
最后,设置一个委托方法来处理推送。如果您的应用在收到推送时未运行,则将自动启动您的应用。
// Handle incoming pushes
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
// Process the received push
}