- Standby
最低的状态,没有数据连接需要传输,电量消耗最少。
总之,为了减少电量的消耗,在蜂窝移动网络下,最好做到批量执行网络请求,尽量避免频繁的间隔网络请求。
使用 Battery Historian 我们可以得到设备的电量消耗数据,如果数据中的移动蜂窝网络(Mobile Radio)电量消耗呈现下面的情况,间隔很小,又频繁断断续续的出现,说明电量消耗性能很不好:
battery bad
经过优化之后,如果呈现下面的图示,说明电量消耗的性能是良好的:
battery good
另外 WiFi 连接下,网络传输的电量消耗要比移动网络少很多,应该尽量减少移动网络下的数据传输,多在 WiFi 环境下传输数据。
battery wif
那么如何才能够把任务缓存起来,做到批量化执行呢?我们可以使用 JobScheduler 来优化。
我们可以通过下面的代码来获取手机的当前充电状态:
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = this.registerReceiver(null, filter);
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean acCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_AC);
if (acCharge) {
Log.v(LOG_TAG, “The phone is charging!”);
}
在上面的例子演示了如何立即获取到手机的充电状态,得到充电状态信息之后,我们可以有针对性的对部分代码做优化。
比如:我们可以判断只有当前手机为 AC 充电状态时 才去执行一些非常耗电的操作。
private boolean checkForPower() {
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = this.registerReceiver(null, filter);
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_USB);
boolean acCharge = (chargePlug == BatteryManager.BATTERY_PLUGGED_AC);
boolean wirelessCharge = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
wirelessCharge =
(chargePlug == BatteryManager.BATTERY_PLUGGED_WIRELESS);
}
return (usbCharge || acCharge || wirelessCharge);
}
监听充电状态变化
在清单文件中注册一个 BroadcastReceiver,通过在一个 Intent 过滤器内定义 ACTION_POWER_CONNECTED
和 ACTION_POWER_DISCONNECTED
来同时侦听这两种事件。
创建监听充电状态变化的 PowerConnectionReceiver。
public class PowerConnectionReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
String batteryStatus = “”;
switch (status) {
case BatteryManager.BATTERY_STATUS_CHARGING:
batteryStatus = “正在充电”;
break;
case BatteryManager.BATTERY_STATUS_DISCHARGING:
batteryStatus = “正在放电”;
break;
case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
batteryStatus = “未充电”;
break;
case BatteryManager.BATTERY_STATUS_FULL:
batteryStatus = “充满电”;
break;
case BatteryManager.BATTERY_STATUS_UNKNOWN:
batteryStatus = “未知道状态”;
break;
}
Toast.makeText(context, "batteryStatus = " + batteryStatus,
Toast.LENGTH_LONG).show();
int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED,
BatteryManager.BATTERY_PLUGGED_AC);
String chargePlug = “”;
switch (plugged) {
case BatteryManager.BATTERY_PLUGGED_AC:
chargePlug = “AC充电”;
break;
case BatteryManager.BATTERY_PLUGGED_USB:
chargePlug = “USB充电”;
break;
case BatteryManager.BATTERY_PLUGGED_WIRELESS:
chargePlug = “无线充电”;
break;
}
Toast.makeText(context, “chargePlug=” + chargePlug,
Toast.LENGTH_LONG).show();
}
}
最后注册 PowerConnectionReceiver,这时当充电状态发生变化时 PowerConnectionReceiver 就会收到通知。
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
this.registerReceiver(new PowerConnectionReceiver(), intentFilter);
监听电池电量变化
在清单文件中注册一个 BroadcastReceiver,通过侦听 ACTION_BATTERY_LOW
和 ACTION_BATTERY_OKAY
,每当设备电池电量不足或退出不足状态时,便会触发该接收器。
创建监听电池电量变化的 BatteryLevelReceiver。
public class BatteryLevelReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 当前剩余电量
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
// 电量最大值
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
// 电量百分比
float batteryPct = level / (float) scale;
Log.d(“BatteryLevelReceiver”, "batteryPct = " + batteryPct);
Toast.makeText(context, "batteryPct = " + batteryPct,
Toast.LENGTH_LONG).show();
}
}
最后注册 BatteryLevelReceiver,这时当电池电量发生变化时 BatteryLevelReceiver 就会收到通知。
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
this.registerReceiver(new BatteryLevelReceiver(), intentFilter);
通常,如果设备连接了交流充电器,您应该最大限度提高后台更新的频率;而如果设备是通过 USB 充电,则应降低更新频率,如果电池正在放电,则应进一步降低更新频率;在电池电量极低时停用所有后台更新。
WakeLock 是一种锁的机制,只要有应用拿着这个锁,CPU 就无法进入休眠状态,一直处于工作状态。
比如,手机屏幕在屏幕关闭的时候,有些应用依然可以唤醒屏幕提示用户消息,这里就是用到了 Wakelock 锁机制,虽然手机屏幕关闭了,但是这些应用依然在运行着。
手机耗电的问题,大部分是开发人员没有正确使用这个锁,成为「待机杀手」。
Android 手机有两个处理器,一个叫 Application Processor(AP),一个叫 Baseband Processor(BP)。
AP 是 ARM 架构的处理器,用于运行 Linux + Android 系统;BP 用于运行实时操作系统(RTOS),通讯协议栈运行于 BP 的 RTOS 之上。非通话时间,BP 的能耗基本上在 5mA 左右,而 AP 只要处于非休眠状态,能耗至少在 50mA 以上,执行图形运算时会更高。另外 LCD 工作时功耗在 100mA 左右,WiFi 也在 100mA 左右。
一般手机待机时,AP、LCD、WIFI 均进入休眠状态,这时 Android 中应用程序的代码也会停止执行。
Android 为了确保应用程序中关键代码的正确执行,提供了 Wake Lock 的 API,使得应用程序有权限通过代码阻止 AP 进入休眠状态。但如果不领会 Android 设计者的意图而滥用 Wake Lock API,为了自身程序在后台的正常工作而长时间阻止 AP 进入休眠状态,就会成为待机电池杀手。
那么 Wake Lock API 具体有啥用呢?心跳包从请求到应答,断线重连重新登陆等关键逻辑的执行过程,就需要 Wake Lock 来保护。而一旦一个关键逻辑执行成功,就应该立即释放掉 Wake Lock 了。两次心跳请求间隔 5 到 10 分钟,基本不会怎么耗电。
WakeLock 使用
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
WakeLock wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
“MyWakelockTag”);
newWakeLock(int levelAndFlags, String tag)
中 PowerManager.PARTIIAL_WAKE_LOCK
是一个标志位,标志位是用来控制获取的 WakeLock 对象的类型,主要控制 CPU 工作时屏幕是否需要亮着以及键盘灯需要亮着,标志位说明如下:
| levelAndFlags | CPU是否运行 | 屏幕是否亮着 | 键盘灯是否亮着 |
| — | — | — | — |
| PARTIAL_WAKE_LOCK | 是 | 否 | 否 |
| SCREEN_DIM_WAKE_LOCK | 是 | 低亮度 | 否 |
| SCREEN_BRIGHT_WAKE_LOCK | 是 | 高亮度 | 否 |
| FULL_WAKE_LOCK | 是 | 是 | 是 |
特殊说明:自 API 等级 17 开始,FULL_WAKE_LOCK 将被弃用。应用应使用FLAG_KEEP_SCREEN_ON。
WakeLock 类可以用来控制设备的工作状态。使用该类中的 acquire 可以使 CPU 一直处于工作的状态,如果不需要使 CPU 处于工作状态就调用 release 来关闭。
- 自动 release
如果我们调用的是 acquire(long timeout),那么就无需我们自己手动调用 release() 来释放锁,系统会帮助我们在 timeout 时间后释放。
- 手动 release
如果我们调用的是 acquire() 那么就需要我们自己手动调用 release() 来释放锁。
最后使用 WakeLock 类记得加上如下权限:
注意:在使用该类的时候,必须保证 acquire 和 release 是成对出现的。
屏幕保持常亮
当设备从休眠状态中,被应用程序唤醒一瞬间会耗电过多,我们可以保持屏幕常亮来节省电量,代码声明:
// 屏幕保持常亮
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// 一般不需要人为的去掉 FLAG_KEEP_SCREEN_ON 的 flag,
// windowManager 会管理好程序进入后台回到前台的的操作
//getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
或者,直接在布局中加上 keepScreenOn = true :
<android.support.constraint.ConstraintLayout xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”
xmlns:tools=“http://schemas.android.com/tools”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:keepScreenOn=“true”
tools:context=“com.jeanboy.app.batterysample.MainActivity”>
</android.support.constraint.ConstraintLayout>
在 API 21,Google 提供了一个新叫做 Job Scheduler API 的组件来处理这样的场景。Job Scheduler API 允许同时执行多个任务,执行某些指定的任务时不需要考虑时机控制引起的电池消耗。
使用 Job Scheduler,应用需要做的事情就是判断哪些任务是不紧急的,可以交给 Job Scheduler 来处理,Job Scheduler 集中处理收到的任务,选择合适的时间,合适的网络,再一起进行执行。
下面是使用 Job Scheduler 的一段简要示例,需要先有一个 JobService:
public class MyJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
Log.i(“MyJobService”, "Totally and completely working on job "
- params.getJobId());
// 检查网络状态
if (isNetworkConnected()) {
new SimpleDownloadTask() .execute(params);
// 返回 true,表示该工作耗时,
// 同时工作处理完成后需要调用 onStopJob 销毁(jobFinished)
return true;
} else {
Log.i(“MyJobService”, "No connection on job " + params.getJobId()
- “; sad face”);
}
// 返回 false,任务运行不需要很长时间,到 return 时已完成任务处理
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
Log.i(“MyJobService”, "Something changed, so I’m calling it on job "
- params.getJobId());
// 有且仅有 onStartJob 返回值为 true 时,才会调用 onStopJob 来销毁 job
// 返回 false 来销毁这个工作
return false;
}
private boolean isNetworkConnected() {
ConnectivityManager connectivityManager =
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
private class SimpleDownloadTask extends AsyncTask<JobParameters,
Void, String> {
protected JobParameters mJobParam;
@Override
protected String doInBackground(JobParameters… params) {
mJobParam = params[0];
try {
InputStream is = null;
int len = 50;
URL url = new URL(“https://www.google.com”);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000); // 10 sec
conn.setConnectTimeout(15000); // 15 sec
conn.setRequestMethod(“GET”);
//Starts the query
conn.connect();
int response = conn.getResponseCode();
Log.d(LOG_TAG, "The response is: " + response);
is = conn.getInputStream();
// Convert the input stream to a string
Reader reader = null;
reader = new InputStreamReader(is, “UTF-8”);
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
} catch (IOException e) {
return “Unable to retrieve web page.”;
}
}
@Override
protected void onPostExecute(String result) {
// 当任务完成时,需要调用 jobFinished() 让系统知道完成了哪项任务
jobFinished(mJobParam, false);
Log.i(“SimpleDownloadTask”, result);
}
}
}
定义了 JobService 的子类后,然后需要在 AndroidManifest.xml 中进行声明:
<service android:name=“pkgName.JobSchedulerService”
android:permission=“android.permission.BIND_JOB_SERVICE” />
最后模拟通过点击 Button 触发 N 个任务,交给 JobService 来处理:
public class FreeTheWakelockActivity extends ActionBarActivity {
public static final String LOG_TAG = “FreeTheWakelockActivity”;
TextView mWakeLockMsg;
ComponentName mServiceComponent;
《设计思想解读开源框架》
第一章、 热修复设计
-
第一节、 AOT/JIT & dexopt 与 dex2oat
-
第二节、 热修复设计之 CLASS_ISPREVERIFIED 问题
-
第三节、热修复设计之热修复原理
-
第四节、Tinker 的集成与使用(自动补丁包生成)
第二章、 插件化框架设计
-
第一节、 Class 文件与 Dex 文件的结构解读
-
第二节、 Android 资源加载机制详解
-
第三节、 四大组件调用原理
-
第四节、 so 文件加载机制
-
第五节、 Android 系统服务实现原理
第三章、 组件化框架设计
-
第一节、阿里巴巴开源路由框——ARouter 原理分析
-
第二节、APT 编译时期自动生成代码&动态类加载
-
第三节、 Java SPI 机制
-
第四节、 AOP&IOC
-
第五节、 手写组件化架构
第四章、图片加载框架
-
第一节、图片加载框架选型
-
第二节、Glide 原理分析
-
第三节、手写图片加载框架实战
第五章、网络访问框架设计
-
第一节、网络通信必备基础
-
第二节、OkHttp 源码解读
-
第三节、Retrofit 源码解析
第六章、 RXJava 响应式编程框架设计
-
第一节、链式调用
-
第二节、 扩展的观察者模式
-
第三节、事件变换设计
-
第四节、Scheduler 线程控制
第七章、 IOC 架构设计
-
第一节、 依赖注入与控制反转
-
第二节、ButterKnife 原理上篇、中篇、下篇
-
第三节、Dagger 架构设计核心解密
第八章、 Android 架构组件 Jetpack
-
第一节、 LiveData 原理
-
第二节、 Navigation 如何解决 tabLayout 问题
-
第三节、 ViewModel 如何感知 View 生命周期及内核原理
-
第四节、 Room 架构方式方法
-
第五节、 dataBinding 为什么能够支持 MVVM
-
第六节、 WorkManager 内核揭秘
-
第七节、 Lifecycles 生命周期
本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
VVDR-1715225098248)]第六章、 RXJava 响应式编程框架设计
-
第一节、链式调用
-
第二节、 扩展的观察者模式
-
第三节、事件变换设计
-
第四节、Scheduler 线程控制
[外链图片转存中…(img-Xj4Bl3Ko-1715225098248)]
第七章、 IOC 架构设计
-
第一节、 依赖注入与控制反转
-
第二节、ButterKnife 原理上篇、中篇、下篇
-
第三节、Dagger 架构设计核心解密
[外链图片转存中…(img-vA3UGdzA-1715225098249)]
第八章、 Android 架构组件 Jetpack
-
第一节、 LiveData 原理
-
第二节、 Navigation 如何解决 tabLayout 问题
-
第三节、 ViewModel 如何感知 View 生命周期及内核原理
-
第四节、 Room 架构方式方法
-
第五节、 dataBinding 为什么能够支持 MVVM
-
第六节、 WorkManager 内核揭秘
-
第七节、 Lifecycles 生命周期
[外链图片转存中…(img-Ii4XGGzK-1715225098249)]
本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
[外链图片转存中…(img-gCHUgmTS-1715225098249)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!