摘要:项目中功耗问题查找。
1.功耗问题事件
这些天测试那边进行亮屏测试发现待机机器时间非常短,于是悲催的被安排来查查什么问题,不查不知道,一查还真有挺多让人f*k的问题:
1、下载任务
下载状态错误,一直在循环尝试下载,由于下载任务获取了wakelock锁,一是无法休眠,二是耗CPU;
2、上传任务
无网络状态下上传失败,也进行多次重试,由于数据采集通过上传任务完成,因此很容易触发上传,每次CPU占用率都到30%多;
3、推送任务
用了极光和云巴的推送,但是没哪一家确保能百分百推送成功,于是应用程序员每隔10秒主动去拉取数据,囧rz;
4、应用商城
5分钟主动拉取一次数据,也不知道干嘛的,关键是没网络也干这事;
5、OTAUpdate
系统OTA模块无网络状况下一开机循环几十次http请求,还定时发送http请求;
6、超级省电
正常情况一晚耗20%的电,回家测试一晚超级省电,居然没电了。回公司一查mediaserver CPU占用率最高到50%,原因是systemui在切换到超级省电时开启了camera。
7、桌面
桌面图标有一个图标,显示一个小时钟,每秒会刷新一次时间(刷屏),导致桌面应用的CPU占用率长期为1%。这个本来影响不大,不过拿亮屏时的桌面测试,自然不让过。
8、硬件问题
测试时使用了研发样机,触摸屏中断一直有,导致CPU占用率一直为10%,工程样机已改。
总结一下这些问题最终导致的都是CPU功耗高,待机时间自然就短了。说白了耗电实质还是在硬件设备的运转,如显示屏、CPU、GPU、WIFI、Codec等。如何降低功耗就是如何降低硬件功耗,由于软件控制硬件,硬件执行软件指定的行为,因此如何降低功耗就成了如何让软件尽量少处理任务。
以查询到的几个问题为例,无一不是因为程序流程不当导致CPU、WIFI等硬件一直处于高负载运行状态中。对于以上问题我们有必要好好考虑执行这些功能的条件与前提,例如网络是否可用、是否需要实时更新,然后再选择一种适合的方式。当然如果是BUG导致的,那也只能慢慢查了。
2.为什么要清理后台?
之前和同事讨论时同事说根据安卓机制应用切换到后台时会被pause,然后就不会耗CPU了。我只想说图样图森破了,如果真那么简单就好了,要不OPPO搞什么纯净后台。功耗不只是我们能看到界面所在进程,如果应用通过定时器定时执行某段代码,那么必然需要CPU处理,那功耗问题就来了。当某个后台进程设置了类型为RTC_WAKEUP或ELAPSED_REALTIME_WAKEUP的alarm,那么AlarmManagerService中会执行WakeLock.acuqire()将CPU从深度休眠时中唤醒。如果定时器唤醒的很频繁,那么将带来极大的电量损耗。
现在很多APP根本不考虑手机的功耗问题,只要自己的应用能在机器中活着就行,在被kill掉后又通过各种方式复活,为解决这个问题需要引入新的进程启动控制管理机制,纯净后台是个不错的想法。
3.如何查找功耗问题
在上面给出了导致功耗过高的原因,那么怎么才能检测出应用功耗过高呢?网上有BetterBatteryStats、Bettray Monitor Widget Pro等,然而作为技术流自然喜欢自己倒腾,一是兴趣,二也是有助于自己提高理解。
首先从网络访问来说,我不希望修改系统源码再进行测试,于是通过hook机制,将http请求的地方全部记录下来进行统计分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| private void hookHttpUrlConnection() { XC_MethodHook.Unhook unhook = XposedHelpers.findAndHookMethod(URL.class, "openConnection", new XC_MethodHook() { @Override protected void beforeHookedMethod( MethodHookParam param) throws Throwable { URL url = (URL) param.thisObject; Log.e("NetWorkHook","AllTester NetworkTester URL openConnection,Package:"+AndroidAppHelper.currentPackageName() + " TIME:"+System.currentTimeMillis()+"ms"+ " URL:"+url.toString()); } }); if (unhook != null) { mUnhookList.add(unhook); } } private void hookHttpClient() { XC_MethodHook.Unhook unhook = XposedHelpers.findAndHookMethod(DefaultRequestDirector.class, "execute", HttpHost.class, HttpRequest.class,HttpContext.class, new XC_MethodHook() { @Override protected void afterHookedMethod( MethodHookParam param) throws Throwable { HttpRequest request = (HttpRequest) param.args[1]; Log.e("NetWorkHook","AllTester NetworkTester DefaultRequestDirector execute,Package:"+AndroidAppHelper.currentPackageName()+" TIME:"+System.currentTimeMillis()+"ms"+ " URL:"+request.getRequestLine().getUri()); } }); if (unhook != null) { mUnhookList.add(unhook); } }
|
其次对定时器进行统计,大致流程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| private void hookActivityManagerServicesetWakelockWorkSource() { Class<?> hookClass = null; try { hookClass = Class.forName("com.android.server.AlarmManagerService", false, Thread.currentThread().getContextClassLoader()); } catch (ClassNotFoundException e) { e.printStackTrace(); } if (hookClass == null) { Log.e("AlarmTester","AllTester AlarmTester WAKELOCK NULL END HOOK"); return; } XC_MethodHook.Unhook unhook = HookUtils.hookLongestMethod(hookClass, "setWakelockWorkSource", new XC_MethodHook() { @Override protected void beforeHookedMethod( MethodHookParam param) throws Throwable { PendingIntent pendingIntent = (PendingIntent) param.args[0]; int type = (Integer) param.args[2]; String tag = (String)param.args[3]; boolean first = (Boolean)param.args[4]; if (pendingIntent != null) { Log.e("AlarmTester","AllTester AlarmTester WAKELOCK alarm setWakelockWorkSource,Package:"+pendingIntent.getTargetPackage() + " type:"+AlarmMessageWhat.codeToString(type)+" tag:"+tag+" first:"+first+" " + getCurrentTimeDesc()); } } }); if (unhook != null) { mUnhookList.add(unhook); } }
|
接下想法是对CPU使用率进行采样,基本想法如手动方式是adb shell top一样,定时获取当前采样的进程CPU使用率,然后进行统计。另外android已经帮我们统计了电量信息,通过dumpsys batterystats可以查看,是否可以借鉴,需要后面实现自动化检测工具时再进行分析。
4.总结
这里分析了引起功耗高的各软件因素以及功耗的根本原因,同时查出了很多隐藏的问题,通过这些问题的解决很好的验证了降低功耗的方法。不足的是这些问题的查找都是通过查看打印日志手动分析,如何构建一个自动化分析的工具才是终极解决方式。