分类
1)、高压低电流快充方案:在充电过程中提升充电电压(7-20V)来提升充电功率。
2)、低压大电流快充方案:在电压一定情况下,增加电流,通常使用并联电路的方式进行分流。
3)、铝-石墨烯超级电池
超高耐用性和安全性,快充充电1.1秒就能充满电。
实验阶段。
3、寿命
通常使用充电循环次数衡量。
4、安全性
严格控制电池容量,例如 VOOC 就使用了各种安全检测技术。
5、电量和硬件
手机耗电是通过使用相应的硬件模块来消耗电能。
CPU、屏幕、WIFI、数据网络、GPS、音视频通话在日常耗电量中占比最大。
6、Android 耗电演进
KITKAT
批处理传感器
分批有效地收集和传递传感器事件。
Alarm 对齐
批处理在合理的相似时间内的所有应用的闹铃,以便系统仅唤醒一次。
Lollipop
开启 Volta 项目
Job Scheduler
dumpsys batterystats
Battery Historian
修复 native fork 进程保活的 bug
Marshmallow
省电功能
Doze 低功耗模式
App Standby 应用待机摸手机
Nougat
优化省电功能
Doze 加强版
implicit broadcasts 显示
混合编译
Oreo
更多优化省电功能
后台执行限制
后台位置限制
P(电压管理严格限制)
应用待机分组(App Standby Bueckets)
从应用安装开始。
分组决定后台被限制的程度。
不常用的应用将被限制地更加严格。
应用后台限制(Background Restrictions)
用户开启。
停止后台运行。
提示用户后台耗电严重的应用,用户可选择停止它们的后台运行。
省电模式(Battery Saver)
用户开启。
所有应用进入待机模式。
更加严格的后台限制,而且无视应用的 Target API。
三、电量检测方案
=================================================================
对于电量的统计有一个公式,如下所示:
模块电量(mAh) = 模块电流(mA)* 模块耗时(h)
Android 系统要求 ROM 厂商必须在 /frameworks/base/core/res/res/xml/power_profile.xml 提供组件的电源配置文件。而 Android 系统的电量计算 PowerProfile 正是通过读取 power_profile.xml 的数据。
1、设置—耗电排行
1)、直观,但没有详细数据,对解决问题帮助不大。
2)、需要找特定场景专项测试,比如在某一个界面操作一段时间,然后来判断这个页面是否耗电。
2、使用广播监听电量变化—ACTION_BATTERY_CHANGED
获取电池电量、充电状态、电池状态等信息。
实战案例
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
Intent intent = registerReceiver(null, filter);
LogUtils.i("battery " + intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1));
缺点
1)、价值不大:针对手机整体的耗电量,而非单个 App。
2)、实时性差、精度较低,被动通知。
3、dumpsys batterystats
batterystats 是 Android 5.0 提供的工具,它可以获取各个 App 的 WakeLock、CPU 时间占用等信息,同时增加了一个 Estimated power use(mAh)功能,预估耗电量。
作用
将电量测量转化为功能模块的使用时间或者次数。
adb shell dumpsys batterystats > battery.txt
在 battery.txt 搜索 ‘Estimated power use’ 关键字,下面粗略统计了各个 Uid 的总耗电量。
Estimated power use (mAh):
Capacity: 3350, Computed drain: 2767, actual drain: 3752-3853
Uid 1000: 1014 ( cpu=999 wake=1.36 radio=11.4 wifi=1.24 gps=0.435 sensor=0.808 ) Excluded from smearing
Unaccounted: 985 ( ) Including smearing: 0 ( ) Excluded from smearing
Uid 0: 416 ( cpu=157 wake=210 radio=38.8 wifi=9.51 ) Excluded from smearing
…
batterystats 所记录的电量统计数据源自于 BatteryStatsService-电量统计服务,其实现类为 BatteryStatsImpl,内部正是使用的 PowerProfile 。
BatteryStatsImpl 为每一个应用创建与之对应的 UID 来监控器系统资源的使用情况,其统计了 12 大模块的电量消耗,如下所示:
Camera、Audio、Video
Bluetooth、Network、Wakelock
Sensor、Radio、Screen
WIFI、CPU、GPS
4、Battery Historian
特点
1)、查看自设备上次充电以来各种汇总统计信息,而且可以选择对应的 App 查看详细信息。
2)、可视化展示指标:
耗电比例。
执行时间、次数。
3)、仅适合线下使用。
安装
1)、安装 Docker
2)、docker – run -p:9999 gcr.io/android-battery-historian/stable:3.0 --port 9999 (需要)
导出电量信息
1)、使用 batterystats 命令重置手机电量:adb shell dumpsys batterystats --reset
2)、使用 batterystats 命令获取电池数据权限并开启记录全面的电量信息:adb shell dumpsys batterystats --enable full-wake-history
3)、测试完成后,使用 bugreport 导出电量信息:
7.0和7.0以后:adb bugreport bugreport.zip
6.0和6.0之前:adb bugreport > bugreport.txt
通过 historian 图形化展示结果:python historian.py -a bugreport.txt > battery.html
上传分析
如果打不开,可以使用备用网站 https://bathist.ef.lc/
Battery Historian 数据分析
Hitorian V2 — 电量统计图表
Add Metrics
![](https://img-blog.csdn
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
img.cn/img_convert/08cae2f8ea15e60a693875b47a2560fe.png)
在 Add Metrics 中我们可以增加更多的测量项。
CPU running
如果一直处于 running,则表明电量消耗比较高。
JobScheduler
选中 Job Scheduler 的某一个工作时间片,我们可以查看具体的 发生的时间、耗时以及次数,最重要的是它统计出来了是哪一个进程在使用这个 JobScheduler。
App Selection
1)、选择要分析电量的指定 App。
2)、点击右边区域的 System Stats 一栏可以在下方查看各个系统组件的电量百分比消耗详情,例如 Userspace Wakelocks。
主入口处的 Switch to Bugreport Comparison
选择多个文件进行上传对比。
5、电量专项测试
1)、耗电场景测试
复杂计算。
音视频播放。
2)、传感器相关
使用时长
耗电量
发热
3)、后台静默测试
四、耗电优化
===============================================================
1、耗电优化的难点
1)、「缺乏现场,无法复现」。
2)、「信息不全,难以定位」。
3)、「无法评估结果」。
在 App 开发中,经常会由于某个需求场景或 代码 bug 而导致大量耗电。
2、后台调度任务省电
思考步骤
需要后台运行
长时间下载:DownloadManager
数据同步:SyncAdapter
本地任务:JobScheduler
特定时间执行:AlarmManager
实时通信:推送服务
立刻执行:Foreground Service
对于耗电优化中,我们最常用的就是 JobScheduler,下面,我们来实战一下。
Job Scheduler 实战
/**
*/
private void startJobScheduler() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(1, new ComponentName(getPackageName(), JobSchedulerService.class.getName()));
// 设置仅在 充电和WIFI 下才使用 JobScheduler 进行批量任务处理
builder.setRequiresCharging(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
jobScheduler.schedule(builder.build());
}
}
其中,「JobSchedulerService 就是用于进行批量任务处理的服务」,示例代码如下所示:
/**
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class JobSchedulerService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
// 此处执行在主线程
// 模拟一些处理:批量网络请求,APM日志上报
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
特点
1)、「仅支持 API 21 及之上」。
2)、「在符合某些条件时创建执行在后台的任务」。
3)、「把不紧急的任务放到更合适的时机批量处理」。
符合 Android 规则,手机在充电状态才去做耗电工作。示例代码如下所示:
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
//获取用户是否在充电的状态或者已经充满电了
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL;
3、电量优化套路总结
避免后台长时间获取 WakeLock、WIFI 和蓝牙的扫描等。
Android P 使用了 Android Vitals 监控后台耗电,其规则如下所示:
1)、Alarm Manager wakeup 唤醒过多:当手机不在充电状态,每小时 wakeup 唤醒次数大于 10 次。
2)、频繁使用局部唤醒锁:当手机不在充电状态,partial wake lock 持有超过1小时。
3)、后台网络使用量过高:当手机不在充电状态而且应用在后台,每小时网络使用量超过 50MB。
4)、后台 WiFi scans 过多:当手机不在充电状态而且应用在后台,每小时大于4次 WiFi scans。
「Android 手机保护 AP 和 BP 两个 CPU。AP 即 Application Processor,所有的用户界面以及 App 都是运行在 AP 上的。BP 即 Baseband Processor,手机射频都是运行在这个 CPU 上的。而一般我们所说的耗电,PowerProfile 文件里面的 CPU,指的是 AP」。
CPU 耗电通常有两种情况:
1)、「长期频繁唤醒:原本可以仅仅在 BP 上运行,消耗 5mA 左右,但是因为唤醒,AP 就会运作,不同手机情况不一样,至少会导致 20~30 mA 左右的耗电」。
2)、「CPU 长期高负荷:例如 App 退到后台的时候没有停止动画,或者程序有不退出的死循环等等,导致 CPU 满频、满核地跑」。
常用优化 CPU 时间片的方式有:
1)、「获取运行过程线程 CPU 消耗,定位 CPU 占用率异常方法」。
2)、「减少后台应用的主动运行」。
通常情况下,使用 WIFI 连接网络时的功耗要低于使用移动网络的功耗。而使用移动网络传输数据,电量的消耗有以下3种状态:
「Full power:高功率状态,移动网络连接被激活,允许设备以最大的传输速率进行操作」。
「Low power:低功耗状态,对电量的消耗差不多是 Full power 状态下的 50%」。
「Standby:空闲态,没有数据连接需要传输,电量消耗最少」。
因此,为了避免网络连接所带来的电量消耗,我们可以采用如下几种方案:
1)、尽量在 WIFI 环境下进行数据传输,在使用 WIFI 传输数据时,应该尽可能增大每个包的大小(不超过 MTU),并降低发包的频率。
2)、在蜂窝移动网络下需要对请求时机及次数控制:可以延迟执行的网络请求稍后一起发送,最好做到批量执行,尽量避免频繁的间隔网络请求,以尽量多地保持在 Radio Standby 状态。
3)、使用 JSON 和 Protobuf 进行数据压缩,减少时间。
4)、禁止使用轮询功能:轮询会导致网络请求一直处于被激活的状态,耗电过高。
1)、「根据场景谨慎选择定位模式:对定位准确度没那么高的场景可以选择低精度模式」。
2)、「可以考虑网络定位代替 GPS」。
3)、「使用后务必及时关闭,减少更新频率,例如定位开启一定时间后超过某个阈值可以执行一个兜底策略:强制关闭 GPS」。
1)、「离开界面后停止相关活动,例如关闭动画」。
2)、「耗电操作判断前后台,如果是后台则不执行相关操作」。
WakeLock 常用于后台播放音视频、录制音视频、下载文件的情况。如果没有合理使用 WakeLock,则会造成严重的耗电问题,为了避免该问题,「我们应该定期针对使用了 WakeLock 的模块进行重点排查」。
我们可以使用 adb shell dumpsys power 命令查看系统当前的耗电信息,其中我们可以看到 WakeLock 列表,它通常会以**「”mLocks.size“ 或者 ”Wake Locks:size“」** 开头。关于 WakeLock 的使用我们要着重注意以下几点:
1)、「注意成对使用 acquire、release」。
2)、「建议使用带参数的 acquire,避免没有及时释放而导致电量消耗过大」。
3)、「使用 finally 确保 release 一定会被调用」。
4)、「常亮场景使用 keepScreenOn 即可」。
5)、「WakeLock 有一个接口 setReferenceCounted,用来设置 WakeLock 的技术机制,官方默认为计数。true 为计数,false 为不计数。所谓计数即每一个 acquire 必须对应一个 release;不计数则是无论有多少个 acquire,一个 release 就可以释放。但是问题是有的第三方 ROM 它将默认设置为了不计数,以为我们需要在调用 newWakeLock 之后再调用 setReferenceCounted 为 false」。
「浮点运算比整数运算更消耗 CPU 时间片,因此耗电也会增加」。避开浮点运算的优化方法如下所示:
1)、「除法变乘法」。
2)、「充分利用移位」。
3)、「在 native 层开发时,可以利用 ARM neon 指令集做并行运算,注意需要 ARM V7 及以上架构 CPU 才能支持」。
「我们可以监听灭屏以及亮屏的广播,在灭屏的时候停止 surfaceView 的动画绘制。在亮屏的时候,恢复动画的绘制」。
五、耗电监控
===============================================================
以后台耗电监控为主,必须监控的模块有:
1)、「Alarm wakeup」
2)、「WakeLock」
3)、「WiFi scans」
4)、「Network」
「必须监控的现场信息有」 :
1)、「堆栈信息」
2)、「是否充电」
3)、「电量水平」
4)、「应用前后台时间」
5)、「CPU 状态信息」
最后,我们需要 「提炼规则,将监控内容 => 抽象成规则」。
1、Java Hook
我们可以通过代理对应的 Service 实现,完成收集 Wakelock、Alarm、GPS 的申请堆栈、释放信息、手机充电状态等等。
❝
示例项目
❞
2、电量辅助监控实战
1)、adb pull /system/framework/framework-res.apk
2)、反编译,xml—》power_profile
线下使用 epic 进行 AOP 电量辅助统计
这里我们就以 WakeLock 的监控为例,切面代码如下所示:
public static long sStartTime = 0;
@Insert(value = “acquire”)
@TargetClass(value = “com.optimize.performance.wakelock.WakeLockUtils”,scope = Scope.SELF)
public static void acquire(Context context){
trace = Log.getStackTraceString(new Throwable());
sStartTime = System.currentTimeMillis();
Origin.callVoid();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
WakeLockUtils.release();
}
},1000);
}
@Insert(value = “release”)
@TargetClass(value = “com.optimize.performance.wakelock.WakeLockUtils”,scope = Scope.SELF)
public static void release(){
LogUtils.i(“PowerManager “+(System.currentTimeMillis() - sStartTime)+”/n”+trace);
此外,我们也可以利用 epic 来监控每个线程的执行时间,超过阈值则警告,示例代码如下所示:
public static long runTime = 0;
@Insert(value = “run”)
@TargetClass(value = “java.lang.Runnable”,scope = Scope.ALL)
public void run(){
runTime = System.currentTimeMillis();
Origin.callVoid();
LogUtils.i("runTime "+(System.currentTimeMillis() - runTime));
}
3、编译插桩
「写一个基础类,然后在统一的调用接口中添加监控逻辑」。这里我们可以参考 Facebook Battery-Metrics 获取、监控数据的方式。其代码如下所示:
public class WakelockMetrics {
/**
获取 WakeLock
@param wakeLock WakeLock
@param timeout 超时时间
*/
public static void acquire(PowerManager.WakeLock wakeLock, long timeout) {
wakeLock.acquire(timeout);
// 监控 wakelock 相关信息
Log.e(“HOOOOOOOOK”, “–acquireWakeLock–”);