重庆公司网站 技术支持,做管道方面的网站,126企业邮箱入口,做网站用的关于作者#xff1a;CSDN内容合伙人、技术专家#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 #xff0c;擅长java后端、移动开发、商业变现、人工智能等#xff0c;希望大家多多支持。 目录 一、导读二、概览三、 查看耗电情况3.1 注册广播 ACTION… 关于作者CSDN内容合伙人、技术专家 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 擅长java后端、移动开发、商业变现、人工智能等希望大家多多支持。 目录 一、导读二、概览三、 查看耗电情况3.1 注册广播 ACTION_BATTERY_CHANGED3.2 battery historion3.3 手机设置3.4 命令行3.5 AOP 代理hook 四、优化思路五、 推荐阅读 一、导读
我们继续总结学习知识温故知新。
本文主要讲了一些电量相关知识。
二、概览
电量的消耗在线上是难以量化目前没有很好的方式能精准的获取到线上用户电量消耗情况所以电量测试在线下非常关键 我们要测试重点业务耗电相关的场景以及app处于后台时耗电量**(后台静默测试)**、app网络请求时机及请求次数。
特别是一些比价耗电的场景如
Camera、Audio、Video、Bluetooth、Network、Wakelock、Sensor、Radio、Screen、WIFI、CPU、GPS
三、 查看耗电情况
模块电量mAh 模块电流mA* 模块耗时h厂商在 /frameworks/base/core/res/res/xml/power_profile.xml 文件中提供了组件的电源配置文件。
3.1 注册广播 ACTION_BATTERY_CHANGED
这种方式拿到的信息相对较少。 IntentFilter filter new IntentFilter();filter.addAction(Intent.ACTION_BATTERY_CHANGED);Intent intent registerReceiver(null, filter);int level intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)我们可以获取到电池的电量、电池状态但这个不能反应单个app的耗电情况。
这里得到的数据都是手机整体电量对排查耗电帮助也不到。
3.2 battery historion
可以拿到精准的电量信息及使用信息
Battery Historian 是一个工具用于在运行 Android 5.0 LollipopAPI 级别 21及更高版本的 Android 设备上检查电池相关信息和事件 而设备未插入电源。它允许应用程序开发人员在时间轴上可视化系统和应用程序级事件通过平移和缩放功能可以轻松查看自设备上次充满电以来的各种汇总统计数据 并选择一个应用程序并检查影响所选应用程序特定电池的指标。它还允许对两个错误报告进行 A/B 比较突出显示关键电池相关指标的差异。
github 地址 google 地址
安装docker安装battery historion准备数据
先重置
adb shell dumpsys batterystats --resetadb shell dumpsys batterystats --enable full-wake-history导出
adb bugreport bugreport.zip查看数据 分析
具体使用可自行学习。
3.3 手机设置
在手机设置里面也可以查看耗电排行但是只有一个总的数据不能定位哪里耗电没啥大作用。
3.4 命令行
adb shell dumpsys batterystats battery.txtbatterystats 所记录的电量统计数据源自于 BatteryStatsService-电量统计服务其实现类为 BatteryStatsImpl内部正是使用的 PowerProfile 。
BatteryStatsImpl 为每一个应用创建与之对应的 UID 来监控器系统资源的使用情况其统计了 12 大模块的电量消耗如下所示
Camera、Audio、Video、Bluetooth、Network、Wakelock、Sensor、Radio、Screen、WIFI、CPU、GPS
在 battery.txt 搜索 ‘Estimated power use’ 关键字可以看到大概的信息。
3.5 AOP 代理hook
我们可以通过 aop 辅助统计耗电组件如果耗电组件在用户的使用过程中使用过多 那么则可以辅助断定这个用户可能出现了耗电的情况那我们就要去了解一下情况。
我们也可以通过代理对应的 Service 实现完成收集 Wakelock、Alarm、GPS 的申请堆栈、释放信息、手机充电状态等等。
public abstract class ProxyHook extends Hook implements InvocationHandler {/*** 要代理的真实对象* 持有的被代理对象就是你要代理谁*/private Object proxyObj;public ProxyHook(Context context) {super(context);}public void setProxyObj(Object proxyObj) {this.proxyObj proxyObj;}Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {HookedMethodHandler hookedMethodHandler mBaseHookHandle.getHookedMethodHandler(method);if (hookedMethodHandler ! null){// beforeInvoke(receiver, method, args); 方法开始// 执行方法调用Object invokeResult hookedMethodHandler.doHookInner(proxyObj, method, args);// afterInvoke(receiver, method, args, invokeResult); 方法执行结束return invokeResult;}return method.invoke(proxyObj, args);}
}Overridepublic void onInstall() {Object oldObj mContext.getSystemService(Context.ALARM_SERVICE);Class? clazz oldObj.getClass();try {// 获取原始mService字段Field field clazz.getDeclaredField(mService);field.setAccessible(true);// 返回指定对象上此 Field 表示的字段的值// IAlarmManager mService;final Object mService field.get(oldObj);// 设置被代理对象也可以通过构造方法传入setProxyObj(mService);// 创建代理Object proxyObject Proxy.newProxyInstance(this.getClass().getClassLoader(), mService.getClass().getInterfaces(), this);// 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。其实就是给 mService 重新赋值代理对象field.set(oldObj, proxyObject);/* 这里举个简单的例子将用户的年龄修改为33//获取public 修饰的 指定字段名称的Field类包含父类字段Field field clazz.getField(age);field.set(user, 33);*/} catch (NoSuchFieldException e) {} catch (IllegalAccessException e){}}源码下载
四、优化思路
在实际使用中一般耗电比较多的场景有视频播放、定位、复杂运算、wakelock、网络等 所以要避免后台长时间使用耗电组件。
同时也可以通过 cpu profiler 查看是否处于高cpu运行状态定位 CPU 占用率异常方法。
然后针对网络请求的优化能使用wifi就使用wifi用 WIFI 连接网络时的功耗要低于使用移动网络的功耗 蜂窝移动网络下需要对请求时机及次数控制能不请求就不请求合理设计请求时机禁止使用轮询导致网络请求一直处于激活状态。
在就是定位根据场景谨慎选择定位模式对定位准确度没那么高的场景可以选择低精度模式或者网络定位代替 GPS根据业务来合理 设计请求频率使用后要及时关闭。
用户页面上比如动画要及时关闭后台不执行等等。
大家在具体项目中具体分析。
五、 推荐阅读
Java 专栏
SQL 专栏
数据结构与算法
Android学习专栏