电量重视度不够:开发中一直连着手机
电量消耗线上难以量化
设置界面-耗电排行
直观,但是没有详细数据,对解决问题没有太多帮助
找特定场景专项测试(如在详情页中进行一段时间的操作)
注册电量相关的广播ACTION_BATTERY_CHANGED
获取电池电量、充电状态、电池状态等信息
价值不大:针对手机整体的耗电量。而非特定的App
//示例代码
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
Intent intent = registerReceiver(null, filter);
LogUtils.i("battery " + intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1));
Battery Historain
Google推出的一款Android系统电量分析工具
支持5.0(API21)及以上系统的电量分析
功能强大,推荐使用
可视化的展示指标:耗电比例、执行时间、次数
适合线下使用
耗电场景测试:复杂运算、视频播放
传感器相关:使用时长、耗电量、发热
后台静默测试
https://github.com/google/battery-historian
安装Docker(http://www.runoob.com/docker/docker-tutorial.html 教程)
docker – run -p :9999 gcr.io/android-battery-historian/stable:3.0 --port 9999
adb shell dumpsys batterystats --reset 电量重置
adb shell dumpsys batterystats --enable full-wake-history 开始记录电量信息
adb shell bugreport bugreport.zip 导出电量信息
http://localhost:9999
上传bugreport文件即可
备用:https://bathist.ef.lc/
获取各个模块的能耗(如屏幕、wifi,bluetooth等)
adb pull /system/framework/framework-res.apk
反编译,xml ————> power_profile
Aop辅助统计:次数、时间
以WakeLock为例子
https://www.cnblogs.com/leipDao/p/8241468.html (安卓电量优化之WakeLock锁机制全面解析)
https://www.jianshu.com/p/67ccdac38271 (Android 功耗分析之wakelock)
public class WakeLockUtils {
private static PowerManager.WakeLock sWakeLock;
public static void acquire(Context context){
if(sWakeLock == null){
sWakeLock = createWakeLock(context);
}
if(sWakeLock != null && !sWakeLock.isHeld()){
sWakeLock.acquire();
sWakeLock.acquire(1000);
}
}
public static void release(){
// 一些逻辑
try{
}catch (Exception e){
}finally {
// 为了演示正确的使用方式
if(sWakeLock != null && sWakeLock.isHeld()){
sWakeLock.release();
sWakeLock = null;
}
}
}
@SuppressLint("InvalidWakeLockTag")
private static PowerManager.WakeLock createWakeLock(Context context){
PowerManager pm = (PowerManager) context.getApplicationContext().getSystemService(Context.POWER_SERVICE);
if(pm != null){
return pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"");
}
return null;
}
}
// 此处模拟的是WakeLock使用的兜底策略
WakeLockUtils.acquire(holder.imageView.getContext());
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
WakeLockUtils.release();
}
},200);
//通过Hooker监控
public class ActivityHooker {
public static ActivityRecord sActivityRecord;
static {
sActivityRecord = new ActivityRecord();
}
public static String trace;
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);
Origin.callVoid();
}
}
超过阀值预警(运行时间过长可以停止)
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));
}
1、CPU时间片
获取运行过程线程CPU的消耗,定位CPU占用率异常位置
减少后台应用的主动运行
2、网络相关
发起网络请求时机及次数
数据压缩,减少时间
禁止使用轮询功能进行业务操作
3、定位相关
根据场景谨慎选择定位模式(精度,频率)
考虑网络定位代替GPS
使用后及时关闭,减少更新
4、界面相关
离开界面后停止相关活动
耗电操作判断前后台
5、wakelock相关
注意成对出现:acquire与release
使用带参数的acquire
finally确保一定会被释放
常亮场景使用keeoScreenOn即可
6、JobScheduler
在符合某些条件时创建执行在后台的任务
把不紧急的任务放到更合适的时机批量处理
https://www.jianshu.com/p/9fb882cae239 (Android Jobscheduler使用)
//创建任务
public class JobSchedulerService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
// 此处执行在主线程
// 模拟一些处理:批量网络请求,APM日志上报
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
//manifest注册
/**
* 演示JobScheduler的使用
*/
private void startJobScheduler() {
//5.0之后才能使用
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()));
builder.setRequiresCharging(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
jobScheduler.schedule(builder.build());
}
}