在移动设备上,电池寿命是用户非常关心的问题。如果一个应用消耗过多电量,用户可能会选择卸载它。知道,设备上的几乎所有进程都会消耗电量。本文将探讨一些能效理论,以帮助开发者优化应用,减少电量消耗。
理解电量消耗的来源是优化的第一步。
几乎所有的进程都需要使用CPU。因此,有效的CPU利用是必要的。这只能通过更好的任务优化来实现,比如批处理、优先级排序和调度。
知道,当设备处于睡眠模式时,它消耗的电量较少。一旦设备唤醒,大部分部件开始工作,消耗更多电量。确保应用允许设备睡眠,并在非常必要时才唤醒设备。
当应用中的网络层开始工作时,应用会消耗更多电量。在这里,需要显著减少电量消耗。
每一次屏幕更新都会消耗额外的电量。标准窗口和控件已经设计得非常节能。但是,当涉及到自定义窗口和控件时,确保内容在新数据可用之前不会被重新渲染。尽量避免使用相同内容刷新UI,并在视图不可见时刷新内容。
如果应用持续跟踪用户的物理位置,并尝试以高精度访问位置数据,将导致电池消耗增加。因此,在使用位置API时,需要智能实现和更好的规划。
本文将重点介绍可以应用优化以实现高效电池使用的主要领域。
首先,让看看可能导致不必要电池使用的活动:
以下是应用进入后台时的一些能效编码实践:
当应用调用applicationWillResignActive
时,应该允许应用暂停或停止进程,保存数据或停止任何UI更新。这个代理在应用因来电、短信或用户切换到另一个应用而移动到非活动状态之前被调用。
func applicationWillResignActive(_ application: UIApplication) {
// 在这里执行所有关闭活动
}
如果应用在挂起之前需要更多时间来完成任务,API beginBackgroundTaskWithExpirationHandler
提供了额外的执行时间。当任务完成时,必须调用endBackgroundTask
:以通知系统任务已完成。
func applicationDidEnterBackground(_ application: UIApplication) {
var bgTask = UIApplication.shared.beginBackgroundTask {
// 这个闭包会在后台剩余时间接近零时被调用。
// 在挂起之前执行所有清理活动。
}
DispatchQueue.global().async {
// 在这里执行任务
// 任务完成后,调用结束任务。系统会在挂起之前调用beginBackgroundTask的闭包
UIApplication.shared.endBackgroundTask(bgTask)
bgTask = UIBackgroundTaskInvalid
}
}
后台应用刷新API是更新内容的首选方式,而不是使用定时器。
应尽量避免使用定时器。如果UI更新可以在用户交互或新内容可用时进行,这是一个好习惯。如果定时器执行对于应用场景是必需的,那么增加间隔是一个好主意。
如果应用可以有效地利用多核、内存等,通过应用并行性和并发性,那么它不仅响应更快,而且能效更高。
当应用执行操作和队列时,可以指定QoS。这将允许系统调整工作的优先级,以进行调度、CPU和I/O分配以及定时器延迟。
以下是主要的QoS:
以下是如何使用QoS优先级创建workItem
的代码示例。
let userInitiatedWorkItem = DispatchWorkItem(qos: .userInitiated, flags: .assignCurrentContext) {
// 在这里执行适当的任务
}
DispatchQueue.main.async(execute: userInitiatedWorkItem)
let bgWorkItem = DispatchWorkItem(qos: .background, flags: .assignCurrentContext) {
// 在这里执行适当的任务
}
DispatchQueue.main.async(execute: bgWorkItem)
利用缓存数据——应用不应反复获取相同的数据。这会导致额外的成本和额外的电量消耗。在内容更改或用户交互时下载数据。
定义可暂停和可恢复的网络加载——NSURLSession允许开发者在交易中断时暂停或恢复事务。这将帮助应用不再重复上传相同的内容。
如果可能,传输压缩数据。
如果网络类在网络环境不佳或频繁断开连接的环境中不断尝试网络加载,或者用户将allowsCellularAccess
设置为false。从iOS 11开始,有一个新的API叫做waitsForConnectivity
和一个全新的委托叫做URLSession:taskIsWaitingForConnectivity
。如果waitsForConnectivity
设置为true,并且适当的连接不可用,那么委托方法URLSession:taskIsWaitingForConnectivity
将被调用,在这里可以决定是否更新UI或显示离线消息,或者使用缓存数据更新UI,然后会话将等待连接。一旦连接可用,会话将继续访问服务器。waitsForConnectivity
只在第一次建立连接时工作,一旦连接可用然后在获取数据期间断开,那么完成处理程序或委托将通知失败条件。
let sessionConfig = URLSessionConfiguration.default
sessionConfig.waitsForConnectivity = true
尝试使用延迟网络——可以使用延迟网络概念来执行低优先级的网络事务。这样系统可以找到一个合适的时间来执行任务。也许在设备连接到充电器时,或者在使用Wi-Fi时。优化的方法是批量所有低优先级事务,并创建一个后台会话,并尝试一次性执行它。对于延迟网络负载,需要设置NSURLSessionConfiguration.discretionary = true
。
let config = URLSessionConfiguration.background(withIdentifier: "com.example.testApp.background")
config.isDiscretionary = true
高精度位置会导致更多的电量消耗。
使用CLLocationManager.requestLocation()
获取当前位置,而不是CLLocationManager.startUpdatingLocation
和CLLocationManager.stopUpdatingLocation
。
一旦用户到达目的地或完成任务,立即停止使用位置服务。
使用CLCircularRegion
在用户进入特定区域时获取提醒,而不是使用startUpdatingLocation
和stopUpdatingLocation
持续跟踪位置。