Android优化之电量优化的实现
作者:七适散人 发布时间:2023-08-20 07:57:25
Android 5.0 后用 Battery Historian 工具分析电量。
耗电因素
移动网络请求
手机通过内置的射频模块和基站联系,从而链接上网的,而这个射频模块(radio)是非常耗电的,为了控制这个射频模块的耗电,硬件驱动及 Android RIL 层做了很多处理。例如可以单独关闭 radio(飞行模式),间歇性假休眠 radio(有数据发生时才上电,保持一个频率的与基站交互)等等。如今的 App 都是移动互联网 App,不可避免的会有大量的网络请求,会导致 radio 一直处于活跃状态,从而耗电量增加。
使用移动网络传输数据,电量的消耗有以下 3 种状态:
Full power:高功率状态,移动网络连接被激活,允许设备以最大 的传输速率进行操作。
Low power:低功耗状态,对电量的消耗差不多是 Full power 状态下的 50%。
Standby:空闲态,没有数据连接需要传输,耗电最少。
从低功率到高功率大约 1.5s,从空闲态到高功率大约 2s,秒。在应用中每创建一个新的网络连接,网络(射频)模块都会转换到高功率状态(Radio Full Power),在数据传输完后再转回低功耗状态(Radio Low Power),转换的过程需要 5 秒,这 5 秒的耗电量保持在高功率状态,最后再转换空闲态需要 12 秒。因此,对于一个典型的移动网络设备,每个数据传输都会导致网络模块消耗 20 秒的电量。
WakeLock
Android 系统本身为了优化电量的使用,会在没有操作时进入休眠状态,来节省电量。当然,为了便于开发(很多应用不可避免的希望在灭屏后还能运行一些事儿,或是要保持屏幕一直亮着--比如播放视频),Android 提供了一个 PowerManager.WakeLock 的东西.
我们可以用 WakeLock 来保持 CPU 运行,或是防止屏幕变暗/关闭,让手机可以在用户不操作时依然可以做一些事儿。然而,获取 WakeLock 很容易,释放不好就会成为难题,消耗电量。例如获取了一个 WakeLock 来保持 CPU 运转,做一个复杂运算并将数据上传到后台服务器,然后释放该 WakeLock。然而这个过程可能并不像我们想象的那么快,可能因为比如服务器挂掉,计算出了异常等等导致 WakeLock 没有释放,CPU 会一直得不到休眠,而大大增加耗电。
另外,WakeLock 还有 android:keepScreenOn 属性,还可以让屏幕常量,这也是耗电大户。
private void acquireWakeLock(Context ctx) {
if (null == mWakeLock) {
PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE, "TestLocknService");
if (null != mWakeLock) {
mWakeLock. acquire();
}
}
}
PARTIAL_WAKE_LOCK:保持 CPU 正常运转,屏幕和键盘灯有可能 会关闭。
SCREEN_DIM_WAKE_LOCK:保持 CPU 运转,允许保持屏幕显示,但有可能变暗,允许关闭键盘灯。
SCREEN_BRIGHT_WAKE_LOCK:保持 CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯。
FULL_WAKE_LOCK:保持 CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度。
ACQUIRE_CAUSES_ WAKEUP:强制使屏幕亮起,这种锁主要用于一些必须通知用户的操作。
ON_AFTER_RELEASE:当锁被释放时,保持屏幕亮起一段时间。
需要注册权限
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.DEVICE_POWER"/>
GPS
应用中经常会用到定位服务,Android 提供了 Network 定位和 GPS 定位。相对来说,GPS 会精确得多,对于一些诸如跑步,导航类的应用基本会使用 GPS 定位。然而,GPS 定位也会消耗大量的电量。
AlarmManager
间隔不能太短。
优化建议
优化网络请求
在蜂窝移动网络下,最好做到批量执行网络请求,尽量避免频繁的间隔网络请求,尽量多地保持在 Radio Standby 状态。
尽量在 Wi-Fi 环境下使用数据传输。
谨慎使用 WakeLock
WakeLock 获取释放成对出现(调用 release),使用超时 WakeLock,以防出异常导致没有释放。
WakeLock 有一个接口 setReferenceCounted,用来设置 WakeLock 的计数机制,true 为计数,false 为不计数,默认是 true。所谓计数即每一个 acquire 必须对应一个 release;不计数则是无论有多少个 acquire,一个 release 就可以释放。虽然官方说默认 是计数的,但有的第三方 ROM 做了修改,使默认是不计数的。
主动设置 wakeLock.setReferenceCounted(false)。
监听手机充电状态
BatteryManager 会发送一个包含充电状态的持续广播,我们可以通过此广播获取充电状态和电量详情。因为这是一个持续广播,无需写 Receiver,可以直接通过 intent 获取相关数据。
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null,ifilter);
// 设备正在充电
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS,-1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
// 也可以监听充电状态的变化,只要设备连接或断开电源,BatteryManager 就会广播相应的操作
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED,-1);
boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;
另外页可以注册 Receiver来监听
<receiver android:name=".PowerConnectionReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
</intent-filter>
</receiver>
Doze and App Standby
Android 6.0 提供了两个用来节省电量的技术 Doze 和 App Standby。
Doze 瞌睡。如果设备闲置了一段较长时间,Doze 技术将通过延迟后台网络活动,CPU 运行等来减少电量损耗。
App Standy 应用待机。不是最近得到过用户使用的 App,App Standy 将延缓这个应用的后台网络活动。
所有 Android 6.0 及以上的设备上,Doze and App Standby 都会运行。可能会影响 App 的运行,可以根据官方文档适配。
可以在代码中调起电量优化的设计页面,让用户选择是否将应用加入白名单,以在 Doze 模式下能够做一些事情。
定位
定位中使用 GPS,及时关闭
// Remove the listener you previously added
locationManager.removeUpdates(locationListener);
计算优化
缩短代码产生指令运行的时间,进而减少某个应用程序对 CPU 时间片 的总占用时间,进而减少单位时间内该应用程序占整个系统耗电的百分比。
浮点运算比整数运算更消耗 CPU 时间片,因此耗电也会增加,在编写 代码的过程中应该尽量减少浮点运算。
除法变乘法。
充分利用移位。
查表法,直接使用映射关系,但这会增加内存占用,视情况而定。
熄屏后停止一些和 UI 效果有关的操作,比如动画。
来源:https://www.jianshu.com/p/627554db9f60


猜你喜欢
- 本文实例讲述了C#实现简单获取及设置Session类。分享给大家供大家参考。具体分析如下:这是一个简单的C#获取Session、设置Sess
- 简介今天的课程开始进入高级课程类了,我们要开始接触网络协议、设备等领域编程了。在今天的课程里我们会使用OKHttp组件来访问网络资源而不是使
- 由Lombok的@AllArgsConstructor注解引发的错误需求:在Service实现中写了一个方法调用第三方接口同步数据。 功能代
- 一、对AOP的初印象首先先给出一段比较专业的术语(来自百度):在软件业,AOP为Aspect Oriented Programming的缩写
- 前言在日常开发过程中,静态变量和 静态方法 是我们常见的用法,Java中相信大家并不陌生了,那么在 Kotlin 中该如何使用呢
- 这篇文章主要介绍了java io读取文件操作代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友
- 需要装一个插件:File - Settings- Plugins - 搜索gson 安装GsonFromat;如下两张图安装完成后 ,新建一
- springboot读取配置文件到静态工具类通常我们读取配置文件可以用@Value注解和@Configuration,@Configurat
- 一:回顾(1)c++中的string类是在面试中和笔试中经常考的题目; 工程代码免费下载 string类的自行实现(2)c++中的strin
- 事件:定义了事件成员的类允许通知其他其他对象发生了特定的事情。具体的说,定义了事件成员的类能提供以下功能1.方法能登记它对事件的关注2.方法
- Spring Security和Shiro的区别相同点1、认证功能2、授权功能3、加密功能4、会话管理5、缓存支持6、rememberMe功
- 背景环境已学习java基础,html,css,js,jquery,bootstrap,layui,maven,servlet和jsp,刚进入
- 业务现象代码中有一部分代码多次嵌套循环和数据处理,执行速度很慢解决方案通过多线程1、启用多线程private final static Ex
- 单例:Singleton,是指仅仅被实例化一次的类。饿汉单例设计模式一、饿汉设计模式public class SingletonHungry
- 这篇文档主要关注下配置修改后对应的 Java 对象是如何更新,并不关注整体的配置改动流程所有代码都来自 apollo-client 项目更新
- 本文主要介绍了关于单例模式的一些问题,想学习C#单例模式的同学们可以看一看,还是有些帮助c#中的单例模式单例模式是指在设计一个类时,保证在运
- 基于opencv的车道线检测,供大家参考,具体内容如下原理:算法基本思想说明:传统的车道线检测,多数是基于霍夫直线检测,其实这个里面有个很大
- 双向链表(Doubly linked list)什么是双向链表?双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直
- 在spring中有很多以XXXAware命名的接口,很多人也不清楚这些接口都是做什么用的,这篇文章将描述常用的一些接口。一,Applicat
- 前言:由于项目需求,短信验证码的接口需要换成阿里大于的,但是尴尬的发现阿里大于的jar包没有maven版本的,于是便开始了一上午的 * 引包之