This is an API for scheduling various types of jobs against the framework that will be executed in your application's own process.
这是官方API对JobSchedule的解释,在你自己应用程序进程中对各种类型作业进行调度。这么说大概大家脑海里面有那么点感觉了吧,好比线程池的调度或者AlarmManager的使用等。那既然java里面或者原先android里面就已经有作业调度,为什么Google又推出JobSchedule?
官方文档在描述这个框架的时候,特意介绍了一个场景:若你不指定作业运行截止时间的话,这个作业会由JobSchedule内部队列决定在哪一个时刻运行。这什么意思呢?简单的来说就是它既可以在某一段时间后执行,又可以在某一个特殊场景下执行,比如充电、网络状态、设备空闲。这样来看线程池或者AlarmManager就不满足上述的需求了,JobSchedule既满足多任务执行的功能,同时也可以降低电池电量的消耗。
整体流程很简单,但是我发现我难以驾驭它。。。我们一步一步的来学习,希望高手能指点一二
创建项目
JobService是android5.0之后才加入的,所以compileSdkVersion一定要21及以上才行
创建Job Service
JobService继承于Service,他是JobScheduler回调的入口点,它的回调方法运行在主线程上。onStartJob(JobParameters)与onStopJob(android.app.job.JobParameters),这2个方法一定要实现。
public class MyJobService extends JobService {
@Override
public boolean onStartJob(final JobParameters params) {
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
当作业开始之后,会先执行onStartJob方法。onStartJob的返回值有所区别:
(1) false:框架认为你作业已经执行完毕了,那么下一个作业就立刻展开了
(2) true:框架将作业结束状态交给你去处理。因为我们可能会异步的通过线程等方式去执行工作,这个时间肯定不能放在主线程里面去控制,这时候需要手动调用jobFinished(JobParameters params, boolean needsReschedule)方法去告诉框架作业结束了,其中needsReschedule表示是否重复执行
这边很奇怪,我在编写过程中并没有发现区别在哪里,因为最终都是执行的,还需请高人赐教
当你使用cancel()或者cancelAll()的话会执行onStopJob方法。有个地方要注意,如果你onStartJob返回的是false的话,系统会因为认为工作已经结束而不再产生onStopJob回调
这里我就演示一下消息(Message)的收发过程,使用Messenger也是为了方便Activity与Service是用同一个Handler对象进行数据传输
private void sendMessage(int messageId, JobParameters params) {
Message message=new Message();
message.arg1=messageId;
message.what=params.getJobId();
message.obj=params.getExtras().getString(MyJobService.WORK_DURATION_KEY);
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
看看具体触发的方法,通过onStartCommand将Activity的Messenger传入,作业触发发射MSG_COLOR_START,作业完成发射MSG_COLOR_PROCESS,作业取消发射并结束MSG_COLOR_STOP
public static final String MESSENGER_INTENT_KEY="MESSENGER_INTENT_KEY";
public static final String WORK_DURATION_KEY="WORK_DURATION_KEY";
public static final int MSG_COLOR_START=1;
public static final int MSG_COLOR_PROCESS=3;
public static final int MSG_COLOR_STOP=2;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent!=null) {
messenger=intent.getParcelableExtra("MESSENGER_INTENT_KEY");
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onStartJob(final JobParameters params) {
sendMessage(MSG_COLOR_START, params);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
sendMessage(MSG_COLOR_PROCESS, params);
jobFinished(params, false);
}
}, 3000);
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
sendMessage(MSG_COLOR_STOP, params);
return false;
}
至此Service部分完成
权限声明
创建JobScheduler对象进行交互
使用Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)去创建JobScheduler对象。然后使用JobInfo.Builder去配置JobScheduler调度工作的参数
JobScheduler service= (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder=new JobInfo.Builder(10, new ComponentName(getPackageName(), MyJobService.class.getName()));
JobInfo.Builder可配置的参数比较多,我具体罗列一下
- setPeriodic 重复执行作业的时间间隔
- setExtras 为该作业添加额外参数
- setMinimumLatency 设置作业延迟执行的时间,与setPeriodic不可同时执行
- setOverrideDeadline 设置作业最大延迟执行时间
- setPersisted 设置是否在设备重启之后继续执行作业。这么我发现貌似没有效果
- setRequiredNetworkType 设置作业只有在满足指定的网络条件时才会被执行
/** 默认条件,不管是否有网络这个作业都会被执行 */
public static final int NETWORK_TYPE_NONE = 0;
/** 任意一种网络这个作业都会被执行 */
public static final int NETWORK_TYPE_ANY = 1;
/** 不是蜂窝网络( 比如在WIFI连接时 )时作业才会被执行 */
public static final int NETWORK_TYPE_UNMETERED = 2;
/** 不在漫游时作业才会被执行 */
public static final int NETWORK_TYPE_NOT_ROAMING = 3;
- setRequiresCharging 设置作业是否在设备充电时才会被执行
- setRequiresDeviceIdle 设置作业是否在设备空闲时才会被执行
最后就是运行了
int result=service.schedule(builder.build());
result返回1是成功,0是失败
整个代码是这样的
service= (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
Intent intent=new Intent(this, MyJobService.class);
Messenger messenger=new Messenger(handler);
intent.putExtra(MyJobService.MESSENGER_INTENT_KEY, messenger);
startService(intent);
final Random random=new Random();
addJob(random.nextInt(1000)+100);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
addJob(random.nextInt(1000)+100);
}}, 500);
private void addJob(int jobId) {
PersistableBundle bundle=new PersistableBundle();
bundle.putString(MyJobService.WORK_DURATION_KEY, "HELLO");
JobInfo.Builder builder=new JobInfo.Builder(jobId, new ComponentName(getPackageName(), MyJobService.class.getName()));
// builder.setPeriodic(3000);
builder.setMinimumLatency(5000);
// builder.setOverrideDeadline(OverrideDeadline);
builder.setExtras(bundle);
builder.setPersisted(true);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
int result=service.schedule(builder.build());
Log.d("MainActivity", "result:" + result);
}
注意这种写法,虽然我没有看源码,但是我想工作入队列这个肯定需要一定时间去处理,如果直接add而不添加一定的延时,那么执行的顺序就不会是依次执行这种排列规则了。并且不同的工作不同的策略也可能产生与之前不同的效果,这边我也有点乱。。。
再来看下终止的过程
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
service.cancelAll();
}}, 6000);
在第6s的时候终止全部工作,看看运行结果,很明显第5s开始执行工作,第6s产生onStopJob回调,随后就是handler完成。这里可以试着在onStopJob中将onStartJob产生的handler结束,留给读者自行完成。这里由于另外一个工作还没有进行,还在队列中,所以就被直接取消了
这里我还是建议大家,对不同的工作采取相同的策略,并且添加工作的时候带一点延迟,暂时这样才能达到预期的效果
参考文章
在Android 5.0中使用JobScheduler