在应用开发中,有以下一些常见的耗电场景:
- 经常为了使一些特殊模块正常工作,而通过唤醒 CPU 去执行对应程序,但 Google 测试发现,每次唤醒 CPU,即使程序只运行 1 秒钟,但实际上消耗了大约两分钟的耗电量。
- 一些并不重要的数据,在非 Wi-Fi 环境下上报,如上传日志等。
- 在 CPU 高负荷状态下去做一些数据清理工作。
以上这些操作在应用中是有必要的,但并不紧急,可以选择在合适的环境下完成,因此Google 为了提高电池的续航能力,在 Android 5.0(API 21)提供了一个 JobScheduler 组件,只有一系列的预置条件满足时,才执行对应的操作,这样既能省电,又保证了功能的完整性。也就是说可以将不紧急的任务交给 Job Scheduler 来处理,Job Scheduler 集中处理收到的任务,选择合适的时间、合适的网络,再一起进行执行。
在以下场景可以考虑使用 Job Scheduler:
- 重要不紧急的任务,可以延迟执行,如定期数据库数据更新和数据上报。
- 耗电量较大的任务,比如充电时才希望执行的备份数据操作。
- 不紧急可以不执行的网络任务,如在 Wi-Fi 环境预加载数据。
- 可以批量执行的任务。
使用 JobScheduler 主要两个步骤,创建一个 JobScheduler 对象预设置任务执行条件以及创建 Job Service 执行具体的任务,接下来详细介绍使用方法。
- 创建 JobScheduler
创建 JobScheduler,用来初始化一个 JobScheduler 以及设置触发 JobScheduler 执行任务的条件。
1)通过 getSystemService()获取一个 JobScheduler 的对象:
2)创建一个 JobInfo,描述一个任务的执行 ID,以及触发这个任务的条件。
public class JobScheduleManager {
private Context mContext;
private JobScheduler jobScheduler;
public JobScheduleManager(Context mContext) {
this.mContext = mContext;
jobScheduler = (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
}
public boolean addJobScheduleTask(int taskId) {
JobInfo.Builder builder = new JobInfo.Builder(taskId, new ComponentName(mContext.getPackageName(), JobScheduleService.class.getName()));
switch (taskId) {
case 1:
builder.setPeriodic(1000);
break;
case 2:
builder.setPersisted(false);
break;
default:
break;
}
if (jobScheduler != null) {
return this.jobScheduler.schedule(builder.build()) > 0;
}
return false;
}
}
从代码看到,首先通过 JobInfo 的 build 方法创建一个 JobInfo,有两个参数,第一个参数 task_id 是任务 ID,这个 ID 有两个作用:
- 添加任务时,对不同的 ID 做不同的触发条件,即用 switch 代码实现。
- 在执行时需要根据任务 ID 执行具体的任务。
第二个参数 ComponentName 是具体执行 JobSchedluer 任务的服务,参数为进程包名以及服务类名。
在 switch 代码段中,针对不同的任务设置不同的触发条件,JobInfo 支持以下触发条件:
在 switch 代码段中,针对不同的任务设置不同的触发条件,JobInfo 支持以下触发条件:
- setMinimumLatency(long minLatencyMillis):设置任务的延迟执行时间(单位是 ms),需要注意的是,setMinimumLatency 与 setPeriodic(long time)方法不兼容,同时调用这两个方法会引起异常。
- setOverrideDeadline(long maxExecutionDelayMillis):设置任务最晚的延迟时间。如果到了规定的时间,其他条件还未满足,这个任务也会被启动。与 setMinimumLatency (long time)一样,setOverrideDeadline 与 setPeriodic(long time)同时调用会引发异常。
- setPersisted(boolean isPersisted):设备重启之后,任务是否还要继续执行。
- setRequiredNetworkType(int networkType):只有在满足指定的网络条件时,才会被执行。共有三种类型:
- JobInfo.NETWORK_TYPE_NONE:不管是否有网络,这个任务都会被执行(如果未设置,这个参数就是默认参数)。
- JobInfo.NETWORK_TYPE_ANY:只有在有网络的情况下,任务才可以执行,和网络类型无关。
- JobInfo.NETWORK_TYPE_UNMETERED:非运营商网络(比如在 Wi-Fi 连接时),时任务才会被执行。
- setRequiresCharging(boolean requiresCharging):只有当设备在充电时,这个任务才会被执行。
- setRequiresDeviceIdle(boolean requiresDeviceIdle):只有当用户没有在使用该设备且有一段时间没有使用时,才会启动该任务。
setRequiredNetworkType (int networkType)、setRequiresCharging (booleanrequireCharging)and setRequiresDeviceIdle(boolean requireIdle)这几个方法可能会使任务无法执行,除非调用 setOverrideDeadline(long time)设置了最大延迟时间,使任务在为满足条件的情况下也会被执行。
这样就完成添加一个任务到 JobScheduler 中。在添加一个任务时,要指定一个完成任务的 Service,JobSchedulerService,在任务条件触发后,进入JobSchedulerService 执行具体的工作。接下来实现一个 JobService,也就是 JobSchedulerService。
public class JobScheduleService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
// 具体执行的任务
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
// 取消一个任务
return false;
}
}
<service
android:name=".JobScheduleService"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
JobSchedulerService 类必须实现两个方法 onStartJob(JobParameters params)和 onStopJob(JobParameters params)。
(1)onStartJob(JobParameters params)
任务开始时,执行 onStartJob(JobParameters params)方法,系统用来触发已经被执行的 任 务 。 任 务 执 行 完 毕 , 需 要 调 用 jobFinished ( JobParameters params , booleanneedsRescheduled)来通知系统。
(2)void jobFinished(JobParameters params,boolean needsReschedule)
任务执行完后,调用 jobFinished 方法通知 JobScheduler。
(3)onStopJob(JobParameters params)
系统接收到一个取消请求时,调用 onStopJob(JobParameters params)方法取消正在等待执行的任务。如果系统在接收到一个取消请求时,实际任务队列中已经没有正在运行的任务,onStopJob(JobParameters params)不会被调用。
注意 JobService 运行在主线程,如果是耗时任务,一定要使用 ThreadHandler或者一个异步任务来运行耗时的操作,以防止阻塞主线程。