从 Android 6.0(API 级别 23)开始,Android 引入了两项省电功能,通过管理应用在设备未连接至电源时的行为方式,帮助用户延长电池寿命。当用户长时间未使用设备时,低电耗模式会延迟应用的后台 CPU 和网络活动,从而降低耗电量。应用待机模式会延迟用户近期未与之交互的应用的后台网络活动。
WakeLock 是一种 Android 中的机制,允许应用程序保持设备唤醒状态,以防止 CPU 和屏幕进入休眠。WakeLock 主要用于在需要时确保设备保持唤醒状态,以执行特定的任务。以下是 WakeLock 的使用方法:
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
wakeLock.acquire();
wakeLock.release();
WakeLock 类提供了不同类型的锁,以满足不同的需求和场景。在 Android 中,常见的 WakeLock 类型包括以下几种:
PowerManager.PARTIAL_WAKE_LOCK
标志创建。PowerManager.SCREEN_DIM_WAKE_LOCK
标志创建。PowerManager.SCREEN_BRIGHT_WAKE_LOCK
标志创建。PowerManager.FULL_WAKE_LOCK
标志创建。PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK
标志创建。PowerManager.PARTIAL_WAKE_LOCK
来保持设备唤醒状态AlarmManager 是 Android 中用于在指定时间触发后台操作的类。它允许你安排在未来的某个时间点或以固定间隔触发某个操作。以下是使用 AlarmManager 的基本步骤:
获取 AlarmManager 对象:
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
通过调用 getSystemService()
方法,传入 Context.ALARM_SERVICE
参数获取 AlarmManager 的实例。
创建 PendingIntent:
Intent intent = new Intent(this, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
在上述代码中,我们创建了一个 Intent 对象,指定了接收闹钟触发事件的广播接收器(MyReceiver)。然后,通过调用 PendingIntent.getBroadcast()
方法创建 PendingIntent。PendingIntent.FLAG_UPDATE_CURRENT
参数表示如果已经存在相同的 PendingIntent,那么更新它的 Extra 数据。
设置闹钟:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
} else {
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), WakeUpBroadcastReceiver.TIME_INTERVAL, pendingIntent);
}
在上述代码中,我们使用 System.currentTimeMillis()
获取当前时间,并在其基础上加上 5000 毫秒(5 秒)来计算触发时间。然后,通过调用 alarmManager.set()
方法,传入触发类型(AlarmManager.ELAPSED_REALTIME_WAKEUP
表示使用实时时钟触发,同时唤醒设备),触发时间和 PendingIntent 对象来设置闹钟。
创建广播接收器:
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 处理闹钟触发事件
Intent intent1 = new Intent(context, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
// 重复定时任务
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent);
}
}
}
AlarmManager的四个唤醒类型,它可以使用以下四个常量:
1、AlarmManager.ELAPSED_REALTIME:使用相对时间,可以通过SystemClock.elapsedRealtime()获取(从开机到现在的毫秒数,包括手机的睡眠时间),设备休眠时并不会唤醒设备。
2、AlarmManager.ELAPSED_REALTIME_WAKEUP:与ELAPSED_REALTIME基本功能一样,只是会在设备休眠时唤醒设备。
3、AlarmManager.RTC:使用绝对时间,可以通过System.currentTimeMillis()获取,设备休眠时并不会唤醒设备。
4、AlarmManager.RTC_WAKEUP:与RTC基本功能一样,只是会在设备休眠时唤醒设备。
相对时间:设备boot后到当前经历的时间,SystemClock.elapsedRealtime()获取到的是相对时间。
绝对时间:1970年1月1日到当前经历的时间,System.currentTimeMillis()和Calendar.getTimeInMillis()获取到的都是绝对时间。
如果是相对时间,那么计算triggerAtMillis就需要使用SystemClock.elapsedRealtime();
如果是绝对时间,那么计算triggerAtMillis就需要使用System.currentTimeMillis()或者calendar.getTimeInMillis()。
以上是使用 AlarmManager 在不同Android版本使用的适配,这样可以实现一个定时任务。
但是在华为平板上测试存在的问题:
1.当手机连接usb电源时,息屏不会造成定时器暂停运行,而且时间间隔准确
2.当手机未连接usb电源时,息屏会造成定时器时间间隔不准,设置为30秒的间隔可能会4分钟或者10分钟再执行,并在再次点亮屏幕时定时器又正常。
可以参考下这个Handler休眠失效
JobScheduler是在Android 5.0添加的,它可以检测网络状态、设备是否充电中、低电量、低存储等状态,当所有条件都满足时就会触发执行对应的JobService来完成任务。
创建 JobService 类:
public class MyJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
// 在这里执行后台任务
return true; // 如果任务是异步的,则返回 true;如果任务是同步的,则返回 false
}
@Override
public boolean onStopJob(JobParameters params) {
// 在这里处理任务停止的逻辑(例如重试机制)
return true; // 返回 true 以重新安排该作业,返回 false 以放弃该作业
}
}
在上述代码中,我们创建了一个继承自 JobService 的类(MyJobService),并重写了 onStartJob()
和 onStopJob()
方法。在 onStartJob()
方法中,你可以执行后台任务的逻辑。如果任务是异步的,记得返回 true,以指示系统该任务正在进行中。如果任务是同步的,则返回 false。在 onStopJob()
方法中,你可以处理任务停止的逻辑(例如重试机制)。
创建 JobInfo 对象:
JobInfo.Builder builder = new JobInfo.Builder(jobId, new ComponentName(this, MyJobService.class));
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); // 设置所需的网络类型
builder.setPersisted(true); // 设置任务持久化,即在设备重启后仍然有效
//android7.0 及以上系统
if (Build.VERSION.SDK_INT >= 24) {
builder.setMinimumLatency(3000) //至少3s 才会执行
//builder.setOverrideDeadline(3000) //3s后一定会执行
//builder.setMinimumLatency(3000)
//默认是10s,如果小于10s,也会按照10s来依次增加
//builder.setBackoffCriteria(10000, JobInfo.BACKOFF_POLICY_LINEAR);
} else {
builder.setPeriodic(5000)
}
// 设置其他参数和约束条件
JobInfo jobInfo = builder.build();
在上述代码中,我们使用 JobInfo.Builder 创建一个 JobInfo 对象。jobId
是唯一标识作业的整数值。new ComponentName(this, MyJobService.class)
指定了执行作业的 JobService 类。通过 setRequiredNetworkType()
方法,我们可以设置作业所需的网络类型(如 JobInfo.NETWORK_TYPE_ANY
表示任何网络都可以)。setPersisted(true)
设置任务持久化,即在设备重启后仍然有效。
你还可以使用其他方法设置作业的约束条件,例如 setRequiresCharging()
(要求设备在充电时才执行作业)或 setRequiresDeviceIdle()
(要求设备处于空闲状态时才执行作业)等。
如果想要周期执行的话7.0 及以上系统通过setMinimumLatency方法并在执行onStartJob重新创建job,其他则使用setPeriodic方法。
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
int result = jobScheduler.schedule(jobInfo);
if (result == JobScheduler.RESULT_SUCCESS) {
// 作业调度成功
}
在华为平板测试时发现:
1.setPeriodic最低时间间隔为15分钟,低于15分钟不起作用
2.锁屏休眠时JobScheduler 不执行
在低电耗模式和应用待机模式期间,列入白名单的应用可以使用网络并保留部分唤醒锁定。不过,列入白名单的应用仍会受到其他限制,就像其他应用一样。例如,列入白名单的应用的作业和同步会延迟(在搭载 API 级别 23 及更低级别的设备上),并且其常规 AlarmManager 闹钟不会触发。应用可以调用 isIgnoringBatteryOptimizations() 来检查它当前是否在豁免白名单中。
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
public static void isIgnoreBatteryOption(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
Intent intent = new Intent();
String packageName = context.getPackageName();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
context.startActivity(intent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
在 Android 设备上,默认情况下,当设备进入休眠状态时,网络连接会自动断开以节省电池。然而,如果你需要在设备休眠期间保持网络连接,可以尝试以下方法:
在 Android 设备上,默认情况下,当设备进入休眠状态时,网络连接会自动断开以节省电池。然而,如果你需要在设备休眠期间保持网络连接,可以尝试以下方法:
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiManager.WifiLock wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "MyWifiLockTag");
wifiLock.acquire();
public class MyBackgroundService extends Service {
private WifiManager.WifiLock wifiLock;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "MyWifiLockTag");
wifiLock.acquire();
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
if (wifiLock != null && wifiLock.isHeld()) {
wifiLock.release();
wifiLock = null;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
具体效果未测试
使用场景,需求是在设备休眠时可以继续访问网络,并维持一个与服务器的长连接。
1.通过WackLock获取PARTIAL_WAKE_LOCK锁,让应用在锁屏时CPU不休眠。
2.通过AlarmManager定时检测长连接是否断
3.避免电池优化白名单申请
4. 通过WifiLock锁定Wi-Fi 来保持设备的 Wi-Fi 连接处于活动状态
5. 去掉华为的自动管理改成手动管理,打开wlan锁屏自动断开的开关(这比较重要)
AlarmManager使用
针对低电耗模式和应用待机模式进行优化
Android休眠机制