Andorid性能优化(七) 之 省电开发技巧总结

1 前言

在今天移动互联网蓬勃发展的浪潮下,再好的智能手机也能逃每天一充电的情况。而一款App的好用与否,电量的耗费也是衡量的重要指标之一。今天我们就来一起探索Android中电量的相关知识以及讲讲App进行省电优化的一些开发技巧。

2 获得手机电量信息

在开发过程中,如果想要获得手机的电量信息,可以通过Android提供了系统广播:ACTION_BATTERY_CHANGED可以在电池信息改变时获得相关的电池信息。如代码:

private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
            int health = intent.getIntExtra("health", 0);
            int iconSmall = intent.getIntExtra("Icon-small", 0);
            int level = intent.getIntExtra("level", 0);

            boolean present = intent.getBooleanExtra("present", false);
            int plugged = intent.getIntExtra("plugged", 0);
            int scale = intent.getIntExtra("scale", 0);
            int status = intent.getIntExtra("status", 0);
            String technology = intent.getStringExtra("technology");
            int temperature = intent.getIntExtra("temperature", 0);
            int voltage = intent.getIntExtra("voltage", 0);
        }
    }
};

电池信息对应变量说明如下:

变量

类型

说明

health

int

取得电池的健康状态,返回的状态类型由 android.os.BatteryManager 类定义的常量所决定,包括:

电池损坏( BATTERY_HEALTH_DEAD )

电池健康( BATTERY_HEALTH_GOOD )

扫描二维码关注公众号,回复: 10881583 查看本文章

电池过热( BATTERY_HEALTH_OVERHEAT )

电池电压过大( BATTERY_HEALTH_OVER_VOLTAGE )

未知状态( BATTERY_HEALTH_UNKOWN )

未明示故障( BATTERY_HEALTH_UNSPECIFIED_FAILURE )

iconSmall

int

取得电池对应的图标 ID

level

int

取得电池的剩余容量

present

boolean

判断当前是否存在电池

plugged

int

连接的电源插座类型,返回的状态由 android.os.BatteryManager 类定义的常量所决定,包括:

USB 电源( BATTERY_PLUGGED_USB )

交流电电源( BATTERY_PLUGGED_AC )

scale

int

取得电池的总容量值,通常为 100

status

int

取得电池的状态,返回的状态类型由 android.os.BatteryManager 类定义的常量所决定,包括:

电池充电状态( BATTERY_STATUS_CHARGING )

电池放电状态( BATTERY_STATUS_DISCHARGING )

电池满电状态( BATTERY_STATUS_FULL )

电池不充电状态( BATTERY_STATUS_NOT_CHARGING )

电池未知状态( BATTERY_STATUS_UNKNOWN )

technology

String

取得电池的类型

temperature

int

取得电池的温度,单位是摄氏度

voltage

int

取得电池的电压

3 分析电量

在Android5.0后,谷歌开源了一款用于检测电池有关信息和事件的工具——Battery Historian。该工具非常强大地进行解析bugreport.txt转换成Html的表示形式,然后可视化地展示分析相关指标耗电情况,还可以对App进行筛选给出总结性说明,帮助我们识别耗电的行为。

3.1生成bugreport.txt文件

bugreport.txt就是通过adb命令导出手机的电量信息文件,步骤可以这样:

步骤一、清除已有的耗电量数据,执行命令:adb shell dumpsys batterystats --enable full-wake-history

步骤二、设备耗电量数据重置,执行命令:adb shell dumpsys batterystats --reset

步骤三、启动手机上的App进行相关操作

步骤四、Android 7.0之前执行命令: adb bugreport > bugreport.txt,Android 7.0及之后执行命令: adb bugreport bugreport.zip 。便可导出bugreport文件到电脑中,同时也会在手机目录/data/user_de/0/com.android.shell/files/bugreports下生成txt或zip文件。

3.2安装Battery Historian

Battery Historian Github官方介绍了两种使用方式,分别是使用使用Docker和编译源码。如果你使用的是MAC系统,建议使用第一种方式,就是通过Docker来安装Battery Historian,在安装完Docker后在终端运行命令:docker run -d -p 9999:9999 bhaavan/battery-historian,然后等待下载完成即可。如:

如果你使用的是Win7系统,那么就只能使用第二种安装方法,步骤如下。

步骤一:下载和安装GO编程语言,并配置环境变量,正常情况下安装成功会自动生成环境变量,如果没有则要自己动手设置一下。

a.配置环境变量GOROOT、GOPATH以及Path:

b.检查是否安装成功:

 

步骤二:下载和安装Git,并配置环境变量。

步骤三:下载和安装Python2.7(注意:是Python2.7,不是Python3),并配置环境变量。

步骤四:下载和安装Java,并配置环境变量。

步骤五:打开C:\Program Files\Git\git-bash.exe,并执行命令:go get -d -u github.com/google/battery-historian/...

使在 %USERPROFILE%\go\src\github.com\google目录下载battery-historian工程源码:

步骤六:同样,通过命令:go get -u github.com/golang/protobuf/protoc-gen-go

使在 %USERPROFILE%\go\src\github.com\golang\目录下载protobuf工程源码:

步骤七:执行命令:cd $GOPATH/src/github.com/google/battery-historian

进入到battery-historian目录下,并执行命令:go run setup.go

该命令主要是在%USERPROFILE%\go\src\github.com\google\battery-historian\third_party目录下创建third_party文件夹,并下载 closure-compilerclosure-libraryflot-axislabels。如果因为超时不成功,可以自行进行下载后放入目录即可:

步骤八:执行命令:cd $GOPATH/src/github.com/google/battery-historian

进入到battery-historian目录下,并执行命令:go run cmd/battery-historian/battery-historian.go

至此,Battery Historian便完装完成。

3.3 分析bugreport

Battery Historian完装完成后,便可在浏览器中进行访问http://localhost:9999 (9999是默认的端口,在安装中可以指定),如下图:

上传bugreport.txt或bugreport.zip文件后便可以进行分析了,笔者在写文章时尝试进行上传bugreprot,但因为国内对谷歌访问的限制,始终不能显示提交按钮,所以并没有成功,所以只能贴一张官网上样图:

4 电量优化技巧

Android开发中,反映一个App电量的消耗主要由:CPU、位置服务、wakelock、传感器、数据传输等组件的使用情况决定。

4.1 位置服务的使用

先择合适的定位服务

Android系统为开发者提供了三种定位服务:

GPS_PROVIDER

GPS定位,利用GPS芯片通过接收全球定位系统的卫星提供的经纬度坐标信息实现位置服务,需要ACCESS_FINE_LOCATION权限。这种方式定位耗电也是最高,精准度也是最高,通常在10米以内。

NETWORK_PROVIDER

网络定位,通过移动通信的基站信号和Wifi节点的地址来大致计算出手机所在的位置,需要ACCESS_COARSE_LOCATION或者ACCESS_FINE_LOCATION权限。这种方式定位精度比GPS定位差很多,通常在几百米范围内。

PASSIVE_PROVIDER

被动定位,使用其他应用或者系统组件发起定位请求后保存下来的位置信息。这种方式是最省电的。

我们在实际开发中需要综合考虑App的具体需要在不同时机采用不同的定位服务。一般地往往在实际开发中很多App通常会选择第三方的定位SDK,像百度定信、高德定位,因为这些SDK无论在定位时间还是定位精度还是耗电量方面都做出专门的优化,还是非常靠谱的。

合理注册和注销定位监听

一般地我们会在Activoity的onResume和onPause两个生命周期回调中做注册和注销定位服务的监听。如代码:

public void onResume(){
    super.onResume();
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 6000, 100, locationListener);
}

public void onPause() {
    super.onPause();
    locationManager.removeUpdates(locationListener);
}

Android SDK提供了requestLocationUpdates方法进行位置更新监听的注册,请注意方法的第二和第三个参数,分别是指位置更新通知的最小时间间隔毫秒数和最小单位为米的距离。我们需要根据具体的业务需求设置一个合适的更新频率值,通常需要在定位精度和耗电量之间综合考虑。

在获取到定位之后或者程序处于后台时,需要及时注销位置监听,因为长时间的监听位置更新会耗费大量的电量。

4.2 慎用WakeLock

Android系统中为了节省电量,在设置里设定了会在用户无操作一段时间之后进入休眠状态。但有时候,我们需要使手机一直处于一种唤醒的状态,例如一些播放器的App,在播放视频时就算不操作屏幕也不需要进入休眠状态,从而保证良好的用户体验。这时就需要使用WakeLock。WakeLock它是一种锁机制,只要拿着该锁,系统就无法进入休眠,同时也可使App更加的耗电。

WakeLock的锁类型有很多种,不同的类型对cpu、屏幕和键盘的影响不相,具体情况如下:

PARTIAL_WAKE_LOCK                     保持CPU正常运转,但屏幕和键盘灯可能是关闭的。

SCREEN_DIM_WAKE_LOCK             保持CPU正常运转,允许屏幕点亮但可能是置灰的,键盘灯可能是关闭的。

SCREEN_BRIGHT_WAKE_LOCK        保持CPU正常运转,允许屏幕高亮度显示,键盘灯可能是关闭的。

FULL_WAKE_LOCK                          保持CPU正常运转,允许屏幕高亮度显示,键盘灯也保持亮度。

ACQUIRE_CAUSES_WAKEUP           强制屏幕和键盘灯亮起,这种锁针对一些必须通知用户的操作。

ON_AFTER_RELEASE                       当WakeLock被释放后,继续保持屏幕和键盘灯开启一定时间。

WakeLock的使用如下:

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

    PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
    mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, this.getClass().getName());
}

@Override
protected void onResume() {
    super.onResume();
    mWakeLock.acquire(60 * 1000);
}

@Override
protected void onPause() {
    super.onPause();
    mWakeLock.release();
}

在使用WakeLock时,建议在申请时传入超时参数,防止由于忘记或者异常情况下没有释放锁,还要切记在任务结束之后及时释放锁,如果不是会导至屏幕一直显示很长一段时间从而耗费了手机的电量。

4.3 传感器合理使用

Android提供了对设备传感器的支持,只要设备的硬件有支持这些传感器,App就可以通过传感器来获取设备的外界条件,包括手机的运行状态、当前摆放的方向等。

传感器几个常用的类型有:

Sensor.TYPE_ORIENTATION              方向传感器

Sensor.TYPE_ACCELEROMETER        重力传感器

Sensor.TYPE_LIGHT                          光线传感器

Sensor.TYPE_MAGNETIC_FIELD         磁场传感器

传感器频率总共分为4等,分别是:

SensorManager.SENSOR_DELAY_FASTEST       最快,延迟最小。

SensorManager.SENSOR_DELAY_GAME          适合游戏的频率。

SensorManager.SENSOR_DELAY_NORMAL     正常频率。

SensorManager.SENSOR_DELAY_UI                适合普通用户界面UI变化的频率。

Android为我们提供了这几个采样率的参数,方便我们使用。但对于选择那种采样率而言,并不是越快越好,要参照实际开发的应用的情况来说,越高的采样率类型就会越费耗电量。

传感器的使用如下:

private SensorManager mSensorManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
}
@Override
protected void onResume() {
    super.onResume();
    mSensorManager.registerListener(mSensorEventListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_GAME);
}
@Override
protected void onPause() {
    super.onPause();
    mSensorManager.unregisterListener(mSensorEventListener);
}

在使用传感器时,一定要切记在任务结束之后或者处理后台时要及时注销传感器的监听。

4.4 数据传输技巧

Android中的数据传输方式有很多种,如常见的:蓝牙、Wifi、蜂窝数据。无论哪一种传输方式,为了更好地延长电池的使用时间,我们在使用过程中都需要重点关注几点:

1 处于后台时根据具体业务需求,严格限制App处于后台时是否需要继续真的需要数据传输,尽量能够避免无效的数据传输。

2 数据传输的频率问题,要确定好数据传输的频率,避免冗余重复的数据传输,同时一定要避免轮询情况。

3 数据传输中要进行压缩数据大小,合并网络请求。

4 失效重试机制,要注意重试是在网络正常的情况下才去重试,否则除了没有重试成功外,而且还增加了消耗电量。

更多的网络数据传输技巧,请见《Andorid性能优化(八) 之 网络请求优化》

4.5 反注册后台BroadcastReceiver

减少应用损耗的电量,那么就要尽量避免无用的操作代码的执行。例如广播的使用中,如果App退到后台,一切界面刷新都是没有意义的而且会浪费内存和电量,所以通常的做法是在Activity的onPause方法回调时根据具体业务需要选择是否应该反注册广播。

4.6 AlarmManager使用上注意

AlarmManager是一个系统级别的服务,它可以在特定的时刻广播一个指定的Intent。AlarmManager常用的方法有:

set                       设置一次性的闹钟操作。

setRepeating        设置重复性的闹钟操作。事实上,seatRepeating方法并不靠谱。根据资料:在API 19(即Kitkat)之后,这一方法将不再准确地保证每次工作都在你设置的时间开始。原来,操作系统为了节能省电,将会调整alarm唤醒的时间。故通过AlarmManager.setRepeating()方法将不再保证你定义的工作能按时开始。

AlarmManager的唤醒操作也是比较耗电的,通常情况下需要保证两次唤醒操作的时间间隔不要太短,在不需要使用唤醒功能的情况下应尽早取消它,否则会一直处于耗电状态。

4.7善用JobScheduler

Android5.0后提供了作业调度器JobScheduler。它的作用是让系统在某个时刻某个特定条件下批处理一些APP的任务请求,而且这项任务的执行是在你自己的应用程序进程中。它的具体使用可见前面文章《Android里JobScheduler的实现》 。它可以对任务进行排期,例如设备在充电或者空闲的时候才执行一些特定任务从而达到优化耗电的任务安排方式的效果。

 

 

 

 

 

 

 

发布了106 篇原创文章 · 获赞 37 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/lyz_zyx/article/details/85842950
今日推荐