JobScheduler介绍
JobScheduler获取不能直接去new一个JobScheduler对象而是通过Context来获取系统服务的形式来获取。
//获取jobScheduler 实例
jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo介绍与使用
JobInfo newJobInfo = new JobInfo.Builder(jobID,new ComponentName(appContext, MyJobService.class))
//在这里可以选择配置很多得属性
//设置不在低电量时工作
.setRequiresCharging(true)
//设置没有在无计量时
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
//设置的详细描述的那种网络你的工作需要 需要 (Build.VERSION_CODES.O =>26)
.setRequiredNetwork(new NetworkRequest.Builder().build())
//设置电量不可以太低 默认是false 如果是设置true 的时候工作会在电量不低的情况工作,
//一般会提示出低电量警告需要 (Build.VERSION_CODES.O =>26)
.setRequiresBatteryNotLow(false)
//设置工作是否被积极使用,默认是false 如果设置成true 及时在操作手机的同时也会工作
.setRequiresDeviceIdle()
//设置存储空间不能太低 需要 (Build.VERSION_CODES.O =>26)
.setRequiresStorageNotLow(true)
.build();
PersistableBundle

JobService介绍

从上图可以看出JobService就是Service的子类,同时在JobService中封装了几个需要实现的方法。
onStartJob方法
onStopJob方法
jobFinished方法
使用JobService时还需要注意在注册时需要加入权限(android.permission.BIND_JOB_SERVICE)如下
<service android:name="MyJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
...
</service>
JobScheduler使用
首先获取到JobScheduler对象,然后开始获取JobScheduler对象的工作来合并工作以便做批处理(这里面以字符串为工作内容数据为例),首先拿到之前相同JobID(JobID为int类型的自定义值随便多少都行,主要是为了根据JobID找到工作并合并)的工作代码如下:
JobInfo jobInfo = null ;
//24之后可以直接寻找jobid合并工作 24之前需要遍历
if (Build.VERSION.SDK_INT>Build.VERSION_CODES.N) {
//Android N之后可以直接获取
jobInfo = jobScheduler.getPendingJob(jobID);
}else{
//Android N之前需要先获取全部工作在循环根据JobID获取
List<JobInfo> allPendingJobs = jobScheduler.getAllPendingJobs();
for (JobInfo info : allPendingJobs) {
if (info.getId()==jobID) {
jobInfo = info ;
break;
}
}
}
通过上面的代码就可以获取之前添加的工作,如果可以找到要在执行的相同JobID工作就合并工作,如果没有找到工作就添加新的工作并给予JobID。代码如下:
//判断jobInfo是否为空
if (jobInfo!=null) {
PersistableBundle extras = jobInfo.getExtras();
String my_location = extras.getString("location_data");
//这里使用字符串(也可以用别的类型的工作,具体的业务逻辑要交给JobService去实现)拼接的&可以随便写 要和后·· 台沟通达成自定义协议
//比如 传给后台的是“北京&上海&深圳&海口这”这一类的字符串后台自己解析 这里是统计数据后提交
location += "&"+location;
//关闭当前任务后面重新提交合并后的任务
jobScheduler.cancel(jobID);
}
//PersistableBundle和Bundle用法差不多都是BaseBundle的子类详情看代码这里用法就是和bundle差不多的
PersistableBundle bundle = new PersistableBundle();
//注意与上面取值时对应的key要相同
bundle.putString("location_data",location);
//这里设置了ComponentName中的MyJobService绑定了服务
JobInfo newJobInfo = new JobInfo.Builder(jobID,new ComponentName(appContext, MyJobService.class))
//在这里可以选择配置很多得属性
//设置不在低电量时工作
.setRequiresCharging(true)
//设置没有在无计量时
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
//设置的详细描述的那种网络你的工作需要 需要 (Build.VERSION_CODES.O =>26)
.setRequiredNetwork(new NetworkRequest.Builder().build())
//设置电量不可以太低 默认是false 如果是设置true 的时候工作会在电量不低的情况工作,一般会提示出低电量警告 需要 (Build.VERSION_CODES.O =>26)
.setRequiresBatteryNotLow(false)
//设置工作是否被积极使用,默认是false 如果设置成true 及时在操作手机的同时也会工作
.setRequiresDeviceIdle()
//设置存储空间不能太低 需要 (Build.VERSION_CODES.O =>26)
.setRequiresStorageNotLow(true)
//将放置在PersistableBundle中的数据设置到JobInfo中
.setExtras(bundle)
.build();
//提交任务
jobScheduler.schedule(newJobInfo);
下面看下MyJobService中的代码。
@Override
public boolean onStartJob(JobParameters params) {
//如果返回值是false,这个方法返回时任务已经执行完毕。
//如果返回值是true,那么这个任务正要被执行,就需要开始执行任务。
//当任务执行完毕时你需要调用jobFinished(JobParameters params, boolean needsRescheduled)来通知系统
new UploadTask().execute();
return true;
}
//当系统接收到一个取消请求时
@Override
public boolean onStopJob(JobParameters params) {
//如果onStartJob返回false,那么onStopJob不会被调用
// 返回 true 则会重新计划这个job
return false;
}
class UploadTask extends AsyncTask<JobParameters,Void,Void> {
JobParameters jobParameters ;
@Override
protected Void doInBackground(JobParameters[] jobInfos) {
jobParameters = jobInfos[0];
String location = jobParameters.getExtras().getString("location_data");
OutputStream os = null ;
HttpURLConnection connection = null ;
try {
//这里随便写的网址无所谓的
connection = (HttpURLConnection) new URL("https://www.xxxxxx.com/").openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
os = connection.getOutputStream();
os.write(location.getBytes());
os.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (os!=null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(connection!=null){
connection.disconnect();
}
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
jobFinished(jobParameters,false);
}
}
代码很简单就是实现了JobService做了一些逻辑处理操作并使用AsyncTask去子线程完成任务。
那么为什么jobScheduler可以绑定服务呢,一般的系统系统服务获取的要么反射要么AIDL,所以看下jobScheduler来一探究竟。首先jobScheduler是通过Context.JOB_SCHEDULER_SERVICE来获得到的,所以在源码中应该可以发现有JobSchedulerService类。使用AS双击一般通过shift键可以全局搜索文档,在弹出窗体内输入JobSchedulerService,
随后就可以找到JobSchedulerService.java,在JobSchedulerService类的结构中可以找到JobSchedulerStub所以看到这里应该可以猜到,JobScheduler最后是通过AIDL来通信最后执行JobSchedulerStub中的代码来执行工作的,所以接下来重点查看JobSchedulerStub中的代码。

JobScheduler使用中通过JobScheduler.schedule(JobInfo)来添加任务在JobSchedulerStub中同样可以找到schedule方法,所以在JobSchedulerStub执行是最后也会执行schedule方法,顺着这个思路继续看代码,可以在schedule方法中找到最后时执行了scheduleAsPackage()方法。

在scheduleAsPackage()中结尾处可以看到如下图,在注释中可以获取到信息,大概意思是这是一个新工作,就可以立即把它放在等待名单并尝试运行它。说明maybeRunPendingJobsLocked()方法就是执行任务时的关键代码。

maybeRunPendingJobsLocked()方法中只执行了两个方法是assignJobsToContextsLocked()和reportActiveLocked()先看下reportActiveLocked()方法,方法内容就是判断是否还有任务在排队等待或是正在执行,也就是会去校验当前的工作队列的状态来设置队列中工作的状态(active),如果有工作在工作或是工作队列中存在排队等待执行的任务,那么active的状态就会被修改为true。所以reportActiveLocked()方法是修改工作队列的状态的代码。

assignJobsToContextsLocked方法中写了很多得循环和判断的代码,查阅代码时可以找到executeRunnableJob(JobStatus job)方法,通过方法名称可以猜出executeRunnableJob(JobStatus job)就是执行工作的代码。

executeRunnableJob(JobStatus job)方法中可以看见注释意思是开始工作但是要确保context上下文可以使用或者context不可以使用异常。

executeRunnableJob(JobStatus job)方法中找到:
final Intent intent = new Intent().setComponent(job.getServiceComponent());
boolean binding = mContext.bindServiceAsUser(intent, this,Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,new UserHandle(job.getUserId()));
代码中通过job.getServiceComponent()获取在创建JobInfo时传入的ComponentName对象,而在传入的ComponentName对象当中就包含了MyJobService.class信息,在执行到这段代码时将JobInfo中的ComponentName取出同时带着MyJobService放到了intent中,然后绑定了服务。

所以JobScheduler之所以能工回调到JobService中是因为在初始化创建JobInfo时传入的ComponentName中包含着集成JobService的类的信息,然后在执行工作时再通过JobInfo来获取,最后通过public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, UserHandle user)方法完成绑定Service实现工作时回调到JobService子类中进行工作。
那么JobScheduler分析就到这里了查看完整代码及更多知识点这里










