Android应用耗电量分析与优化建议

Battery Historian工具使用

Battery Historian 一款由Google提供的Android系统电量分析工具,从手机中导出bugreport文件上传至页面,在网页中生成详细的图表数据来展示手机上各模块电量消耗过程,最后通过App数据的分析制定出相关的电量优化的方法。
https://github.com/google/battery-historian

运行环境

根据gitbub上面介绍,Battery Historian工具的安装有两种方式:

  1. 通过安装Docker环境来安装。(这种方式很简单,推荐使用)
  2. 安装GO环境、Python、Java环境。(安装比较繁琐感兴趣可以去GitHub上看看)

Docker环境

Docker是一种容器,一般用于云计算和大数据平台。提倡的一种思想就是:软件即服务。一句话就可以将别人发布的docker服务环境一次全部copy过来(注意是整个软件环境,相当于复制了一台一模一样的主机,连软件都不要安装了,全有了。)

官方下载地址:

安装Battery Historian命令

安装完Docker环境后通过以下命令来安装Battery Historian工具:

Mac:

# <port>替换成本机一个端口
$ docker -- run -p <port>:9999 gcr.io/android-battery-historian:2.1 --port 9999

在此需要个漫长的下载安装等待,完成后在浏览器输入http://localhost:9999地址可以正常访问到Battery Historian页面说明已经成功了。

提交文件页面

Windows:

没有尝试!可参考:https://github.com/google/battery-historian

App电量消耗分析

工具安装成功后我们需要从手机上导出数据进行分析

# 将bugreport的信息保存到.zip文件中
$ adb bugreport bugreport.zip

Android 6.0以下系统请使用以下导出命令

# 将bugreport的信息保存到txt文档中
$ adb bugreport bugreport.txt

打开浏览器输入http://localhost:9999成功访问页面,将导出的.zip或.txt文件进行提交生成分析图表:

在该图中左侧为各单元模块,X轴为对应的时间线,可通过点击左侧Batter level(电量百分比)、Coulomb charge(电池容量)、Temperature(手机温度)选项展示出相应的变化曲线

电量消耗明细

通过选择标签System Stats、History Stats来查看手机整体的运行状态详情,也可以通过指定App包名选择App Stats标签查看单个的运行状态详情

详细数据

导致电量消耗过快的原因

Android系统为了尽可能的增加设备的续航,会不断的关闭各种硬件模块来节省电量。当我们的App在设备处于休眠状态下想要执行一次网络请求的时候;首先需要唤醒设备,接着会发送数据请求,然后等待服务端返回的结果,最后再经过一段时间的等待才会慢慢进入休眠状态。

  • 尽可能地避免唤醒锁或批量操作以避免频繁的唤醒设备即使屏幕没有被点亮CPU也会保持运行状态,通过AlarmManager可唤醒设备,而项目中不限制的滥用,导致系统被频繁唤醒;Android 的 Timer 类可以用来计划需要循环执行的任务,Timer 的问题是它需要用 WakeLock 让 CPU 保持唤醒状态,再加上不恰当的使用WakeLock最终没有合理释放掉,使得系统长时间无法进入休眠,势必导致高耗电,可参考(android设备休眠[http://www.cnblogs.com/kobe8/p/3819305.html]

  • CPU和网络耗电方面,主要是减少I/O操作(包括数据库操作),大量的计算;减少网络网络请求次数和数据量,将不重要的操作放在用户充电或已经连接至WiFi的时候。分析和记录之类的操作不需要实时进行。

  • 传感器方面设备屏幕亮度、颜色背景等需要考虑,但除了阅读类等应用,一般是不太考虑屏幕消耗的。更多的是对GPS的使用注意,减少无用的GPS请求和及时关闭GPS搜索。

优化建议

开发过程中可以尝试通过调整任务优先级等策略来达到降低损耗的目的,使用JobScheduler是个不错的选择:

  • 可以推迟的非面向用户的任务(如定期数据库数据更新);
  • 当充电时才希望执行的工作(如备份数据);
  • 需要访问网络或 Wi-Fi 连接的任务(如向服务器拉取配置数据);
  • 零散任务合并到一个批次去定期运行;
  • 当设备空闲时启动某些任务;
  • 只有当条件得到满足, 系统才会启动计划中的任务(充电、WIFI...);

官方建议优化的一些方法
https://developer.android.google.cn/training/monitoring-device-state/index.html

对低电耗模式和应用待机模式进行针对性优化
https://developer.android.google.cn/training/monitoring-device-state/doze-standby.html

Android 7.0新特性对电池管理进一步加强,一些新的变化可能多对我们现有的业务会造成影响需关注
https://developer.android.google.cn/about/versions/nougat/android-7.0-changes.html#perf

JobScheduler

自 Android 5.0 发布以来,JobScheduler 已成为执行后台工作的首选方式,其工作方式有利于用户。应用可以在安排作业的同时允许系统基于内存、电源和连接情况进行优化。JobSchedule的宗旨就是把一些不是特别紧急的任务放到更合适的时机批量处理。这样做有两个好处:

  • 避免频繁的唤醒硬件模块,造成不必要的电量消耗。
  • 避免在不合适的时间(例如低电量情况下、弱网络或者移动网络情况下的)执行过多的任务消耗电量;

JobScheduler的简单使用,首先自定义一个Service类,继承自JobService

public class JobSchedulerService extends JobService{
    private String TAG = JobSchedulerService.class.getSimpleName();

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        Log.d(TAG, "onStartJob:" + jobParameters.getJobId());

        if(true) {
            // JobService在主线程运行,如果我们这里需要处理比较耗时的业务逻辑需单独开启一条子线程来处理并返回true,
            // 当给定的任务完成时通过调用jobFinished(JobParameters params, boolean needsRescheduled)告知系统。

            //假设开启一个线程去下载文件
            new DownloadTask().execute(jobParameters);

            return true;

        }else {
            //如果只是在本方法内执行一些简单的逻辑话返回false就可以了
            return false;
        }
    }

    /**
     * 比如我们的服务设定的约束条件为在WIFI状态下运行,结果在任务运行的过程中WIFI断开了系统
     * 就会通过回掉onStopJob()来通知我们停止运行,正常的情况下不会回掉此方法
     *
     * @param jobParameters
     * @return
     */
    @Override
    public boolean onStopJob(JobParameters jobParameters) {
        Log.d(TAG, "onStopJob:" + jobParameters.getJobId());

        //如果需要服务在设定的约定条件再次满足时再次执行服务请返回true,反之false
        return true;
    }

    class DownloadTask extends AsyncTask<JobParameters, Object, Object> {
        JobParameters mJobParameters;

        @Override
        protected Object doInBackground(JobParameters... jobParameterses) {
            mJobParameters = jobParameterses[0];

            //比如说我们这里处理一个下载任务
            //或是处理一些比较复杂的运算逻辑
            //...

            try {
                Thread.sleep(30*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return null;
        }

        @Override
        protected void onPostExecute(Object o) {
            super.onPostExecute(o);
            //如果在onStartJob()中返回true的话,处理完成逻辑后一定要执行jobFinished()告知系统已完成,
            //如果需要重新安排服务请true,反之false
            jobFinished(mJobParameters, false);
        }
    }
}

记得在Manifest文件内配置Service <service android:name=".JobSchedulerService" android:permission="android.permission.BIND_JOB_SERVICE"/>

创建工作计划

public class MainActivity extends Activity{
    private JobScheduler mJobScheduler;
    private final int JOB_ID = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mai_layout);

        mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE );

        //通过JobInfo.Builder来设定触发服务的约束条件,最少设定一个条件
        JobInfo.Builder jobBuilder = new JobInfo.Builder(JOB_ID, new ComponentName(this, JobSchedulerService.class));

        //循环触发,设置任务每三秒定期运行一次
        jobBuilder.setPeriodic(3000);

        //单次定时触发,设置为三秒以后去触发。这是与setPeriodic(long time)不兼容的,
        // 并且如果同时使用这两个函数将会导致抛出异常。
        jobBuilder.setMinimumLatency(3000);

        //在约定的时间内设置的条件都没有被触发时三秒以后开始触发。类似于setMinimumLatency(long time),
        // 这个函数是与 setPeriodic(long time) 互相排斥的,并且如果同时使用这两个函数,将会导致抛出异常。
        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对象
        JobInfo jobInfo = jobBuilder.build();

        //设置开始安排任务,它将返回一个状态码
        //JobScheduler.RESULT_SUCCESS,成功
        //JobScheduler.RESULT_FAILURE,失败
        if (mJobScheduler.schedule(jobInfo) == JobScheduler.RESULT_FAILURE) {
            //安排任务失败
        }

        //停止指定JobId的工作服务
        mJobScheduler.cancel(JOB_ID);
        //停止全部的工作服务
        mJobScheduler.cancelAll();
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容