电量斲丧的盘算与统计是一件贫苦而且抵牾的事变,记录电量斲丧本身也是一个费电量的事变,随着Android的性能要求越来越高,电量的优化,也显得格外紧张,一个耗电的应用,用户肯定会绝不夷由的举行卸载,以是本篇博客,我们一起来学习Android性能优化之电量优化。
耗电是怎样产生的?
耗电环境,比方:打开屏幕,全部要利用CPU/GPU工作的动作都会唤醒屏幕,都会斲丧电量。这和应用程序唤醒装备还不一样。
(1)唤醒屏幕
当用户电量屏幕的时间,意味着体系的各组件要开始举行工作,界面也必要开始实行渲染。
待机状态的电量斲丧:
利用和唤醒屏幕后:
当装备从休眠状态中,被应用程序唤醒时,就会产生一条电量利用高峰线。
当工作完成后,装备会主动举行休眠,这非常紧张,在不利用大概很少利用的环境下,长时间保持屏幕唤醒会敏捷斲丧电池的电量。
(2)蜂窝式无线
通过这张图,我们知道通过利用蜂窝无线时,会产生几个高峰:
当装备通过无线网发送数据的时间,为了利用硬件,这里会出现一个唤醒好点高峰。
接下来尚有一个高数值,这是发送数据包斲丧的电量,
然后担当数据包也会斲丧大量电量也看到一个峰值。
开启无线模式这个过程非常耗电,那么硬件这块为了防止频仍开启关闭耗电,采取了一个无奈的办法,会在一个小段时间内保持开启模式,防止短时间内尚有数据包必要吸取,也就是图中的KeepAwake的那一段。
怎样举行电量利用分析?
(1)电量数据网络
Android5.0及以上的装备,答应我们通过adb下令dump出电量利用统计信息.
1.由于电量统计数据是连续的,统计我们的待测试App之前先reset下,连上装备,下令行实行:
$adbshelldumpsysbatterystats--reset
Batterystatsreset.
2.断开测试装备,操纵我们的待测试App.
3.重新毗连装备,利用adb下令导出相干统计数据:
//此下令连续记录输出,想要克制记录时按Ctrl+C退出.
$adbbugreportbugreport.txt
导出的统计数据存储到bugreport.txt,此时我们可以借助如下工具来图形化展示电池的斲丧环境.
留意,官方SDK文档导出文件方式为:
adbshelldumpsysbatterystatsbatterystats.txt
利用Pythonhistorian.pybatterystats.txtbatterystats.html查察数据
是battery-historian老版本的利用方式.如今BatteryHistorian已更新2.0版本,保举利用bugreport方式导出数据分析,可以看到更多信息.
(2)电量分析工具BatteryHistorian
工具开源地点:https://github.com/google/battery-historian
根据gitbub上面先容,BatteryHistory工具的安装有两种方式:
1.通过安装Docker环境来安装。(必要翻墙)
Docker只支持Windows10
Gitbub上面是如许的下令及地点:
docker--run-pport:9999gcr.io/android-battery-historian:2.1--port9999
2.通过编译gitbub上面的源码来安装。
(1)GO环境安装:
1.下载
下载目次:https://golang.org/doc/install
https://golang.org/doc/install?download=go1.7.3.windows-amd64.msi
2.安装GO
3.设置GOROOT和GOPATH
a.GOROOT的作用是告诉Go下令和其他相干工具,在那边去找到安装在你体系上的Go包,以是这里设置的是GO的安装目次
b.GOPATH可以简单明白为是工程的目次,以是创建一个GO的工程路径
C.末了设置一下环境变量,把Go的bin目次放到path环境变量中
D.查抄Go是否安装乐成,打开下令行输入Goversion
(2)安装Git
2.按照步调安装;
3.安装完成查抄:下令行输入gitversion
(3)安装Python
2.安装完成;
3.环境变量设置,添加Path的路径,是Python的安装路径
4.输入下令行python–V(留意是大写V)查抄是否安装乐成
(4)安装Java环境
1.点击下载:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html;
2.完成安装。
(5)下载BatteryHistorian源码而且运行
输入下令行goget-d-ugithub.com/google/battery-historian/...
下载到GOPATH设置目次下
1.进入到$GOPATH/src/github.com/google/battery-historian目次下方
$cd$GOPATH/src/github.com/google/battery-historian
2.运行BatteryHistorian
1)gorunsetup.go
$gorunsetup.go
等待数分钟大概10分钟左右,假如仍旧没有下载乐成,可以手动下载,如下操纵
下载【closure-library】和【closure-compiler】和【flot-axislabels】,解压放到GOROOT目次下third_party文件夹下方的的closure-compiler和closure-library和flot-axislabels文件夹../battery-historianhird_party;假如没有均手动创建
2)goruncmd/battery-historian/battery-historian.go
$goruncmd/battery-historian/battery-historian.go[--portdefault:9999]
battery-historian利用
数据预备
battery-historian工具必要利用bugreport中的BatteryHistory
1.先断开adb服务,然后开启adb服务
adbkill-server这一步很紧张,由于当我们开辟时做电量记录时会打开很多大概造成辩论的东西。为了保险起见我们重启adb。
adbdevices就会主动毗连查找手机。固然也可以adbstart-server
2.重置电池数据网络
数据,我们在开始的时间必要通过以下下令来打开电池数据的获取以及重置:
adbshelldumpsysbatterystats--enablefull-wake-history
adbshelldumpsysbatterystats--reset
上面的操纵相称于初始化操纵,假如不这么做会有一大堆的干扰的数据,看起来会比力痛楚。然后把数据线直接拔掉(防止数据线造成充放电数据干扰),如今做一些测试,手动大概跑一些主动化的case都行。颠末一段时间后,我们重新毗连办机确认adb连上了,运行下面这条下令来将bugreport的信息生存到txt文档中,
adbbugreportbugreport.txt
大概用下面的下令也可以:
adbshelldumpsysbatterystatsbatterystats.txt
adbshelldumpsysbatterystatscom.example.android.demo.appbatterystats.txt
加上包名可以限定输出的数据是我们要检测的。
但是这个txt的数据可读性不强。接下来我们就要用到这个battery-historian工具了。
分析数据
各个参数的意义
起首我们在bugreport.txt找到BatteryHistory数据栏雷同下面的信息:
DUMPOFSERVICEbatterystats:
BatteryHistory(2%used,5980usedof256KB,45stringsusing2592):
0(9)RESET:TIME:2015-03-05-15-21-56
0(2)100c0900422status=discharginghealth=goodplug=nonetemp=200volt=4167+running+wake_lock+sensor+screendata_conn=edgephone_signal_strength=greatbrightness=mediumproc=u0a15:"android.process.acore"
0(2)100c0900422proc=u0a7:"com.android.cellbroadcastreceiver"
0(2)100c0900422proc=u0a53:"com.android.gallery3d"
你在html中信息都能从bugreport.txt中找到相应的信息。
如今来分析各个指标代表的意义:
横坐标
上面的10,20代表的就是秒的意思,它是以一分钟为周期,到第60秒的时间变为0。横坐标就是一个时间范围,咱们的例子中统计的数据是以重置为出发点,获取bugreport内容时候为尽头。我们一共收罗了多长时间的数据,图表下也有信息阐明。(经其他人的反馈,这个坐标隔断是会随着时间的长度发生改变,以是要以你的实际环境为准。这个缩放级别可以调解的,如下图:)
纵坐标
举行电量优化
TrackBatteryStatusBatteryManager
我们可以通过下面的代码来获取手机的当前充电状态:
//Itisveryeasytosubscribetochangestothebatterystate,butyoucangetthecurrent
//statebysimplypassingnullinasyourreceiver.Nifty,isn'tthat?
IntentFilterfilter=newIntentFilter(Intent.ACTION_BATTERY_CHANGED);
IntentbatteryStatus=this.registerReceiver(null,filter);
intchargePlug=batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED,-1);
booleanacCharge=(chargePlug==BatteryManager.BATTERY_PLUGGED_AC);
if(acCharge){
Log.v(LOG_TAG,“Thephoneischarging!”);
}
在上面的例子演示了怎样立即获取得手机的充电状态,得到充电状态信息之后,我们可以有针对性的对部分代码做优化。比如我们可以判定只有当前手机为AC充电状态时才去实行一些非常耗电的操纵。
/***Thismethodchecksforpowerbycomparingthecurrentbatterystateagainstallpossible*pluggedinstates.Inthiscase,adevicemaybeconsideredpluggedineitherbyUSB,AC,or*wirelesscharge.(WirelesschargewasintroducedinAPILevel17.)*/
privatebooleancheckForPower(){
IntentFilterfilter=newIntentFilter(Intent.ACTION_BATTERY_CHANGED);IntentbatteryStatus=this.registerReceiver(null,filter);
//Therearecurrentlythreewaysadevicecanbepluggedin.Weshouldcheckthemall.
booleanusbCharge=(chargePlug==BatteryManager.BATTERY_PLUGGED_USB);
booleanacCharge=(chargePlug==BatteryManager.BATTERY_PLUGGED_AC);
booleanwirelessCharge=false;
if(Build.VERSION.SDK_INT=Build.VERSION_CODES.JELLY_BEAN_MR1)
{wirelessCharge=(chargePlug==BatteryManager.BATTERY_PLUGGED_WIRELESS);
}
return(usbCharge||acCharge||wirelessCharge);
}
屏幕唤醒
有些时间我们必要改变Android体系默认的这种状态:比如玩游戏时我们必要保持屏幕常亮,比如一些下载操纵不必要屏幕常亮但必要CPU不停运行直到任务完成。
最好的方式是在Activity中利用FLAG_KEEP_SCREEN_ON的Flag。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
另一个方式是在布局文件中利用android:keepScreenOn属性:
RelativeLayoutxmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
...
/RelativeLayout
android:keepScreenOn=”true“的作用和FLAG_KEEP_SCREEN_ON一样。利用代码的长处是你答应你在必要的地方关闭屏幕。
留意:一样平常不必要人为的去掉FLAG_KEEP_SCREEN_ON的flag,windowManager会管理好程序进入背景回到前台的的操纵。假如确实必要手动清掉常亮的flag,利用getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
WakelockandBatteryDrain
假设你的手机内里装了大量的交际类应用,纵然手机处于待机状态,也会常常被这些应用唤醒用来查抄同步新的数据信息。一个最简单的唤醒手机的方法是利用PowerManager.WakeLock的API来保持CPU工作并防止屏幕变暗关闭。这使得手机可以被唤醒,实行工作,然后回到就寝状态。知道怎样获取WakeLock是简单的,但是及时开释WakeLock也黑白常紧张的,不得当的利用WakeLock会导致严峻错误。比方网络哀求的数据返回时间不确定,导致原来只必要10s的事变不停等待了1个小时,如许会使得电量白白浪费了。这也是为何利用带超时参数的wakelock.acquice()方法是很关键的。
wake_lock锁重要是相对体系的休眠而言的,意思就是我的程序给CPU加了这个锁那体系就不会休眠了,如许做的目标是为了尽力共同我们程序的运行。有的环境假如不这么做就会出现一些题目,比如微信等及时通讯的心跳包会在熄屏不久后克制网络访问等题目。以是微信内里是有大量利用到了wake_lock锁。
wake_lock:两种锁,一种计数锁;非计数锁(锁了很多次,只必要release一次就可以打扫了)
唤醒锁可分别为并辨认四种用户唤醒锁:
自API品级17开始,FULL_WAKE_LOCK被弃用。改为利用FLAG_KEEP_SCREEN_ON。
添加唤醒锁权限:
uses-permissionandroid:name="android.permission.WAKE_LOCK"/
直接利用唤醒锁:
PowerManagerpowerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLockwakeLock=powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag");
wakeLock.acquire();
留意:在利用该类的时间,必须包管acquire和release是成对出现的。否则当我们业务已经不必要时,当CPU处于唤醒状态,这个时间就会斲丧多余的电量。
但是仅仅设置超时并不敷够办理题目,比方设置多长的超时比力符合?什么时间举行重试等等?办理上面的题目,精确的方式大概是利用非精准定时器。通常环境下,我们会设定一个时间举行某个操纵,但是动态修改这个时间大概会更好。比方,假如有别的一个程序必要比你设定的时间晚5分钟唤醒,最好可以或许比及谁人时间,两个任务捆绑一起同时举行,这就黑白正确定时器的核心工作原理。我们可以定制筹划的任务,但是体系假如检测到一个更好的时间,它可以推迟你的任务,以节流电量斲丧。
JobScheduler
JobSchedule的宗旨就是把一些不是特别告急的任务放到更符合的机遇批量处理惩罚。
自界说一个Service类,继承自JobService
publicclassJobSchedulerServiceextendsJobService{
privateStringTAG=JobSchedulerService.class.getSimpleName();
@Override
publicbooleanonStartJob(JobParametersjobParameters){
Log.d(TAG,"onStartJob:"+jobParameters.getJobId());
if(true){
//JobService在主线程运行,假如我们这里必要处理惩罚比力耗时的业务逻辑需单独开启一条子线程来处理惩罚并返回true,
//当给定的任务完成时通过调用jobFinished(JobParametersparams,booleanneedsRescheduled)告知体系。
//假设开启一个线程去下载文件
newDownloadTask().execute(jobParameters);
returntrue;
}else{
//假如只是在本方法内实行一些简单的逻辑话返回false就可以了
returnfalse;
}
}
/**
*比如我们的服务设定的束缚条件为在WIFI状态下运行,结果在任务运行的过程中WIFI断开了体系
*就会通过回掉onStopJob()来关照我们克制运行,正常的环境下不会回掉此方法
*
*@paramjobParameters
*@return
*/
@Override
publicbooleanonStopJob(JobParametersjobParameters){
Log.d(TAG,"onStopJob:"+jobParameters.getJobId());
//假如必要服务在设定的约定条件再次满意时再次实行服务请返回true,反之false
returntrue;
}
classDownloadTaskextendsAsyncTaskJobParameters,Object,Object{
JobParametersmJobParameters;
@Override
protectedObjectdoInBackground(JobParameters...jobParameterses){
mJobParameters=jobParameterses[0];
//比如说我们这里处理惩罚一个下载任务
//或是处理惩罚一些比力复杂的运算逻辑
//...
try{
Thread.sleep(30*1000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
returnnull;
}
@Override
protectedvoidonPostExecute(Objecto){
super.onPostExecute(o);
//假如在onStartJob()中返回true的话,处理惩罚完成逻辑后肯定要实行jobFinished()告知体系已完成,
//假如必要重新安排服务请true,反之false
jobFinished(mJobParameters,false);
}
}
}
记得在Manifest文件内设置Service
serviceandroid:name=".JobSchedulerService"android:permission="android.permission.BIND_JOB_SERVICE"/
创建工作筹划
publicclassMainActivityextendsActivity{
privateJobSchedulermJobScheduler;
privatefinalintJOB_ID=1;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.mai_layout);
mJobScheduler=(JobScheduler)getSystemService(Context.JOB_SCHEDULER_SERVICE);
//通过JobInfo.Builder来设定触发服务的束缚条件,最少设定一个条件
JobInfo.BuilderjobBuilder=newJobInfo.Builder(JOB_ID,newComponentName(this,JobSchedulerService.class));
//循环触发,设置任务每三秒定期运行一次
jobBuilder.setPeriodic(3000);
//单次定时触发,设置为三秒以后去触发。这是与setPeriodic(longtime)不兼容的,
//而且假如同时利用这两个函数将会导致抛出非常。
jobBuilder.setMinimumLatency(3000);
//在约定的时间内设置的条件都没有被触发时三秒以后开始触发。雷同于setMinimumLatency(longtime),
//这个函数是与setPeriodic(longtime)相互倾轧的,而且假如同时利用这两个函数,将会导致抛出非常。
jobBuilder.setOverrideDeadline(3000);
//在装备重新启动后设置的触发条件是否尚有效
jobBuilder.setPersisted(false);
//只有在装备处于一种特定的网络状态时,它才触发。
//JobInfo.NETWORK_TYPE_NONE,无论是否有网络均可触发,这个是默认值;
//JobInfo.NETWORK_TYPE_ANY,有网络毗连时就触发;
//JobInfo.NETWORK_TYPE_UNMETERED,非蜂窝网络中触发;
//JobInfo.NETWORK_TYPE_NOT_ROAMING,非环游网络时才可触发;
jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
//设置手机充电状态下触发
jobBuilder.setRequiresCharging(true);
//设置手机处于空闲状态时触发
jobBuilder.setRequiresDeviceIdle(true);
//得到JobInfo对象
JobInfojobInfo=jobBuilder.build();
//设置开始安排任务,它将返回一个状态码
//JobScheduler.RESULT_SUCCESS,乐成
//JobScheduler.RESULT_FAILURE,失败
if(mJobScheduler.schedule(jobInfo)==JobScheduler.RESULT_FAILURE){
//安排任务失败
}
//克制指定JobId的工作服务
mJobScheduler.cancel(JOB_ID);
//克制全部的工作服务
mJobScheduler.cancelAll();
}
来自:CSDN-马云龙
https://blog.csdn.net/u012124438/article/details/74617649#t3
程序员大咖整剃头布,转载请接洽作者得到授权
我要评论