App优化之电池省着用
star87826
8年前
<p style="text-align:center"><img src="https://simg.open-open.com/show/48289e32c3924d0aba954c3845a197b2.png"></p> <h2><strong>引言</strong></h2> <p>电量使用优化, 基本上是我们最不怎么关注的一项优化. 可能很多公司连QA/Tester也不会关注测试App电量的使用. 一般来说开发和测试的测试设备也一直是连着USB处于充电状态的, 感官上也体会不到电量的损耗.</p> <p>然而, 对于用户来说, 实际上App的电量损耗也是用户体验的一个方面. 特别是当今人们对移动设备的依赖度越来越高, 电量也是用户特别关注的.</p> <p>今天我们就来聊聊Android App的电量优化.</p> <h2><strong>1, 分析电量的使用情况</strong></h2> <p>老套路, 上来还是先介绍下我们使用什么工具来做电量分析.</p> <h3><strong>1.1 Batterystats & bugreport</strong></h3> <p>Android 5.0及以上的设备, 允许我们通过adb命令dump出电量使用统计信息.</p> <p>1, 因为电量统计数据是持续的, 会非常大, 统计我们的待测试App之前先reset下, 连上设备, 命令行执行:</p> <pre> <code class="language-java">$ adb shell dumpsys batterystats --reset Battery stats reset.</code></pre> <p>2, 断开测试设备, 操作我们的待测试App.</p> <p>3, 重新连接设备, 使用adb命令导出相关统计数据:</p> <pre> <code class="language-java">// 此命令持续记录输出, 想要停止记录时按Ctrl+C退出. $ adb bugreport > bugreport.txt</code></pre> <p>导出的统计数据存储到bugreport.txt, 此时我们可以借助如下工具来图形化展示电池的消耗情况.</p> <p>注意, <a href="/misc/goto?guid=4959719645591586694" rel="nofollow,noindex">官方SDK文档</a> 导出文件方式为:</p> <p>adb shell dumpsys batterystats > batterystats.txt</p> <p>使用python historian.py batterystats.txt > batterystats.html查看数据</p> <p>是battery-historian老版本的使用方式. 目前Battery Historian已更新2.0版本, 推荐使用bugreport方式导出数据分析, 可以看到更多信息.</p> <h3><strong>1.2 Battery Historian</strong></h3> <p>Google提供了一个开源的电池历史数据分析工具 -- <a href="/misc/goto?guid=4958860265658930737" rel="nofollow,noindex">Battery Historian</a> .</p> <p><strong>1.2.1 安装</strong></p> <p>按照 <a href="/misc/goto?guid=4958860265658930737" rel="nofollow,noindex">Battery Historian</a> 在github上的readme, 一步步安装即可.</p> <p>需要注意的是, Battery Historian是Go语言的, 安装Go的时候需要配置其bin的环境变量.</p> <p>Python环境需要是2.7的(3.x不行), 建议使用pyenv管理本地的python环境.</p> <p>另外, 因为Battery Historian是一个网页版工具, 涉及一些JS引用, 有时需国内或许不能访问.</p> <p>安装完成后, 执行:</p> <pre> <code class="language-java">cd $GOPATH/src/github.com/google/battery-historian go run cmd/battery-historian/battery-historian.go [--port <default:9999>]</code></pre> <p>程序运行在 <a href="/misc/goto?guid=4959668811738351320" rel="nofollow,noindex">http://localhost:9999</a> , 如下:</p> <p><img src="https://simg.open-open.com/show/bf5da9f4db76ee1eb3a74d0b397229f7.png"></p> <p style="text-align: center;">battery historian running web</p> <p><strong>1.2.2 界面</strong></p> <p>导入我们在第一步通过adb bugreport生成的bugreport.txt文件:</p> <p><img src="https://simg.open-open.com/show/f412282da3d55155eb3f81c625309431.png"></p> <p style="text-align: center;">battery historian</p> <h2><strong>2, 主要的耗电因素</strong></h2> <p style="text-align:center"><img src="https://simg.open-open.com/show/6e19f7b10f0bcf0894222f7c494eb74f.png"></p> <p style="text-align:center">battery usage</p> <p>从手机的电池详情统计可以简单看出, 手机中最耗电的模块肯定是屏幕了, 接着就是网络相关, 另外可能的耗电大户还有GPS芯片, Camera等.</p> <p>对于一个App, 对应因素主要有:</p> <h3><strong>2.1 网络请求</strong></h3> <p>我们可能会有发现:</p> <ul> <li>测试用的手机充满电放了一个十一假期还有电, 是因为测试手机没有上SIM卡.</li> <li>飞行模式下的手机灭屏下, 可能可以放一个月都还有电.</li> </ul> <p>这是因为:</p> <ul> <li>手机的通过内置的射频模块和基站几乎, 从而链接上网的, 而这个射频模块(radio)是非常耗电的.</li> <li>为了控制这个射频模块的耗电, 硬件驱动及Android RIL层做了很多处理. 例如可以单独关闭radio(飞行模式), 间歇性假休眠radio(有数据发生时才上电, 保持一个频率的与基站交互)等等.</li> </ul> <p>现如今App都是移动互联网App, 不可避免的会有大量的网络请求, 会导致radio一直处于活跃状态, 从而耗电量增加.</p> <h3><strong>2.2 WakeLock</strong></h3> <p>Android系统本身为了优化电量的使用, 会在没有操作时进入休眠状态, 来节省电量. 当然, 为了便于开发(很多应用不可避免的希望在灭屏后还能运行一些事儿, 或是要保持屏幕一直亮着--比如播放视频), Android提供了一个PowerManager.WakeLock的东西.</p> <p>我们可以用WakeLock来保持CPU运行, 或是防止屏幕变暗/关闭, 让手机可以在用户不操作时依然可以做一些事儿. 然而, 获取WakeLock很容易, 释放不好就会成为难题, 消耗电量.</p> <p>例如我们获取了一个WakeLock来保持CPU运转, 做一个复杂运算并将数据上传到后台服务器, 然后释放该WakeLock. 然而这个过程可能并不像我们想象的那么快, 可能因为比如服务器挂掉, 计算出了异常等等WakeLock没有释放. 问题就来了, CPU会一直得不到休眠, 而大大增加耗电.</p> <p>另外, WakeLock还有android:keepScreenOn属性, 还可以让屏幕常量, 这可是耗电大户.</p> <h3><strong>2.3 GPS</strong></h3> <p>应用中经常会用到定位服务, Android提供了Network定位和GPS定位. 相对来说, GPS会精确得多, 对于一些诸如跑步, 导航类的应用基本会使用GPS定位. 然而, GPS定位也会消耗大量的电量.</p> <h2><strong>3, 尽可能减少App的电量消耗的建议</strong></h2> <p>了解了上述的主要的耗电因素, 还有一些程序的耗电问题, 我们通过Battery Historian也可以分析.</p> <p>针对这些耗电情况, 给出如下优化建议:</p> <h3><strong>3.1 优化网络请求</strong></h3> <p>这个会在网络优化那篇中细聊, 在此略过.</p> <h3><strong>3.2 谨慎使用WakeLock</strong></h3> <ol> <li>WakeLock获取释放成对出现.</li> <li>使用超时WakeLock, 以防出异常导致没有释放.</li> </ol> <pre> <code class="language-java">// Acquires the wake lock with a timeout. acquire(long timeout)</code></pre> <h3><strong>3.3 监听手机充电状态</strong></h3> <p>BatteryManager会发送一个包含充电状态的持续广播, 我们可以通过此广播获取充电状态和电量详情:</p> <pre> <code class="language-java">IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent batteryStatus = context.registerReceiver(null, ifilter);</code></pre> <p>注意: 因为这是一个持续广播, 我们无需写receiver, 可以直接通过intent获取相关数据.</p> <p>例如, 如果设备正在充电:</p> <pre> <code class="language-java">// Are we charging / charged? int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1); boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL; // How are we charging? int chargePlug = battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB; boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;</code></pre> <p>另外我们也可以监听充电状态的变化, 只要设备连接或断开电源, BatteryManager就会广播相应的操作, 我们可以注册receiver来监听:</p> <pre> <code class="language-java"><receiver android:name=".PowerConnectionReceiver"> <intent-filter> <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/> </intent-filter> </receiver></code></pre> <p>监听电池状态, 可以让我们将一些操作放在充电或是电量足够的情况下进行, 以提升用户体验. 例如用户数据同步, Log上传等.</p> <h3><strong>3.4 Doze and App Standby</strong></h3> <p>Android 6.0提供了两个用来节省电量的技术Doze和App Standby.</p> <ul> <li> <p>Doze</p> <p>瞌睡. 如果设备闲置了一段较长时间, Doze技术将通过延迟后台网络活动, CPU运行等来减少电量损耗.</p> </li> <li> <p>App Standy</p> <p>应用待机. 不是最近得到过用户"宠幸"的App, App Standy将延缓这个应用的后台网络活动.</p> </li> </ul> <p>因为所有Android 6.0及以上的设备上, Doze and App Standby都会运行. 可能会影响你的App的运行, 具体的适配请参考 <a href="/misc/goto?guid=4959719645743059025" rel="nofollow,noindex">官方文档</a> .</p> <h3><strong>3.5 关于定位</strong></h3> <ul> <li> <p>定位中使用GPS, 请记得及时关闭</p> </li> </ul> <pre> <code class="language-java">// Remove the listener you previously added locationManager.removeUpdates(locationListener);</code></pre> <ul> <li> <p>减少更新频率</p> </li> <li> <p>根据实际情况选择GPS或网络或两者. 只使用一个会降低电量损耗.</p> </li> </ul> <p> </p> <p>来自:http://www.jianshu.com/p/c55ef05c0047</p> <p> </p>