iOS性能优化之耗电量

4,380 阅读10分钟

iOS性能优化之耗电量

前言

最近在测试App的时候,发现手机特别容易发烫,我们都知道 ,如果手机容易发烫,那么耗电量肯定会相当大,手机电量使用的时间也会相对少;对此,我在工作之余抽了点时间,对手机的耗电量进行了一些研究,希望可以给大家起到抛砖引玉的作用,对自己的App进行相应的优化,(在我们的能力范围内 ,系统问题我们暂时没还得靠苹果大佬那边进行优化)让我们的手机电量的使用时间更长些。

一、耗电量相关知识的了解

为了更好的去优化手机的耗电量 ,我查看了苹果官方文档相关的资料,分别主要需要了解的有以下几点: Apple文档:

  1. 入口: UIKit — App Structure (Core APP) — Device Environment (UIDevice) — Getting the Device Battery State 到这里 ,我们就可以看到 耗电量有几个个重要属性batteryStateThe battery state for the device.

  2. @property(nonatomic, readonly) float batteryLevel; 电池电量范围从0.0(完全放电)到1.0(100%充电)。在访问此属性之前,请确保已启用电池监视。

如果未启用电池监控,则电池状态为且此属性的值为-1.0。UIDeviceBatteryStateUnknown

  1. 耗电量的状态

  2. UIDeviceBatteryState设备的电池电量状态。

  3. UIDeviceBatteryState设备的电池电量状态。

  4. UIDeviceBatteryState设备的电池电量状态。

UIDeviceBatteryState设备的电池电量状态。分别有如下几种:UIDeviceBatteryStateUnknown无法确定设备的电池状态。

UIDeviceBatteryStateUnplugged设备未插入电源; 电池正在放电。

UIDeviceBatteryStateCharging设备已接通电源,电池电量低于100%。

UIDeviceBatteryStateFull设备已接通电源,电池100%充电。

二、耗电量的基本概念

图一

通过这张图,我们大概可以了解 ,我们的手机的耗电量,主要的状态:

  1. Idle 状态说明 app 处于休眠状态,几乎不使用电量。
  2. Active 状态说明 app 处于前台工作状态,用电量比较高,我们可以看到图中的第二个 Active 的耗电远高于第一个,这主要因为 app 实际所做的工作类型不同而导致的。
  3. Overhead 指的是调起硬件来支持 app 功能所消耗的电量(原文是 bring hardware up)。note:如果你的 app 就算只做了一点点事,Overhead 所带来的电量消耗都是一点不会减少的! 图中,横线以下所包区域是固定开销(Fixed Cost),横线以上区域是动态开销(Dynamic cost)。

三、影响电量的因素

  1. CPU 处理(Processing)
  2. 网络(Networking)
  3. 图像(Graphics)
  4. 定位(Location)

(1)首页我们针对CPU和能耗的关系:CPU使用量越大,功率越大,电能消耗越多,电池消耗也就越快。功率大小由于设备、处理器、其他硬件资源等会有所不同,表1基于闲置状态(idle state),给出了不同CPU使用量的一个大致的对比。

我们在使用我们app执行任务有动态能耗(dynamic cost)和固定能耗(fixed cost)。

  1. 动态能耗也就是app实际工作消耗的能量。
  2. 固定能耗是在任务执行前后把系统和各种资源调用起来和关闭所消耗的能量。出现大量零散的工作时,在零散的任务之间因为资源可能永远没法真正变为闲置,所以有动态能耗的同时固定能耗也很高。这种情况导致大量电能在相对较小的实际工作中流失了。下图是动态能耗和固定能耗图 如图一

四、优化方案

1. 减少固定能耗交换动态能耗

你的app可以通过分批执行、降低执行频率来避免产生零散的任务。例如,不采用同一个线程串行执行一系列任务,而是把任务同时放到多个线程,如图所示。每次使用CPU,内存、缓存、总线等都得通电。通过分批执行,使用时间也更短。因为给定时间内做了更多的工作,需要更多能量,这种策略会导致更大的前期动态功耗。作为交换,固定能耗减小了,随着时间推移,这会极大地节省电能。你的app提高了功率,但它更高效,用了更少的时间。这使得CPU回到闲置状态,其他元件也更快地断电

图2

2. 少使用定时器或者用高级的定时器

GCD里的dispatch queues、dispatch semaphores等同步工具比定时器效率高很多,尽量不要用定时器做同步工具。所有需要指定一个最后期限的函数或方法都属于定时器,比如: a. 高级定时器包括dispatch timer sources、CFRunLoopTimerCreate和其他CFRunLoopTimer函数、NSTimer、performSelector:withObject:afterDelay:方法。 b. 底层定时器包括sleep, usleep, nanosleep, pthread_cond_timedwait, select, poll, kevent, dispatch_after, dispatch_semaphore_wait。 如果一定要用定时器,尽量高效地使用,可以参照下列指导方针:

  1. 设置一个合适的超时时间。
  2. 不再需要时及时关闭重复性定时器。
  3. 设置触发公差。

3. 使用低电量模式

iOS9之后,iPhone增加了低电量模式,用户如果希望延长iPhone电池的寿命,可以在设置 > 电池中开启该功能。开启该功能之后iOS会采取一些措施,比如:

  1. 降低CPU和GPU性能
  2. 暂停随意的和后台的活动,包括网络
  3. 降低屏幕亮度
  4. 缩短自动锁屏时间
  5. 关闭邮件刷新
  6. 关闭视角缩放
  7. 关闭动态壁纸

4.缩减网络请求

  1. 减少、压缩网络数据。可以降低上传或下载的多媒体内容质量和尺寸等。
  2. 使用缓存,不要重复下载相同的数据。
  3. 使用断点续传,否则网络不稳定时可能多次传输相同的内容。
  4. 网络不可用时不要尝试执行网络请求,尽量只在Wi-Fi情况下联网。
  5. 让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间。

5. 延迟联网和 图像、动画、视频优化

  1. 延迟网络:分批传输。比如,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。如果提供广告,一次性多下载一些,然后再慢慢展示。如果要从服务器下载电子邮件,一次下载多条,不要一条一条地下载。网络操作能推迟就推迟。如果通过HTTP上传、下载数据,建议使用NSURLSession中的后台会话,这样系统可以针对整个设备所有的网络操作优化功耗。将可以推迟的操作尽量推迟到设备充电状态并且连接Wi-Fi时进行,比如同步和备份工作。
  2. 图像 减少不透明视图的使用,比如视图上显示一个半透明模糊效果。如果要用不透明效果,避免用在内容频繁变化的地方。另外,由于内容变化后背景视图和半透明视图必须同时改变,这也会放大功耗。
  3. 避免绘制不可见的内容,比如app的内容被其他视图遮挡、被剪切(clipped)或者出画了。
  4. 动画尽可能用较低的帧率。比如,高帧率在玩游戏时有意义,但是菜单画面可能较低的帧率就够了。只有对用户体验有影响时才使用高帧率。

6. 优化定位

如果你的app只是需要快速确定一下用户的位置,最好用CLLocationManager的requestLocation (iOS9引入)方法。定位完成之后会自动让硬件断电。 除了导航,大多数app不需要一直实时更新位置。需要位置服务时开启一下定位,尽量多隔一些时间再进行下次位置更新,更新完了之后马上关掉定位。除非用户在移动的交通工具里,否则不频繁地更新位置一般没多大问题。降低定位精度。iOS设备默认采用最高精度定位,如果你的app不是确实需要米级的位置信息,不要用最高精度(kCLLocationAccuracyBest)或10米左右的精度(kCLLocationAccuracyNearestTenMeters)。一般来说Core Location提供的精度比你设置的要好,比如你设置为3公里左右的精度,可能会收到100米左右的精度信息。 如果定位精度一直达不到设置的精度时,停止更新位置,稍后再试。 需要后台更新位置时,尽量把pausesLocationUpdatesAutomatically设为YES,如果用户不太可能移动的时候系统会自动暂停位置更新。

7. 代码层面优化

  1. 合理使用NSDateFormatter 和 NSCalendar这种高开销对象 性能测试表明,NSDateFormatter的性能瓶颈是由于NSDate格式到NSString格式的转化,所以把NSDateFormatter创建单例意义不大.推荐的做法是,把最常用到的日期格式做缓存.
static NSDateFormatter *cachedDateFormatter = nil;

+ (NSDateFormatter *)cachedDateFormatter {

if (!dateFormatter) {

dateFormatter = [[NSDateFormatter alloc] init];


[dateFormatter setDateFormat: @“YYYY-MM-dd HH:mm:ss”];

}

return dateFormatter;

}
  1. 不要频繁的刷新页面,能刷新1行cell最好只刷新一行,尽量不要使用reloadData.
  2. 选择正确的集合 NSArray,使用index来查找很快(插入和删除很慢) 字典,使用键来查找很快 NSSets,是无序的,用键查找很快,插入/删除很快
  3. 少用运算获得圆角,不论view.maskToBounds还是layer.clipToBounds都会有很大的资源开销,必须要用圆角的话,不如把图片本身就做成圆角
  4. 懒加载,不要一次性创建所有的subview,而是需要时才创建.
  5. 重用 可以模仿UITableView和UICollectionView,不要一次性创建所有的subview,而是需要时才创建.完成了使命,把他放入到一个可重用集合中
  6. 图片处理 图片与imageView相同大小,避免多余运算 可以使用整副的图片,增加应用体积,但是节省CPU 可调大小的图片,可以省去一些不必要的空间 CALayer,CoreGraphics,甚至OpenGL来绘制,消耗CPU
  7. cache,cache,cache(缓存所有需要的) 服务器相应结果的缓存(图片) 复杂计算结果的缓存(UITableView的行高)
  8. 尽量少用透明或半透明,会产生额外的运算.
  9. 使用ARC减少内存失误,dealloc需要重写并对属性置为nil
  10. 避免庞大的xib,storyBoard,尽量使用纯代码开发

8.CPU层面优化

1.Timer的时间间隔不宜太短,满足需求即可 2.线程适量,不宜过多,不要阻塞主线程 3.优化算法,减少循环次数 4.定位和蓝牙按需取用,定位之后要关闭或降低定位频率

数据采集步骤

由于直接数据采集会出现较大误差,建议使用以下方法进行数据采集: 1.在设备上进入设置 > 2. 开发者 > 3. Logging > 4. Enery打开 > 5. Networking打开> 6.点击 Start Recording > 7. 然后点开我们想要测试的App,进行测试,定好时间,> 8.时间到了后点击 停止 Stop Recording > 9.在Instruments中选择好设备,进入Energy Log > 10.选择File > Import Logged Data from Device

参考文章: 内存泄漏对耗电量的影响 教你开发省电 耗电量基础概念