iOS 电量消耗改善:一招套路及相关姿势

7,357 阅读7分钟

工作流

解决电量问题的工作流:

先使用 Xcode Energy Gauge 分析出哪一块耗电(网络和 motion , 还是定位 ... ), 用 Time Profiler 定位问题与解决 ( Instruments 模版 ), 得到用户好的反馈。

三个原则:
  • Do it never/do it less (能不做,就不做。少做的,好)

比如: 网络请求,先压缩数据

  • Do it at a better time (合适的时机处理 )

网络请求,使用缓存机制,设置内容验证( 需要的数据是否更新了 ),或者缓存的失效时间

  • Do it efficiently ( 有效处理 )

合并网络请求。一次请求大量数据,比多次请求少量数据省电

(类比 CPU,线程开多了不好。开线程,就会有消耗)


套路很简单,将手机放在桌子上,Xcode 里面启动 app, 并检测,啥也不干。如果电量消耗较高,很不合适了

WWDC 推荐使用 Xcode Debug 栏的 Energy Debug Gauge。

( 调性能,都是用真机。机器老一点,效果更好 )

Energy Debug Gauge 形象、直观

Energy Debug Gauge

可看出,当前手机的耗电情况,耗电低、高、很高。苹果的三个阶段,有些不太细致。(左上的 Utilization, Current Impact)

app 的平均能耗,一目了然 ( 右上的 Average ) 。

每一个时刻,耗电的是什么。 CPU 、网络、文件 I/O 、定位,哪些消耗了。( 中间的 Energy Impact )


Xcode Energy Gauge 可以快速定位问题,想要进一步的细致分析,下面有各种选项,跳转到对应的 Instrumens 模版。

比如:

分析 CPU 使用的 time profile, (能够知道代码的执行情况了,根据函数的调用消耗。找出权重大的,干掉不必要的。)

分析网络活动的 network profile, 分析定位活动的 location profile

跳


本文示例代码: 解决的两个问题,给 CoreMotion 更新设置过滤,干掉频繁的日志上传

before

这里电量消耗很高,很稳定

主要是 CPU 和网络请求在耗电。


使用 Instruments 的 Time Profiler 分析,

CPU 分析

可以先放大上面的 time line,再选择一个时间段,在调用树 call tree 中,进一步分析。

Time Profiler 的选项默认是按线程划分的,再选一个隐藏系统调用函数。

(系统执行的函数,可以参考一下,到底发生了什么。系统的改不了。可以改自己的源代码 )

option

在调用树的表格中,按权重展开 ( weight ),要干掉的就是权重大的,耗时间的。

接着展开主线程 ( main thread 。看上图,其他线程的耗时,相比主线程的,可忽略 ), 按住 Option 键,点击 main thread 左边的小三角,可以一下子展开很多。

可清晰看出,耗时严重的是 450 毫秒左右的那一行 thunk for ... CMDeviceMotion? ...

里面调用了一个耗时的方法,CatPhotoTableViewCell.panImage , 上图, 454 毫秒中,占 419 毫秒。

hop

点击进入详情,就看到代码了。

在 CatFeedViewController 的 viewDidLoad 方法中,有一个倾斜的设置

motionManager.startDeviceMotionUpdates(to: .main, withHandler:{ deviceMotion, error in
            guard let deviceMotion = deviceMotion else { return }
            let xRotationRate = CGFloat(deviceMotion.rotationRate.x)
            let yRotationRate = CGFloat(deviceMotion.rotationRate.y)
            let zRotationRate = CGFloat(deviceMotion.rotationRate.z)
            //  y > z, 这个动作是翘起来
            //  y > x + z, 这个动作是斜着翘起来
              if abs(yRotationRate) > (abs(xRotationRate) + abs(zRotationRate)) {
                for cell in self.tableView.visibleCells as! [CatPhotoTableViewCell] {
                    cell.panImage(with: yRotationRate)
                }
              }
  })

现在的代码显示栏 ( 原来的 Call Tree 表格 ), 右上角有一个 Xcode 的小图标,点击返回 Xcode 调试代码。

手机没动,老是调用 cell.panImage(with: yRotationRate), 根本就没效果。

设置一下,调用 cell.panImage 的时候,要超过最小的手机幅度。幅度小,根本就没效果。 添加一个属性记录 lastY 来设置,过滤掉手机小的抖动。

private var lastY = 0.0
override func viewDidLoad() {
        super.viewDidLoad()
        ......
        motionManager.startDeviceMotionUpdates(to: .main, withHandler:{ deviceMotion, error in
            guard let deviceMotion = deviceMotion else { return }
          
            // 添加了这两行
           guard abs(self.lastY - deviceMotion.rotationRate.y) > 0.1 else { return }
            self.lastY = deviceMotion.rotationRate.y

            let xRotationRate = CGFloat(deviceMotion.rotationRate.x)
            let yRotationRate = CGFloat(deviceMotion.rotationRate.y)
            let zRotationRate = CGFloat(deviceMotion.rotationRate.z)
              if abs(yRotationRate) > (abs(xRotationRate) + abs(zRotationRate)) {
                for cell in self.tableView.visibleCells as! [CatPhotoTableViewCell] {
                    cell.panImage(with: yRotationRate)
                }
              }
        })
    }

还有一个使用 Timer 定时发送日志的问题,CPU 根本没有空闲的时间,开销很大。

具体见文末的 Demo Code.

最后这样

after

会慢慢降下去,至于电量低消耗。 需要大约两分钟时间,一屏幕放不下。


Instruments 的 Energy Log 有问题,连着 Xcode 实时调试的部分 gg 了

Instruments 的 Energy Log 模版用途不大

因为不能手机在线调试。Energy 是空的, 或者提示 No Data,

这是苹果的一个长期的 bug .(参见 Apple Forum )

Energy Log 模版的模块挺丰富的,可以看屏幕亮度、定位、蓝牙、GPU 和网络等等的功耗情况,其中网络又包括 WiFi 和蜂窝网络。


想着一边给手机充电,一边调试电量损失,不靠谱。

试了下,无线用 Instruments 的 Energy Log 模版调试,结果一样。

无线连接 Xcode 调试耗电,也没有数据。


无线用 Instruments 调试,首先要设置 Xcode 无线 Debug ,

无线 debug 功能,隐藏在 Xcode 的 Window > Devices and Simulators 中。

Devices_and_Simulators

实际上是,使用共享的无线网络,取代了数据线的连接,与 Xcode 建立连接。

会有一个网络的 Icon . 上面还有提示语 ( connected , 连上了 )

如下图:

connect

更多参见博客 How to use Wireless Debugging on Xcode 9

然后就可以设置 Instruments 无线设备调试了,

更多参见苹果文档 Energy Efficiency Guide for iOS Apps


instruments 的 Energy 模版,将电量消耗的程度划分为 20 个级别。 0 代表不耗电,自然 app 没做什么 20 代表耗电严重

WWDC 中说,要看到,就导入离线的 log。(几个月以前,还能用)

在手机的设置中,开发者选项中的 Logging, 选中 Energy, 点击开始录制:

开发者

之后,使用你的 app 一段时间,(可以重点测耗电功能) 开发者选项中的 Logging, 点击完成录制, 导入电量消耗 log 数据,到 Instruments 的 Energy Log 模版.

导入

推测老版本的不行( 11.4 ), 没数据。操作的时候,手机的设置 app ,还老是闪退。

手机升级到最新版(12.1 , 20181127),试了多次,也不行, 猜测目前是彻底挂了

(本文中,重启过手机,升级过手机。没试过重启电脑)


湿一点,好消化

耗电是不好的。

写入硬盘与网络请求,都是高耗电操作。

网络请求特别耗电,每一个网络请求,手机设备需要使用他的蜂窝网络天线,发送无线电波。

网络的质量与类型,对于耗电的影响也很大。 使用 Wi-Fi 比 3G , 4G 要省电得多。 使用 4G 比 3G 要省电,因为 4G 的信号更强。

计时器,能不用就不用。( NSTimer )

一般情况下,app 都用 Timer 做了很多无用功。

比如, 一个列表屏幕, 上方 banner 计时器,往下滑到看不见 banner ,就可以暂停计时器。上滑,看得见 banner 了,又可以恢复 resume。

同样的,进入子界面,可以选择暂停 Timer,或者释放,...

例子: 定时做重复的大量工作不好,可能每当系统休眠(系统要降低能级了),系统又被唤醒了,开始功耗。

定位

与网络请求类似,手机设备定位通过 GPS 天线发送信号,也挺耗电的。

如果 app 经常去获取手机设备的精确定位,定位精度越高,能耗越严重。 建议使用策略,手机的负担会小很多。

( 🌰,Deferred location updates, 位置更新延迟(直到移动了 x 米或者时间超过了 xx 秒 )、

significant location change, 定位变化比较大的时候,唤醒、

region monitoring, 监测用户进入或离开特定地理区域)

Motion 物理引擎,动态效果更新状态,挺耗电的。

使用罗盘、陀螺仪、加速计,都消耗不小。


相关代码: github.com/BoxDengJZ/I…

更多资料:

WWDC 2015 Debugging Energy Issues

视频教程,practical-instruments


本文 Demo 使用的是 500 px 的 API .


后来发现, 有人都写过了

好尴尬

想了一下,可以写他没交代的。苹果更新太快,人是物非