1潮剪、JobScheduler的使用
1.1 簡介
JobScheduler主要用于在未來某個時間下滿足一定條件時觸發(fā)執(zhí)行某項任務的情況涣楷,涉及的條件可以是網絡、電量抗碰、時間等狮斗,例如執(zhí)行特定的網絡、是否只在充電時執(zhí)行任務等弧蝇。
1.2 相關API
1.2.1 JobScheduler
JobScheduler類負責將應用需要執(zhí)行的任務發(fā)送給框架碳褒,以備對該應用Job的調度,是一個系統(tǒng)服務看疗,可以通過如下方式獲壬尘:
JobScheduler mJobScheduler = (JobScheduler) Context.getSystemService(Context.JOB_SCHEDULER_SERVICE).
1.2.2 JobInfo 及 JobInfo.Builder
JobInfo是傳遞給JobScheduler類的數據容器,它封裝了針對調用應用程序調度任務所需的各種約束两芳,也可以認為一個JobInfo對象對應一個任務摔寨,JobInfo對象通過JobInfo.Builder創(chuàng)建。它將作為參數傳遞給JobScheduler:
mJobScheduler.scheduler(mJobInfo);
JobInfo.Builder是JobInfo的一個內部類怖辆,用來創(chuàng)建JobInfo的Builder類是复。
JobInfo.Builder mBuilder = new JobInfo.Builder(id,new ComponentName(this, MyJobService.class));
mJobInfo = mBuilder.build();
1.2.3 JobService
JobService是JobScheduler最終回調的端點,JobScheduler將會回調該類中的onStartJob()開始執(zhí)行異步任務疗隶。它是一個繼承于JobService的抽象類佑笋,做為系統(tǒng)回調執(zhí)行任務內容的終端,JobScheduler框架將通過bindService()方式來啟動該服務斑鼻。因此蒋纬,用戶必須在應用程序中創(chuàng)建一個JobService的子類,并實現(xiàn)其onStartJob()等回調方法坚弱,以及在AndroidManifest.xml中對它授予如下權限:
<service android:name=".MyJobService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
1.3 使用流程
1.3.1 創(chuàng)建一個JobService的子類蜀备,作為系統(tǒng)回調終端:
public class MyJobService extends JobService {
@Override
public boolean onStartJob(final JobParameters params) {
//todo 執(zhí)行任務
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;//返回false表示停止后不再重試執(zhí)行
}
}
注意在AndroidManifest.xml中添加權限
<service android:name=".MyJobService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
當任務開始時會執(zhí)行onStartJob(JobParameters params)方法,如果返回值是false荒叶,則系統(tǒng)認為這個方法返回時碾阁,任務已經執(zhí)行完畢。如果返回值是true些楣,那么系統(tǒng)認為這個任務正要被執(zhí)行脂凶,執(zhí)行任務的重擔就落在了你的肩上。當任務執(zhí)行完畢時你需要調用jobFinished(JobParameters params, boolean needsRescheduled)來通知系統(tǒng)愁茁。
當系統(tǒng)接收到一個取消請求時蚕钦,系統(tǒng)會調用onStopJob(JobParameters params)方法取消正在等待執(zhí)行的任務。很重要的一點是如果onStartJob(JobParameters params)返回false鹅很,那么系統(tǒng)假定在接收到一個取消請求時已經沒有正在運行的任務嘶居。換句話說,onStopJob(JobParameters params)在這種情況下不會被調用促煮。
需要注意的是這個Job Service運行在主線程邮屁,這意味著你需要使用子線程但壮,handler兴喂,或者一個異步任務來運行耗時的操作以防止阻塞主線程。
1.3.2 創(chuàng)建JobInfo.Builder對象苦始,為Job設置約束條件
private ComponentName mServiceComponent;
//根據JobService創(chuàng)建一個ComponentName對象
mServiceComponent = new ComponentName(this, MyJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent);
builder.setMinimumLatency(1000);//設置延遲調度時間
builder.setOverrideDeadline(2000);//設置該Job截至時間博杖,在截至時間前肯定會執(zhí)行該Job
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);//設置所需網絡類型
builder.setRequiresDeviceIdle(true);//設置在DeviceIdle時執(zhí)行Job
builder.setRequiresCharging(true);//設置在充電時執(zhí)行Job
builder.setExtras(extras);//設置一個額外的附加項
//...
1.3.3 獲取JobScheduler實例
JobScheduler mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
1.3.4 開始調度Job
mJobScheduler.schedule(builder.build());//調度Job
mJobScheduler.cancel(jobId);//取消特定Job
mJobScheduler.cancelAll();//取消應用所有的Job
具體的示例
Google官方的Sample:https://github.com/googlearchive/android-JobScheduler
2才沧、JobScheduler的原理
2.1 JobScheduler如何執(zhí)行
JobScheduler是一個抽象類样刷,它在系統(tǒng)框架的實現(xiàn)類是android.app.JobSchedulerImpl
public class JobSchedulerImpl extends JobScheduler {
IJobScheduler mBinder;
JobSchedulerImpl(IJobScheduler binder) {
mBinder = binder;
}
@Override
public int schedule(JobInfo job) {
try {
return mBinder.schedule(job);
} catch (RemoteException e) {
return JobScheduler.RESULT_FAILURE;
}
}
@Override
public void cancel(int jobId) {
try {
mBinder.cancel(jobId);
} catch (RemoteException e) {}
}
@Override
public void cancelAll() {
try {
mBinder.cancelAll();
} catch (RemoteException e) {}
}
@Override
public List<JobInfo> getAllPendingJobs() {
try {
return mBinder.getAllPendingJobs();
} catch (RemoteException e) {
return null;
}
}
}
執(zhí)行的入口是JobScheduler.scheduler败去,其實是調了JobSchedulerImpl中的schedule方法;然后再調了mBinder.schedule(job)盗飒。這個mBinder就是JobSchedulerService,通過Binder跨進程調用JobSchedulerService陋桂。
最后調用到JobSchedulerService中的schedule方法:
public int schedule(JobInfo job, int uId) {
JobStatus jobStatus = new JobStatus(job, uId);
cancelJob(uId, job.getId());
startTrackingJob(jobStatus);
//通過handler發(fā)消息
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
return JobScheduler.RESULT_SUCCESS;
}
接著發(fā)送MSG_CHECK_JOB消息逆趣,消息處理的地方是
private class JobHandler extends Handler {
...
@Override
public void handleMessage(Message message) {
...
switch (message.what) {
...
case MSG_CHECK_JOB:
synchronized (mJobs) {
//主要是遍歷將來要處理的工作任務,然后一個個加到待處理工作任務集合中去
maybeQueueReadyJobsForExecutionLockedH();
}
break;
}
maybeRunPendingJobsH();
}
...
}
接著執(zhí)行JobHandler中的maybeRunPendingJobsH
方法,處理相應的任務
private void maybeRunPendingJobsH() {
synchronized (mJobs) {
...
if (!availableContext.executeRunnableJob(nextPending)) {
...
}
...
}
availableContext是JobServiceContext嗜历,即ServiceConnection宣渗,這個是進程間通訊ServiceConnection,通過調用availableContext.executeRunnableJob(nextPending)方法梨州,會觸發(fā)調用onServiceConnected痕囱,看到這里應該明白了,onServiceConnected方法中的service就是Jobservice暴匠,里面還用了WakeLock鎖鞍恢,防止手機休眠。
public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
...
final PowerManager pm =
(PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, runningJob.getTag());
mWakeLock.setWorkSource(new WorkSource(runningJob.getUid()));
mWakeLock.setReferenceCounted(false);
mWakeLock.acquire();
mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
}
}
接著每窖,通過Handler發(fā)消息帮掉,調用了handleServiceBoundH()方法。
/** Start the job on the service. */
private void handleServiceBoundH() {
...
try {
mVerb = VERB_STARTING;
scheduleOpTimeOut();
service.startJob(mParams);
} catch (RemoteException e) {
...
}
}
從上面源碼可以看出窒典,最終是觸發(fā)調用了JobService中的startJob方法蟆炊。
2.2 JobInfo.Buidler的設置
builder.setMinimumLatency(1000);//設置延遲調度時間
builder.setOverrideDeadline(2000);//設置該Job截至時間,在截至時間前肯定會執(zhí)行該Job
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);//設置所需網絡類型
builder.setRequiresDeviceIdle(true);//設置在DeviceIdle時執(zhí)行Job
builder.setRequiresCharging(true);//設置在充電時執(zhí)行Job
builder.setExtras(extras);//設置一個額外的附加項
從源碼看瀑志,設置的內容應用于JobStatus
涩搓,例如網絡限制
public class JobStatus {
public boolean hasConnectivityConstraint() {
return job.getNetworkType() == JobInfo.NETWORK_TYPE_ANY;
}
public boolean hasUnmeteredConstraint() {
return job.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED;
}
}
而在JobSchedulerService類,相關的狀態(tài)控制在其構造函數里:
public JobSchedulerService(Context context) {
super(context);
// Create the controllers.
mControllers = new ArrayList<StateController>();
mControllers.add(ConnectivityController.get(this));
mControllers.add(TimeController.get(this));
mControllers.add(IdleController.get(this));
mControllers.add(BatteryController.get(this));
mControllers.add(AppIdleController.get(this));
mHandler = new JobHandler(context.getMainLooper());
mJobSchedulerStub = new JobSchedulerStub();
mJobs = JobStore.initAndGet(this);
}
例如網絡控制類ConnectivityController類
public class ConnectivityController extends StateController implements
ConnectivityManager.OnNetworkActiveListener {
//工作任務狀態(tài)集合
private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>();
//這個是手機網絡連接改變廣播劈猪,網絡發(fā)生改變昧甘,會觸發(fā)這個廣播
private final BroadcastReceiver mConnectivityChangedReceiver =
new ConnectivityChangedReceiver();
/**
* @param userId Id of the user for whom we are updating the connectivity state.
*/
private void updateTrackedJobs(int userId) {
...
if (changed) {
mStateChangedListener.onControllerStateChanged();
}
}
class ConnectivityChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) {
Slog.d(TAG, "Received connectivity event: " + intent.getAction() + " u"
+ context.getUserId());
}
final String action = intent.getAction();
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
final int networkType =
intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
ConnectivityManager.TYPE_NONE);
// Connectivity manager for THIS context - important!
final ConnectivityManager connManager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
final int userid = context.getUserId();
// This broadcast gets sent a lot, only update if the active network has changed.
if (activeNetwork == null) {
mNetworkUnmetered = false;
mNetworkConnected = false;
updateTrackedJobs(userid);
} else if (activeNetwork.getType() == networkType) {
mNetworkUnmetered = false;
mNetworkConnected = !intent.getBooleanExtra(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if (mNetworkConnected) { // No point making the call if we know there's no conn.
mNetworkUnmetered = !connManager.isActiveNetworkMetered();
}
updateTrackedJobs(userid);
}
} else {
...
}
}
}
}
當網絡發(fā)生改變時,會調用updateTrackedJobs(userid)方法岸霹,在updateTrackedJobs方法中疾层,會判斷網絡是否有改變,有改變的會調mStateChangedListener.onControllerStateChanged()方法贡避;然后調用了JobSchedulerService類中onControllerStateChanged方法:
public class JobSchedulerService extends com.android.server.SystemService
implements StateChangedListener, JobCompletedListener {
@Override
public void onControllerStateChanged() {
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
}
接著也是處理MSG_CHECK_JOB 消息痛黎,和上文一樣予弧,最終觸發(fā)調用了JobService中的startJob方法。
2.3 JobSchedulerService的啟動
JobSchedulerService是一個系統(tǒng)服務湖饱,即應該在SystemServer啟動的掖蛤。閱讀SystemServer的源碼:
public final class SystemServer {
private static final String TAG = "SystemServer";
//手機開機啟動后會走這個main方法,然后調用run方法
public static void main(String[] args) {
new SystemServer().run();
}
}
run
方法如下:
private void run() {
...
// Start services.
try {
startBootstrapServices();
startCoreServices();
startOtherServices();
} catch (Throwable ex) {
...
}
}
接著看startOtherServices()
private void startOtherServices() {
...
mSystemServiceManager.startService(JobSchedulerService.class);
...
}
因此井厌,在這里就啟動了JobSchedulerService服務蚓庭。
引用
1.android 性能優(yōu)化JobScheduler使用及源碼分析
2.Android 9.0 JobScheduler(一) JobScheduler的使用
3.Android 9.0 JobScheduler(二) JobScheduler框架結構簡述及JobSchedulerService的啟動
4.Android 9.0 JobScheduler(三) 從Job的創(chuàng)建到執(zhí)行