当前位置: 首页 > news >正文

Android电量相关知识

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。

目录

  • 一、导读
  • 二、概览
  • 三、 查看耗电情况
    • 3.1 注册广播 ACTION_BATTERY_CHANGED
    • 3.2 battery historion
    • 3.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 Lollipop(API 级别 21)及更高版本的 Android 设备上检查电池相关信息和事件,
而设备未插入电源。它允许应用程序开发人员在时间轴上可视化系统和应用程序级事件通过平移和缩放功能,可以轻松查看自设备上次充满电以来的各种汇总统计数据,
并选择一个应用程序并检查影响所选应用程序特定电池的指标。它还允许对两个错误报告进行 A/B 比较,突出显示关键电池相关指标的差异。

github 地址
google 地址

  1. 安装docker
  2. 安装battery historion
  3. 准备数据
先重置
adb shell dumpsys batterystats --resetadb shell dumpsys batterystats --enable full-wake-history导出
adb bugreport bugreport.zip
  1. 查看数据 & 分析

具体使用可自行学习。

3.3 手机设置

在手机设置里面也可以查看耗电排行,但是只有一个总的数据,不能定位哪里耗电,没啥大作用。

3.4 命令行

adb shell dumpsys batterystats > battery.txt

batterystats 所记录的电量统计数据源自于 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学习专栏

ddd

http://www.lryc.cn/news/301668.html

相关文章:

  • 【Java多线程】线程中几个常见的属性以及状态
  • 鸿蒙OS跨进程IPC与RPC通信
  • Effective Objective-C 学习(三)
  • 蓝桥杯备赛攻略
  • react反向代理
  • 债券专题二:可转债估值-二叉树模型
  • 【闲谈】开源软件的崛起与影响
  • 【教程】Linux使用aria2c多线程满速下载
  • 【漏洞复现】蓝网科技临床浏览系统信息泄露漏洞
  • JSON转换List<Map<String, Object>>、Map<String, Object>
  • 单主模式和多主模式切换
  • petalinux2018.3安装步骤
  • ubuntu22.04下使用conda安装pytorch(cpu及gpu版本)
  • 突破编程_C++_高级教程(模板编程的基础知识)
  • 胆小勿入!AI创作恐怖电影宣传片《生化危机:重生》
  • HTTP 超文本传送协议
  • MySQL导入/导出数据
  • Matplotlib初探:认识数据可视化与Matplotlib
  • LeetCode 0987.二叉树的垂序遍历:遍历时存节点信息,遍历完自定义排序
  • TCP 和 UDP的区别
  • Python 将一维数组或矩阵变为三维
  • Python如何实现定时发送qq消息
  • 支付方式接入:支付宝、微信支付、微软支付
  • C++中的互斥量
  • 盲盒小程序开发
  • 安装 Windows 10
  • C++文件操作->文本文件(->写文件、读文件)、二进制文件(->写文件、读文件)
  • Mac相关问题
  • Python爬虫之Splash详解
  • Deep深度系统下载安装Beyond compare4