性能优化 (六) 老板问你我们 APP 耗电量,看完这篇文章不只能知道还能作出对应优化。

性能优化系列

APP 启动优化java

UI 绘制优化python

内存优化android

图片压缩git

长图优化github

电量优化golang

Dex 加解密docker

动态替换 Applicationshell

APP 稳定性之热修复原理探索浏览器

APP 持续运行之进程保活实现性能优化

ProGuard 对代码和资源压缩

APK 极限压缩

电量优化 - battery-historian

简介

如今基本上都是人手一部智能手机,你能够发现无论走在街上,公交地铁上,等等任何娱乐办公地方,随处可见有人正在低着头玩手机,有的还随身携带充电宝。因而可知,如今智能手机的电量有多么的不经用,固然咱们是优化不了电池的,不过咱们能够从 APP 中着手优化,我相信一线大厂也有本身电量分析工具,我相信只要是可以分析电量的工具,都是好工具 (^▽^)。那么该咱们的主角上场了 Google 开源电量分析工具 battery-historian 下面就让咱们一块儿来了解下 BatteryHistonian 吧!

Battery-Historian

Battery Historian 背景

Battery Historian 是在运行 Android 5.0 Lollipop(API级别21)及更高版本的 Android 设备上检查电池相关信息和事件的工具,而设备未插入。它容许应用程序开发人员在时间线上可视化系统和应用程序级事件经过平移和缩放功能,能够轻松查看自设备上次彻底充电以来的各类汇总统计信息,并选择一个应用程序并检查影响所选应用程序特定电池的指标。 它还容许对两个错误报告进行A / B比较,突出显示关键电池相关指标的差别

Battery Historian 安装

Battery Historian 安装是一个复杂的工程,须要配置一大堆的环境。不过官方给我们提供了 2 中安装方式,下面让咱们一块儿来了解下吧。

快速安装 - 百度云

battery historian 源码及 JS 环境pan.baidu.com/s/1D3Guq0WW… 提取码:g1cx

使用方式:

  1. 解压在 GO 的 $GOPATH/目录 执行 5.2 -> 5.4 步骤
  2. IE 浏览器 或者 Google 浏览器输入 http://localhost:9999/
源码安装 (推荐)

源码安装虽然配置的环境不少,可是用起来仍是比较稳定,下面就跟个人步伐来一块儿安装吧。

  1. 安装配置 GO 语言

    1. 科学上网下载 或者 使用我下载好的 提取码:nh8p

    2. 安装 注意更改路径

    3. 配置环境

      系统变量中新建 GOROOT GOPATH 变量,而后配置 Path 环境变量

    4. 检查是否安装成功

      在 cmd 命令行中输入 go version 查看是否成功安装及当前版本

  2. python 安装

    因为 historian.py 脚本是 python2 写的,因此须要安装 python2.7 环境。

    下载安装: www.python.org/

    云盘提供安装: pan.baidu.com/s/103GXARgc… 提取码:sm8p

    1. 官网下载 2.7 版本

    2. 安装 py

    3. 配置环境

    4. 检验 root 权限输入 python -V

      显示版本号,就说明安装成功了

  3. 配置 JAVA 环境

    这个就本身百度配置了哈。

  4. 配置 Git 环境

    这个也本身百度吧,很简单。

  5. 下载 Battery Historian 源码

    1. 在 Git Bash 中输入命令 go get -d -u github.com/google/battery-historian/...(即下载到GOPATH配置目录下)

    2. 进入到$GOPATH/src/github.com/google/battery-historian目录下

    3. 运行 Battery Historian:输入命令行 go run setup.go

    4. 运行 Battery Historian.go

      在 battery-historian 目录下执行 go run cmd/battery-historian/battery-historian.go

    5. 浏览器输入

      http://localhost:9999

Docker 安装

docker 我这里就粗略的说下,由于在 win10 下安装 坑太多了。还有点不稳定,致使最后使用 源码安装

准备工做

官网下载 Docker : docs.docker.com/engine/inst…

win 10 家庭版本缺乏 Hyper-V 组件需升级企业版密码钥匙也能够百度: NPPR9-FWDCX-D2C8J-H872K-2YT43

  1. 安装 Docker for Windows Installer 安装步骤直接下一步,安装过程当中会出现自动重启电脑。

  2. 命令输入运行 docker

    docker run --name=battery -d -p 9999:9999 bhaavan/battery-historian
    复制代码

  3. 验证: 在浏览器上输入 http://localhost:9999

注意:

  1. 若是 win10 企业版安装失败,能够看看官网提示

  2. 若是升级了企业版 那么 VMware 虚拟机用不了,下面给出解决办法。

    1. 关闭 Hyper-V 组件

      bcdedit /set hypervisorlaunchtype off
      复制代码
    2. 开启

      bcdedit /set hypervisorlaunchtype auto
      复制代码

Battery Historian 使用及数据分析

adb 对手机的操做
  1. 重置内部数据,至关于清空

    adb shell dumpsys batterystats --reset
    复制代码
  2. 获取完整的 wakelock 信息

    adb shell dumpsys batterystats --enable full-wake-history
    复制代码
  3. 拔掉 USB 等待一段时间建议长一点,如今随意使用 APP

  4. 得到电量报告

    // > 6.0
    adb bugreport bugreport.zip
    // <= 6.0
    adb bugreport > bugreport.txt
    复制代码
  5. 提交电量报告,并查看

数据参数详细说明
  1. WakeLock 级别

    • PARTIAL_WAKE_LOCK: 保证 CPU 保持高性能运行,而屏幕和键盘背光(也多是触摸按键的背光)关闭。通常状况下都会使用这个 WakeLock 。

    • ACQUIRE_CAUSES_WAKEUP: 这个WakeLock除了会使 CPU 高性能运行外还会致使屏幕亮起,即便屏幕原先处于关闭的状态下。

    • ON_AFTER_RELEASE: 若是释放 WakeLock 的时候屏幕处于亮着的状态,则在释放WakeLock 以后让屏幕再保持亮一小会。若是释放 WakeLock 的时候屏幕自己就没亮,则不会有动做。

    • API17 被弃用的 WakeLock:保持屏幕长亮

      • SCREEN_DIM_WAKE_LOCK:保证屏幕亮起,可是亮度可能比较低。同时键盘背光也能够不亮。
        
        SCREEN_BRIGHT_WAKE_LOCK :保证屏幕全亮,同时键盘背光也亮。
        
        FULL_WAKE_LOCK:表现和SCREEN_BRIGHT_WAKE_LOCK 相似,可是区别在于这个等级的WakeLock使用的是最高亮度
        复制代码
    • 推荐使用

      • //在Activity中:
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        
        //或者在布局中添加这个属性:
        android:keepScreenOn="true"
        复制代码

    剩下参数由表格说明

    参数 说明
    CPU runing cpu 运行的状态, 是否被唤醒
    Kernel only uptime 只有内核运行时间
    Activity Manager Proc 活跃的用户进程
    Mobile network type 网络类型
    Mobile radio active 移动蜂窝信号 比 wifi 耗电
    Crashes(logcat) Crashes(logcat) 某个时间点出现 crash 的应用
    Doze 是否进入doze 模式
    JobScheduler 异步做业调度,延迟操做,父类 Service
    SyncManager 同步操做
    Temp White List 电量优化白名单
    Phone call 是否打电话
    GPS 是否使用 GPS
    Network connectivity 网络链接状态 (wifi、mobile是否链接)
    Mobile signal strength 移动信号强度 (great\good\moderate\poor)
    Wifi scan 是否在扫描 wifi 信号
    Wifi supplicant 是否有 wifi 请求
    Wifi radio 是否正在经过 wifi 传输数据
    Wifi running wifi 组件是否在工做(未传输数据)
    Wifi on wifi 组件是否在工做(未传输数据)
    Audio 音频是否开启
    Camera 相机是否在工做
    Video 是否在播放视频
    Foreground process 前台进程
    Package install 是否在进行包安装
    Package active 包管理在工做
    Battery level 电池当前电量
    Temperature 电池温度
    Logcat misc 是否在导出日志
    Plugged 是不是充电状态
当前 APP Stats

分析当前 APP 电量详细信息
  1. 从上图能够看出 早上 11:00 - 下午 12: 00 这一个小时内电量降低的最快,耗电量最多的罪魁祸首就是 GPSwakelock
  2. 在 APP Stats -> Device estimated power 中能够查看在这一小时耗电时常,(由上图 Battery Level 得知电量变化过程是 69 - 61 ,1h 降低 8 )APP 1h 耗电为 5.38 % 仍是挺耗电的。

当前 APP 优化建议:

GPS :GPS 能够间断获取,或者使用第三方(高德、百度)它们提供省电模式定位,有 wifi 地方切换 wifi 定位,少用 高精准定位模式。

网络链接: 有 wifi 地方建议切换 wifi

wakelock : 尽可能不要使用。

总结-优化方案

1. 加入电量优化白名单

/** * 加入电量白名单 * * @param activity * @param type 1: 启动 设置页面 2:触发系统对话框 */
    public static void addWhite(Activity activity, int type) {
        if (activity == null)return;
        WeakReference<Activity> activityWeakReference = new WeakReference<Activity>(activity);
        PowerManager packageManager = (PowerManager) activityWeakReference.get().getApplication()
                .getSystemService(Context.POWER_SERVICE);
        //应用是否在 白名单中
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!packageManager.isIgnoringBatteryOptimizations(activityWeakReference.get().getApplication().getPackageName())) {
                if (type == 1) {
                    //方法一、启动一个 ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS Intent
                    Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
                    activityWeakReference.get().getApplication().startActivity(intent);
                } else {
                    //方法二、触发系统对话框
                    Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                    intent.setData(Uri.parse("package:" + activityWeakReference.get().getApplication().getPackageName()));
                    activityWeakReference.get().getApplication().startActivity(intent);
                }
            }
        }
    }
复制代码
2. GPS 优化建议

定位是 App 中经常使用的功能,可是定位不能千篇一概,不一样的场景以及不一样类型的 App 对定位更加须要个性化的区分。

  • 选择合适的 Location Provider Android系统支持多个 Location Provider:

    GPS_PROVIDER: GPS 定位,利用 GPS 芯片经过卫星得到本身的位置信息。定位精准度高,通常在 10 米左右,耗电量大;可是在室内,GPS 定位基本没用。

    NETWORK_PROVIDER: 网络定位,利用手机基站和 WIFI 节点的地址来大体定位位置,这种定位方式取决于服务器,即取决于将基站或 WIFI 节点信息翻译成位置信息的服务器的能力。

    PASSIVE_PROVIDER: 被动定位,就是用现成的,当其余应用使用定位更新了定位信息,系统会保存下来,该应用接收到消息后直接读取就能够了。好比若是系统中已经安装了百度地图,高德地图(室内能够实现精肯定位),你只要使用它们定位事后,再使用这种方法在你的程序确定是能够拿到比较精确的定位信息。

    使用 Criteria,设置合适的模式、功耗、海拔、速度等需求,系统会返回合适的 Location Provider。 例如你的 App 只是须要一个粗略的定位那么就不须要使用 GPS 进行定位,既耗费电量,定位的耗时也久。

  • 及时注销定位监听 在获取到定位以后或者程序处于后台时,注销定位监听,此时监听GPS传感器至关于执行no-op(无操做指令),用户不会有感知可是却耗电。

    //若是对定位要求不那么严格的话 能够在关闭屏幕的时候能够暂停 
    public void onPause() {
         super.onPause();
         if(locationManager != null)
             locationManager.removeListener()
     }
    
    复制代码
  • 多模块使用定位尽可能复用 多个模块使用定位,尽可能复用上一次的结果,而不是都从新走定位的过程,节省电量损耗;例如:在应用启动的时候获取一次定位,保存结果,以后再用到定位的地方都直接去取。

3.网络切换建议 上传下载尽可能用 WIFI
//优化方案 二 :网络数据切换
        if (!BatteryUtils.isPlugged(getApplicationContext())){
            //若是没有充电,提醒用户是否有可用 wifi 
        }
复制代码
4. WakeLock 亮屏,唤醒 CPU 建议
  1. 亮屏替换者

    //在Activity中: 
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    
    //或在布局中添加这个属性:
    android:keepScreenOn="true"
    复制代码
  2. alarm 闹钟让 CPU 间断式工做

    /** * 开启一个闹钟 * @param context * @param action * @param requestId * @param interval */
        public static void startTimer(Context context,String action,int requestId,int interval) {
            Intent intent = new Intent(action);
            PendingIntent sender = PendingIntent.getBroadcast(context, requestId, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    
            AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    
            Calendar calendar = Calendar.getInstance();
            int second = calendar.get(Calendar.SECOND);
            //延迟一分钟执行
            int delay = 60 - second + 1;
            calendar.add(Calendar.SECOND, delay);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                String format = new SimpleDateFormat("HH:mm:ss", Locale.CHINA).format(calendar.getTimeInMillis());
                Log.d("下次闹钟执行的时间--》","delay: " + delay +", startMillis: " +format);
                alarmManager.setWindow(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 100, sender);
    
            } else {
                alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), interval, sender);
            }
        }
    复制代码
5. JobScheduler (Service 替代者。 8.0 之后 Google 推荐使用)
  1. 把工做任务放到合适的时间再去执行,好比充电时间,wifi 链接后

  2. 也能够把多个任务合并到一块儿,再选择时间去执行

    //需求 如今我须要在充电的状态下而且链接上 wifi 在上传 gps 数据
    //jobScheduler 会自动把数据添加到一个队列中
    //存入数据
        @Override
        public int enqueue(JobInfo job, JobWorkItem work) {
            try {
                return mBinder.enqueue(job, work);
            } catch (RemoteException e) {
                return JobScheduler.RESULT_FAILURE;
            }
        }
    //在适当的时候取出全部数据
        @Override
        public List<JobInfo> getAllPendingJobs() {
            try {
                return mBinder.getAllPendingJobs();
            } catch (RemoteException e) {
                return null;
            }
        }
    复制代码

    也许有的对这个 jobSchedler 合并任务执行还不是那么清晰,如今看下一个(5 .3 )的录屏

  3. 在充电而且链接 wifi 的状态下发送数据(这里旋转屏幕是为了发送数据用的)。

    • 先说一下演示流程 链接 wifi + 充电 正常发送数据

    • 关闭 wifi 发送数据,在打开 wifi 合并发送数据

到这里 我们电量优化讲的差很少了 最后实际优化了 2% 下去。也仍是不错的了。

代码传送阵