学而不思则罔,思而不学则殆
【Android】Android定时任务 -Jobscheduler
参考
官方Demo - https://github.com/googlearchive/android-JobScheduler
JobScheduler系列1-基本用法和注意事项
个人Demo - https://github.com/aJanefish/JobSchedulerDemo
简介
Jobscheduler的android在5.0上针对于降低功耗而提出来的一种策略方案,自 Android 5.0 发布以来,JobScheduler 已成为执行后台工作的首选方式,其工作方式有利于用户。应用可以在安排作业的同时允许系统基于设备状态、电源和连接情况等具体条件进行优化。JobScheduler 可实现控制和简洁性,谷歌推出该机制是想要所有应用在执行后台任务时使用它制是想要所有应用在执行后台任务时使用它。
Demo
继承JobService
public class MyJobService extends JobService {
private static final String TAG = MyJobService.class.getSimpleName();
private Messenger mActivityMessenger;
@Override
public void onCreate() {
super.onCreate();
Constant.tag(TAG, "Service created");
}
@Override
public void onDestroy() {
super.onDestroy();
Constant.tag(TAG, "Service destroyed");
}
/**
* When the app's MainActivity is created, it starts this service. This is so that the
* activity and this service can communicate back and forth. See "setUiCallback()"
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mActivityMessenger = intent.getParcelableExtra(MESSENGER_INTENT_KEY);
return START_NOT_STICKY;
}
@Override
public boolean onStartJob(final JobParameters params) {
Constant.tag(TAG, "onStartJob: " + params.getJobId() + " " + Log.getStackTraceString(new Throwable()));
sendMessage(MSG_COLOR_START, params.getJobId());
long duration = params.getExtras().getLong(WORK_DURATION_KEY);
Constant.tag(TAG, "onStartJob duration:" + duration);
// Uses a handler to delay the execution of jobFinished().
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
sendMessage(MSG_COLOR_STOP, params.getJobId());
jobFinished(params, false);
}
}, duration);
// Return true as there's more work to be done with this job.
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
Constant.tag(TAG, "onStopJob: " + params.getJobId());
sendMessage(MSG_COLOR_STOP, params.getJobId());
return false;
}
//服务端通过mActivityMessenger给客户端发送信息
private void sendMessage(int messageID, @Nullable Object params) {
// If this service is launched by the JobScheduler, there's no callback Messenger. It
// only exists when the MainActivity calls startService() with the callback in the Intent.
if (mActivityMessenger == null) {
Constant.tag(TAG, "Service is bound, not started. There's no callback to send a message to.");
return;
}
Message m = Message.obtain();
m.what = messageID;
m.obj = params;
try {
Constant.tag(TAG, "sendMessage m.what:" + m.what + " m.ob:" + m.obj);
mActivityMessenger.send(m);
} catch (RemoteException e) {
Constant.tag(TAG, "Error passing service object back to activity.");
}
}
}
注册服务:
<service
android:name=".service.MyJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"/>
配置任务信息
JobInfo.Builder builder = new JobInfo.Builder(mJobId++, new ComponentName(this, MyJobService.class));
//String delay = mDelayEditText.getText().toString();
String delay = "1";
//设置延迟时间
if (!TextUtils.isEmpty(delay)) {
builder.setMinimumLatency(Long.parseLong(delay) * 1000);
}
//String deadline = mDeadlineEditText.getText().toString();
String deadline = "15";
//设置最后期限
if (!TextUtils.isEmpty(deadline)) {
builder.setOverrideDeadline(Long.parseLong(deadline) * 1000);
}
//设置网络类型 - 设定工作需要的基本网络描述
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
//如果你需要对网络能力进行更精确的控制
//builder.setRequiredNetwork()
//是否需要Idle - 默认false
builder.setRequiresDeviceIdle(false);
//是否需要充电 - 默认false
builder.setRequiresCharging(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//指定要运行此作业,设备的电池电量不得过低。
builder.setRequiresBatteryNotLow(false);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//指定要运行此作业,设备的可用存储空间不得过低
builder.setRequiresStorageNotLow(false);
}
//设置额外参数
PersistableBundle extras = new PersistableBundle();
//String workDuration = mDurationTimeEditText.getText().toString();
String workDuration = "";
if (TextUtils.isEmpty(workDuration)) {
workDuration = "1";
}
extras.putLong(WORK_DURATION_KEY, Long.parseLong(workDuration) * 1000);
builder.setExtras(extras);
JobInfo jobInfo = builder.build();
设备状态约束
//是否需要处于充电状态
setRequiresCharging(boolean requiresCharging)
//是否需要处于非低电量状态
setRequiresBatteryNotLow(boolean batteryNotLow)
//是否需要设备处于空闲状态
setRequiresDeviceIdle(boolean requiresDeviceIdle)
//是否需要设备剩余空间不为low
setRequiresStorageNotLow(boolean storageNotLow)
//可选参数
//NETWORK_TYPE_ANY :需要网络连接
//NETWORK_TYPE_UNMETERED : 需要不计费的网络连接
//NETWORK_TYPE_NOT_ROAMING : 需要不漫游的网络连接
//NETWORK_TYPE_METERED : 需要计费的网络连接,例如蜂窝数据网络
setRequiredNetworkType(@NetworkType int networkType)
时间约束条件
此类约束条件与任务的执行时间有关,例如设置周期执行的任务,设置任务延时执行等。我们在使用的时候需要注意如下2点。
1,周期任务setPeriodic和setMinimumLatency设置任务延时,setOverrideDeadline设置任务最大执行时间冲突,我们不应该一起设置它们,否则会抛出异常。
2,setOverrideDeadline方法比较特殊,我们为任务指定一个最晚的执行时间,该事件达到后,不管其他约束条件是否满足,任务都将会执行
//设置任务周期执行,其周期为intervalMillis参数
//你无法控制任务的执行时间,系统只保证在此时间间隔内,任务最多执行一次。
setPeriodic(long intervalMillis)
//任务将被延迟至少minLatencyMillis时间执行,和setPeriodic周期任务冲突。
setMinimumLatency(long minLatencyMillis)
//不管其他约束条件是否满足,任务最多于maxExecutionDelayMillis时间后被执行
//和setPeriodic周期任务冲突。
setOverrideDeadline(long maxExecutionDelayMillis)
监听Uri约束条件
该类约束条件可以让JobScheduler监听指定的uri,当uri发生变化时,启动任务。
//增加待监听的uri
addTriggerContentUri(@NonNull TriggerContentUri uri)
//设置uri变化时,延迟durationMs后执行任务。在此期间Uri再次变化,则重新计时
//设置该时间可以让我们过滤掉太频繁的变化,减少任务的执行次数。
setTriggerContentUpdateDelay(long durationMs)
//第一次uri变化后,我们任务可以等待的最大时间,和updateDelay配合使用。
setTriggerContentMaxDelay(long durationMs)
发布任务
JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.schedule(jobInfo);
这样就把任务交个了系统,系统会在何时的实际执行任务。
原理
职责划分
类型 | 作用 | 说明 |
---|---|---|
JobInfo | 封装的任务信息 | 通过构建者模式构建 |
JobScheduler | 根据任务配置在合适的时机触发任务 | 系统服务 |
JobService | 任务的具体执行者 | 服务,抽象类,需要继承实现业务逻辑 |
JobIntentService | 任务的具体执行者 | 服务,抽象类,需要继承实现业务逻辑,内部通过JobScheduler |
类图
整体类图如下:
主要有两个部分:
- 一是JobService,任务具体实现需要继承该服务
- 二是JobServiceEngine,任务服务引擎。内部两个重要类:JobInterface和JobHandler.
2.1 JobInterface是Binder,是服务接收的起点,把消息推送个JobHandler
2.2 JobHandler是一个Handler,主要是接收消息分发出去
时序图
JobService
private JobServiceEngine mEngine;
/** @hide */
public final IBinder onBind(Intent intent) {
if (mEngine == null) {
mEngine = new JobServiceEngine(this) {
@Override
public boolean onStartJob(JobParameters params) {
return JobService.this.onStartJob(params);
}
@Override
public boolean onStopJob(JobParameters params) {
return JobService.this.onStopJob(params);
}
};
}
return mEngine.getBinder();
}
JobService中实现了抽象类JobServiceEngine 的两个抽象方法,onStartJob和onStopJob,这两个方法是不是很熟悉,然后去调用JobService中的onStartJob和onStopJob方法。而onStartJob和onStopJob也需要我们继承JobService去继承实现。
JobInterface
static final class JobInterface extends IJobService.Stub {
final WeakReference<JobServiceEngine> mService;
JobInterface(JobServiceEngine service) {
mService = new WeakReference<>(service);
}
@Override
public void startJob(JobParameters jobParams) throws RemoteException {
JobServiceEngine service = mService.get();
if (service != null) {
Message m = Message.obtain(service.mHandler, MSG_EXECUTE_JOB, jobParams);
m.sendToTarget();
}
}
@Override
public void stopJob(JobParameters jobParams) throws RemoteException {
JobServiceEngine service = mService.get();
if (service != null) {
Message m = Message.obtain(service.mHandler, MSG_STOP_JOB, jobParams);
m.sendToTarget();
}
}
}
JobInterface 主要是接收消息后交个mHandler,也就是JobHandler。
JobHandler
class JobHandler extends Handler {
JobHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
final JobParameters params = (JobParameters) msg.obj;
switch (msg.what) {
case MSG_EXECUTE_JOB:
try {
boolean workOngoing = JobServiceEngine.this.onStartJob(params);
ackStartMessage(params, workOngoing);
} catch (Exception e) {
Log.e(TAG, "Error while executing job: " + params.getJobId());
throw new RuntimeException(e);
}
break;
case MSG_STOP_JOB:
try {
boolean ret = JobServiceEngine.this.onStopJob(params);
ackStopMessage(params, ret);
} catch (Exception e) {
Log.e(TAG, "Application unable to handle onStopJob.", e);
throw new RuntimeException(e);
}
break;
case MSG_JOB_FINISHED:
final boolean needsReschedule = (msg.arg2 == 1);
IJobCallback callback = params.getCallback();
if (callback != null) {
try {
callback.jobFinished(params.getJobId(), needsReschedule);
} catch (RemoteException e) {
Log.e(TAG, "Error reporting job finish to system: binder has gone" +
"away.");
}
} else {
Log.e(TAG, "finishJob() called for a nonexistent job id.");
}
break;
default:
Log.e(TAG, "Unrecognised message received.");
break;
}
}
private void ackStartMessage(JobParameters params, boolean workOngoing) {
final IJobCallback callback = params.getCallback();
final int jobId = params.getJobId();
if (callback != null) {
try {
callback.acknowledgeStartMessage(jobId, workOngoing);
} catch(RemoteException e) {
Log.e(TAG, "System unreachable for starting job.");
}
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Attempting to ack a job that has already been processed.");
}
}
}
private void ackStopMessage(JobParameters params, boolean reschedule) {
final IJobCallback callback = params.getCallback();
final int jobId = params.getJobId();
if (callback != null) {
try {
callback.acknowledgeStopMessage(jobId, reschedule);
} catch(RemoteException e) {
Log.e(TAG, "System unreachable for stopping job.");
}
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Attempting to ack a job that has already been processed.");
}
}
}
}
JobHandler 主要是调用了JobServiceEngine的抽象相关方法,刚在JobService中实现的方法,最终调用到JobService的方法中。
以上就是JobService的基本实现逻辑。
总结
- onStartJob和onStopJob方法是执行在主线程中的,我们不可以在其中做耗时操作,否则可能导致ANR。
- onStartJob方法在系统判定达到约束条件时被调用,我们在此处执行我们的业务逻辑。
- onStopJob方法在系统判定你需要停止你的任务时被调用,可能在你调用jobFinish停止任务之前,那么什么时候会发生该情况呢?一般为约束条件不满足的时候,例如我们设置约束条件为充电中,则我们的任务会在充电中开始执行,如果在执行过程中,我们拔下了充电线,则系统判定我们的约束条件失效了,就会回调onStopJob方法,通知我们停止任务,我们应该在此时立即停止当前正在执行的业务逻辑。
- onStartJob方法的返回false,代表我们的工作已经处理完了,系统会自动结束该任务,适用于任务在主线程中执行的情况。返回true,代表我们在子线程中执行任务,在任务执行完成后,我们需要手动调用jobFinish方法,通知系统任务已经执行完成。
- onStopJob方法返回false,代表我们直接丢弃该任务。返回true则代表,如果我们设置了重试策略,该任务将按照重试策略进行调度。