前言-理论篇
耗电量分析是衡量应用性能表现的一个重要指标,要做好一款app,不仅仅是实现功能,我们需要考虑很多性能指标,让用户用的更爽,在开发过程中,要充分考虑到各项性能指标,比如定位精度,更高的精度,往往意味着更高的能耗,因此要平衡好精度和功耗,避免我们的app过多的过多的电量消耗,而界面卡顿可能意味着大量的多余计算,不仅影响流畅度,影响电量消耗,网络的不合理使用,也会增加耗电量,这些都是影响耗电量和用户体验的重要因素,因此必须要重视app耗电量。
1.耗电量概念
-
Idle
表示app处于休眠状态,几乎不使用电量。 -
Active
状态说明app处于工作状态,用电量比较高,图中的第二个Active
的耗电远高于第一个,这是因为app所做的工作类型不同而导致的。 -
Overhead
指的是调起硬件来支持app功能所消耗的电量,这部分是支持硬件工作必要的电量消耗。 - 图中横线以下所包区域是固定开销(
Fixed Cost
),横线以上区域是动态开销(Dynamic cost
)。
2.节省电量的四个基本原则:
识别:想清楚你需要app在特定时刻需要完成哪些工作,如果是不必要的工作,考虑延后执行或者省去。
优化:优化app的功能实现,尽可能以更有效率的方式去完成功能。
合并:不需要立刻获取,可以延后合并执行,比如合并网络
减少:在满足需求的基础上,尽量减少做重复工作的频率。
一、耗电大户
1.网络
app进行网络通信时,蜂窝数据和Wi-Fi等元器件开始工作就会消耗大量电能,应减少数据传输,合并网络请求,适当的网络延时等。
2.定位
app为了记录用户的活动或者提供基于位置的服务会进行定位。定位精度越高,定位时间越长,消耗电量也就越多。
3.CPU
app做的每件事几乎都需要用CPU,所以CPU是电能消耗大户,高CPU使用时会迅速消耗掉用户的电池电量,所以我们要更合理的使用CPU,降低能耗。
4.GPU
app内容每次更新到屏幕上都需要消耗电能处理像素信息。动画和视频格外耗电。不经意的或者不必要的内容更新同样会消耗电能,所以UI不可见时,应该避免更新其内容。
5.传感器和蓝牙
长时间用不上加速度计、陀螺仪、磁力计等设备的动作数据时,应该停止更新数据,不然也会浪费电能。应按需获取,用完即停。蓝牙活动频度太高会消耗电能,应该尽量分批、减少数据轮询等操作。
二、测试工具
1.Energy Impact
1.1Average energy Imapct
Average energy Imapct
代表的是app的耗电量评级,low(绿色区域)
、high (黄色区域)
、very high(红色区域
)。通过这张图,我们可以大致了解 app 电量的使用情况。理想的状态是 app 处于 low、high 状态。如果处于very high状态,我们需要分析哪些功能导致耗电量大涨。
1.2Average Componet Utilization
Average Componet Utilization
展示的是Overhead
、CPU
、NetWork
、Location
、GPU
各部分耗电量的占比,通过这个占比,我们可以分析各部分耗电量是否较高。
1.3Energy Impact
Energy Impact
会实时展示电量消耗,每个柱子代表每秒的电量消耗,通过每个柱子由不同的颜色组成,展示各部分电量消耗的比例,依次是Overhead(红色)、CPU(蓝色)、网络(深黄)、定位(淡黄)、GPU(绿色)、后台(深灰)、前台(淡灰)、Suspend(白色)
。
1.4总结
在使用Energy Impact
的过程中,首先可以观察Average energy Imapct
。了解app的整体耗电量情况,处在什么位置,对整体有个大概的了解,然后通过Average Componet Utilization
来观察app整体耗电量中,具体哪一部分占比较高,最后使用Energy Impact
进行实时分析,分析页面各部分消耗是否是必要的,比如某个页面只在进入的时候需要获取经纬度,后面不需要实时获取经纬度,那么在进入页面获取经纬度以后,就要将定位关闭,以减少电量消耗。
优点:
实时性高、数据分析可以通过图形直观展示、可以获取`Overhead(红色)、CPU(蓝色)、网络(深黄)、定位(淡黄)、GPU(绿色)、后台(深灰)、前台(淡灰)、Suspend(白色)等数据和各部分数据占比。
缺点:
颗粒度较大,想做详细的分析,需要借助,其它工具。比如针对CPU的消耗情况,可以通过Time Profiler
来进行分析,分析具体哪部分代码消耗较多,使用Network Profiler
来分析网络的消耗情况、使用Location Profiler
来分析定位的情况,详细获取各部分数据,找到电量的消耗点。
2.Energy Log
2.1Energy Log使用:
设置路径:开发者 -> Logging -> Energy -> Start Recording -> Stop Recording。
首先找到开发者,点击进入,找到Logging选项,如下图:
<img src="https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/Art/ios_settings_2x.png" width = "300" height = "500" align = center /><img src="https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/Art/ios_developer_2x.png" width = "300" height = "500" align = center /></br>
然后点金进入Logging选项、进入新页面,打开Energy开关,同时点击Start Recording,此时开始记录电量消耗情况,,这时你可以选择想要测试的app,在测试完毕后,点击Stop Recording,关闭电量记录功能,如下图:
<img src="https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/Art/ios_logging_startrecording_2x.png" width = "300" height = "500" align = center /><img src="https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/Art/ios_logging_stoprecording_2x.png" width = "300" height = "500" align = center /></br>
最后,将手机连接到电脑,导出设备中的电量记录数据,在Energy Log中,可以看到详细的电量消耗数据,如下图:
<img src="https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/Art/instruments_file_menu_importloggeddatafromdevice_menuitem_2x.png" width = "400" height = "500" div align = center /></br>
2.2Energy Log组成:
Energy Usage Log
代表能耗消耗级别,有 0 到 20 的级别,表示应用在任何给定时间使用了多少电量,值越大表示越耗电。可以用来分析app的总体能耗。CPU Activity Log
代表cpu能源消耗,包括 :Foreground app Activity
、Graphics Activity
、Media Activity
、Other Activity
。Network Activity Log
代表网络能源消耗,包括 :Cellular in
、Cellular out
、Wi-Fi In
、Wi-Fi Out
。Display Brightness Log
代表屏幕消耗。Sleep/Wake Log
代表仪器捕获有关设备当前状态的信息。Bluetooth On/Off Log
代表蓝牙开关状态。Wi-Fi On/Off Log
代表Wi-Fi开关状态。Gps On/Off Log
代表Gps开关状态。
2.3总结
在使用Energy Log过程中,可以通过横向比较各个维度的数据,来分析各部分的能源消耗,分析能耗的使用情况,更合理的使用手机电量。
优点:
Energy Log可以测试自己app和别人app的电量消耗,有各个上面👆维度的详细数据,比如:某个时刻的cpu使用情况、定位使用情况,可以关联CPU、定位、总能耗消耗等数据,通过这些数据,分析使用是否合理。
缺点:
Energy Log只能看整体能耗的水平,同时由于log不是实时的,要具体确定某个页面或者某个路径的能耗情况,需要自己测试的时候记录一下,只能大概对照,而且不能输出精准数据。
3.Sysdiagnose
Sysdiagnose是用来诊断系统问题的工具,它包括了过去几天里,系统整体的详细功耗情况、各个App在各个硬件上的耗电情况,温度、电流等等一系列详细的数据。这些数据最终会存储在数据库中,数据库中包含多张数据表,比如在iOS12的系统中,有多达406张表。
3.1配置和数据获取
苹果官方在Battery Life Logging Instructions中详细说明了导出日志的步骤。通过安装电量分析的profile来记录手机耗电量信息,使用iTunes进行同步,最终从文件夹(~/Library/Logs/CrashReporter/MobileDevice/你的手机名/)中获取后缀是.PLSQL或者.PLSQL.gz的几个文件,这些文件记录了手机中各个app的耗电情况。
3.2分析数据
数据库中有多张表格,这些表格的具体用途,苹果没有详细说明,下面是腾讯和一些开发者总结的部分表格信息。
表名 | 内容 |
---|---|
PLBatteryAgent_EventBackward_Battery | 整机的电量信息,包含电流、电压、温度等信息。(每20秒记录一条数据) |
PLBatteryAgent_EventBackward_Battery_UI | 剩余电量百分比。(每20秒记录一条数据) |
PLIOReportAgent_EventBackward_EnergyModel | 整机不同硬件上的详细功耗数据。分别记录了 CPU、GPU、DRAM 等硬件的耗电量。 |
PLAccountingOperator_Aggregate_RootNodeEnergy | 各个 App 的详细耗电数据。记录各个 App 在各个硬件上的耗电量。(每小时更新一次数据) |
PLAccountingOperator_EventNone_Nodes | 各个硬件对应的 Node ID,以及各个 App 的对应的 Node ID。 |
PLAccountingOperator_EventNone_AllApps | 手机中安装的所有 App 的信息 |
PLApplicationAgent_EventForward_Application | App 运行状态记录。记录各个 App 在某个时间段以什么状态运行。 |
PLAppTimeService_Aggregate_AppRunTime | App 的运行时长统计。(每小时更新一次数据。 |
PLBatteryAgent_EventForward_LightningConnectorStatus Lighting | 接口连接状态。 |
PLBatteryAgent_EventNone_BatteryConfig | 电池的配置信息。包括电池容量、循环计数、电池寿命、电池温度等信息。 |
PLBatteryAgent_EventNone_BatteryShutdown | 电池导致的意外关机记录。 |
PLCameraAgent_EventForward_Camera | 相机使用记录。记录了相机类型和使用相机的 App |
PLConfigAgent_EventNone_Config | 本机的一些配置信息和一些系统设置。 |
PLDisplayAgent_Aggregate_UserTouch | 屏幕点击计数。每 15 分钟记录一条数据。 |
PLDisplayAgent_EventForward_Display | 屏幕亮度信息。包括流明、尼特、亮度滑竿值等信息。 |
PLProcessNetworkAgent_EventPoint_Connection | 网络连接记录。记录了发起网络连接的 App、地址、端口等信息。 |
PLXPCAgent_EventPoint_CacheDelete | 清除缓存的记录。包括申请的空间大小、清除缓存的耗时、清除的缓存大小、服务名称、紧急程度等信息。 |
我们可以根据上面的表格,来分析耗电情况,比如:
- PLBatteryAgent_EventBackward_Battery_UI 可以分析剩余电量曲线
- PLBatteryAgent_EventBackward_Battery 可以分析整体耗电量和温度变化
- PLAccountingOperator_Aggregate_RootNodeEnergy 和 PLAccountingOperator_EventNone_Nodes 两张表,可以得到某个 Bundle ID 对应的 App 在各个硬件上的耗电情况。
下面是一些数据获取的SQL语句:
-
查看APP运行的时间timestamp
select ID,timestamp,datetime(timestamp, 'unixepoch', 'localtime')as time,BundleID from PLAppTimeService_Aggregate_AppRunTime where BundleID='XXX' order by timestamp
-
查看APP的NodeID
select * from PLAccountingOperator_EventNone_Nodes where Name ='XXX'
-
查看APP的电量测试
select ID,timestamp,datetime(timestamp, 'unixepoch','localtime')as time,Energy,NodeID,RootNodeID from PLAccountingOperator_Aggregate_RootNodeEnergy where NodeID=XXX order by time
-
查看APP某个时间点的电量求和
select datetime(timestamp, 'unixepoch', 'localtime')as time,sum(Energy),NodeID from PLAccountingOperator_Aggregate_RootNodeEnergy where NodeID=XXX and timestamp='XXX'
RootNodeID硬件对照表
RootNodeID | 硬件 |
---|---|
2 | CPU |
4 | VENC |
5 | VDEC |
6 | ISP |
7 | RestOfSOC |
8 | GPU |
9 | DRAM |
10 | Display |
11 | WiFi-Data |
14 | AudioHeadset |
15 | AudioSpeaker |
17 | FrontCamera |
18 | BackCamera |
19 | Torch |
37 | BB |
38 | BB-Voice |
47 | BB-Pssi |
48 | GPS |
50 | Bluetooth |
53 | WiFi-IdLe |
54 | APSOCBaseIOReport |
55 | AudioCodec |
3.4总结
Sysdiagnose是一个可以详细获取各个维度数据的工具,具体到某个app、某段时间、某个硬件的具体能源消耗,可以使用这个工具,进行app的不同场景的能耗数据对比、竞品app的能耗数据分析和对比,十分精确。
优点:
可以精确获取各个维度的能耗消耗数据,包括:isp
、apsocbase
、 display
、wifi data
、GPU
、CPU
、restofsoc
、GPS
、DRAM
、等详细数据。通过这些数据,可以分析某个app的某段时间,各个维度的能耗数据,这个数据十分精确。
缺点:
数据不能实时获取,部分数据需要等待一个小时才能获取,测试周期长,数据量较大,没有官方文档指导,需要自己分析各个表中的数据含义。
三、案例分析
1.对比分析
使用Sysdiagnose,通过对比A页面和B页面(A和B是两个不同app相同业务的页面),来分析各自耗电量情况,以及可能的优化点。下面数据单位是1/1000 mAh
1.1A页面和B页面
测试条件:iphone7,iOS12.2,记录5分钟电量消耗,滑动100次,取三次平均值.
APP | Display | GPS | WiFi-Data | CPU | RestOfSOC | DRAM | GPU | SUM |
---|---|---|---|---|---|---|---|---|
A页面 | 31888 | 0 | 1753 | 18523 | 5437 | 5059 | 0 | 62660 |
B页面 | 36180 | 9652 | 1500 | 16338 | 4792 | 4460 | 289 | 73211 |
对比A页面和B页面能耗数据,整体能耗数据,B页面高于A页面电量消耗,CPU、RestOfSOC、DRAM数据,A页面都高于B页面,B页面GPS电量消耗量较高,这是因为B页面一直开启了GPS,Display数据B页面高于A页面,可能和动画使用有关。通过这些指标分析,可对比竞品之间的能耗水平,也可以初步为电量优化提供方向。
1.2页面中硬件电量消耗
通过图片可以看出页面中,Display和CPU是主要消耗,占用页面整体电量消耗的80%左右,这一部分也是优化的重点,对于Diaplay的电量消耗受很多因素影响,比较复杂,比如:屏幕亮度和动画等影响。
总结:通过对比分析,可以获取app的具体电量消耗水平,可以根据具体数据,推断出app能运行的时间,同时可以对比竞品,判断app的电量消耗水平,并从具体指标分析,初步推断优化方向。
2.需求分析
上面是使用Energy Log来测试C页面的电量消耗情况,红色部分是Overhead,表示调起硬件的正常电量消耗,其占据页面57.1%,网络占10.2%,cpu占1.2%,Location占31.1%,但在这个页面中,GPS是一直开启的,也就是图中的黄色部分,实际情况是是不需要一直开启GPS定位,这一部分存在优化空间,按需要开启和关闭GPS开关。
总结
在进行电量消耗测试时,需要对页面需求进行具体分析,用来判别页面中网络、CPU、GPU、GPS各部分的电量消耗是否是合理和必要的,上面例子的情况,GPS没必要一直开启,可以在不需要使用时关闭,存在优化空间。关于更多电量优化建议,可以参考苹果官方Apple Energy Guide或者这篇翻译App功耗优化建议,会从网络、CPU、GPU、GPS等方面,更详细的角度来分析耗电量优化。
四、优化建议
1.网络
只要app执行网络操作,就会产生大量的见解能耗,网络硬件为了响应下一次任务,往往会持续活跃一段时间,下面是多次网络的能耗图。
1.1 缩减网络请求
减少、压缩网络数据。可以降低上传或下载的多媒体内容质量和尺寸等。
使用缓存,不要重复下载相同的数据。
使用断点续传,否则网络不稳定时可能多次传输相同的内容。
网络不可用时不要尝试执行网络请求,尽量只在Wi-Fi情况下联网。
让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间。
网络请求失败后用SCNetworkReachability的通知监测网络状态,网络可用后再重试。
1.2 延迟联网
分批传输。比如,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。如果提供广告,一次性多下载一些,然后再慢慢展示。如果要从服务器下载电子邮件,一次下载多条,不要一条一条地下载。
网络操作能推迟就推迟。如果通过HTTP上传、下载数据,建议使用NSURLSession中的后台会话,这样系统可以针对整个设备所有的网络操作优化功耗。将可以推迟的操作尽量推迟到设备充电状态并且连接Wi-Fi时进行,比如同步和备份工作。
2.定位
如果你的app只是需要快速确定一下用户的位置,最好用CLLocationManager的requestLocation (iOS9引入)方法。定位完成之后会自动让硬件断电。
除非是在导航的时候,app大部分时间不需要实时更新,降低位置的更新频率。
尽量降低定位精度。iOS设备默认采用最高精度定位,如果你的app不是确实需要米级的位置信息,不要用最高精度(kCLLocationAccuracyBest)或10米左右的精度(kCLLocationAccuracyNearestTenMeters)。一般来说Core Location提供的精度比你设置的要好,比如你设置为3公里左右的精度,可能会收到100米左右的精度信息。
如果定位精度一直达不到设置的精度时,停止更新位置,稍后再试。
需要后台更新位置时,尽量把pausesLocationUpdatesAutomatically设为YES,如果用户不太可能移动的时候系统会自动暂停位置更新。
后台定位时延时更新位置。如果要做一个健身类的软件追踪用户徒步的距离,可以等用户移动一段距离或者过一段时间之后再更新位置,这样可以让系统优化能耗。
3.CPU
3.1尽量减少计时器使用
使用计时器时,设置一个合适的超时时,不再需要时及时关闭重复性定时器。用事件通知代替定时器。有些app用定时器监控文件内容、网络或者其他状态的变化,这会导致CPU无法进入闲置状态而增加功耗。
3.2减少后台工作
实现UIApplicationDelegate中的方法,应用进入后台前做好暂停任务,保存数据等工作。如果确实需要完成用户执行的一些任务,应该调用UIApplicationDelegate中的beginBackgroundTaskWithExpirationHandler: 方法,这样后台任务可以继续执行几分钟。任务执行完毕后一定要调用endBackgroundTask:方法,不要等着系统强行挂起进程。
3.3用QoS分级有序工作
多个app和众多操作需要共享CPU、缓存、网络等资源,为了保持高效,系统需要根据不同任务的优先级智能地管理这些工作。比如更新UI这种重要的事需要多分配资源,而一些后台任务可以延迟一些执行。服务质量(quality of service, 以下简称QoS, iOS8引入)级别可以通过NSOperation, NSOperationQueue, NSThread objects, dispatch queues, 和pthreads (POSIX threads)指定工作的优先级。
3.4优化I/O访问
app每次执行I/O任务,比如写文件,会导致系统退出闲置模式。而且写入缓存格外耗电。通过下列方法可以提高能效、改善app性能。
减小写入数据。数据有变化再写文件,尽量把多个更改攒到一起一次性写入。如果只有几个字节的数据改变,不要把整个文件重新写入一次。如果你的app经常要修改大文件里很少的内容,可以考虑用数据库存储这些数据。
避免访问存储频度太高。如果app要存储状态信息,要等到状态信息有变化时再写入。尽量分批修改,不要频繁地写入这些小变动。
尽量顺序读写数据。在文件中跳转位置会消耗一些时间。
尽量从文件读写大数据块,一次读取太多数据可能会引发一些问题。比如,读取一个32M文件的全部内容可能会在读取完成前触发内容分页。
读写大量重要数据时,考虑用dispatch_io,其提供了基于GCD的异步操作文件I/O的API。用dispatch_io系统会优化磁盘访问。
如果你的数据由随机访问的结构化内容组成,建议将其存储在数据库中,可以使用SQLite或Core Data访问。特别是需要操作的内容可能增长到超过几兆的时候。
了解系统如何缓存文件、如何优化缓存的使用。如果你不打算多次引用某些数据,不要自己缓存数据。
4.GPU
减少app使用的视图数量。
少用运算获得圆角,不论view.maskToBounds还是layer.clipToBounds都会有很大的资源开销.
尽量少用透明或半透明,会产生额外的运算.
执行动画时不要修改帧率。比如,你的app帧率是60fps,整个动画就保持这个帧率不要变。
视频播放时,app尽量不要在全屏视频上添加额外的图层(即使是隐藏的图层)
5.优化通知
尽量用本地通知(local notification),如果你的app不依赖外部数据,而是需要基于时间的通知,应该用本地通知,可以让设备的网络硬件休息一下。
远程推送有两个级别,一个是立即推送,另一个是针对功耗优化过的延时推送。如果不是真的需要即时推送,尽量使用延时推送。
五、总结
耗电量分析是应用开发中很难的课题,也是衡量性能的重要指标,做好电量优化,可以大幅度提升用户体验,在实际开发过程中,电量消耗往往是一个综合的结果,小到日常的代码习惯,积少成多,大到网络的乱用、不合理的网络超时、不合理的定位使用、不合理的CPU使用,都会带来能耗的过多消耗,而这些问题仅仅会影响电量,还会影响用户体验,而对于这一个很多因素综合起来的结果,我们需要提高日常开发的代码质量,了解不同实现方式的差异点,优化页面卡顿,优化网络使用,优化GPS使用等等,只有综合这些因素,最终获得较好的用户体验。
在做耗电量分析的时候,首先要清楚耗电量较大的点,比如:网络、定位、CPU、GPU,从这些点来入手分析,再详细分析每个点,细分每个点中的一些细节的影响,比如:网络中可能会存在导致耗电量过高的因素,我们可以使用哪些优化方法,同时根据我们的需求特点,怎么来优化,最终找到可优化点的,找到解决方案,来优化耗电量。
在优化点寻找的过程中,我们首先可以使用Energy Impact来实时分析,各个维度指标是否有问题,占比是否合理,然后在通过Time Profiler
、Network Profiler
、Location Profiler
等工具对各项指标进行详细分析,寻找优化点。
其次我们可以使用Energy Log来横向对比各个维度的数据,来分析各个维度对总功耗的影响,比如:wifi数据传输和4G数据传输时,耗电量的对比,打开蓝牙和未打开蓝牙,能耗数据的对比,查看各个维度对能耗消耗影响的权重。然后,我们可以使用Sysdiagnose可以进行不同场景、竞品app的对比分析,查找app的可优化点,也可以进行各种能耗数据的对比分析,以及优化前后的能耗数据对比,获取最终的优化效果。Sysdiagnose中的数量比较精确,同时数据比较全面,可以挖掘更多的信息,来辅助我们进行电量优化。</br>
附:iPhone各代电池容量
📱手机型号 | 🔋电池容量 |
---|---|
iPhone 11 Pro Max | 3500mAh |
iPhone 11 Pro | 3190mAh |
iPhone 11 | 3110mAh |
iPhone XS Max | 3174mAh |
iPhone XS | 2658mAh |
iPhone XR | 2942mAh |
iPhone 8 Plus | 2675mAh |
iPhone 8 | 1821mAh |
iPhone 7 Plus | 2900mAh |
iPhone 7 | 1960mAh |
iPhone 6s Plus | 2750mAh |
iPhone 6s | 1715 mAh |
iPhone 6 Plus | 2915mAh |
iPhone 6 | 1810 mAh |
参考内容: