Android?JobScheduler詳細(xì)介紹
Android JobScheduler介紹
介紹
JobScheduler是在Android 5.0添加的,它可以檢測(cè)網(wǎng)絡(luò)狀態(tài)、設(shè)備是否充電中、低電量、低存儲(chǔ)等狀態(tài),當(dāng)所有條件都滿足時(shí)就會(huì)觸發(fā)執(zhí)行對(duì)應(yīng)的JobService來(lái)完成任務(wù)。同時(shí)具備了重試、定時(shí)執(zhí)行、持久化任務(wù)(設(shè)備重啟后可恢復(fù)任務(wù))等功能??芍^是十分強(qiáng)大
JobScheduler的使用大致分為三步:
- 創(chuàng)建JobService類
- 創(chuàng)建一個(gè)JobInfo
- 獲取JobScheduler系統(tǒng)服務(wù)執(zhí)行任務(wù)
1 創(chuàng)建JobService
JobScheduler的原理大致是是通過(guò)監(jiān)聽(tīng)手機(jī)狀態(tài),當(dāng)條件滿足時(shí)啟動(dòng)Service執(zhí)行任務(wù)。
因此在使用JobScheduler之前,我們需要定義一個(gè)Service,當(dāng)然并不是隨隨便便定義,而是需要派生自JobService。
只需要重寫(xiě)onStopJob和onStartJob即可。
class DemoJobService: JobService() {
override fun onStartJob(params: JobParameters?): Boolean {
//do sth...
//這個(gè)返回值是有講究的
//true表示Service的工作在一個(gè)獨(dú)立線程中執(zhí)行,工作完成之后需要調(diào)用jobFinish方法通知JobScheduler工作完成
//false表示Service的工作已經(jīng)完成,JobScheduler收到通知之后會(huì)釋放資源
return false
}
//該方法會(huì)在一些極端場(chǎng)景下觸發(fā)
//比如當(dāng)前的Job需要有Wifi連接的場(chǎng)景下才可執(zhí)行,但是在執(zhí)行期間
//用戶關(guān)閉Wifi,那么就會(huì)觸發(fā)該方法
override fun onStopJob(params: JobParameters?): Boolean {
//do sth...
//true表示需要進(jìn)行重試
//false表示不再進(jìn)行重試,Job將會(huì)被丟棄
return true
}
}同時(shí),在manifest中注冊(cè)時(shí),還需要設(shè)置一個(gè)權(quán)限(否則會(huì)報(bào)錯(cuò)),如下
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
//...
<service android:name=".DemoJobService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
</application>2 創(chuàng)建JobInfo
JobInfo是對(duì)任務(wù)的描述,比如說(shuō)需要監(jiān)聽(tīng)哪些狀態(tài)、重試策略、任務(wù)執(zhí)行時(shí)間、是否持久化等等。
JobInfo.Builder的構(gòu)造函數(shù)需要傳入一個(gè)jobId,是Job的唯一標(biāo)志,后續(xù)通過(guò)該jobId來(lái)取消Job。
通過(guò)Builder模式構(gòu)造JobInfo。
val jobInfo = JobInfo.Builder(1, ComponentName(packageName, DemoJobService::class.java.name))
.setBackoffCriteria(1000,JobInfo.BACKOFF_POLICY_LINEAR) //重試機(jī)制
.setMinimumLatency(1000)//設(shè)置延遲時(shí)間
.setOverrideDeadline(10000)//設(shè)置最后期限,如果達(dá)到該時(shí)間點(diǎn),Job還沒(méi)被執(zhí)行,那么會(huì)強(qiáng)制執(zhí)行一次
// .setPeriodic(2000)//每隔2s執(zhí)行一次,跟上面兩個(gè)會(huì)沖突
.setPersisted(true)//持久化,就算手機(jī)關(guān)機(jī),啟動(dòng)之后也可以恢復(fù)Job,需要RECEIVE_BOOT_COMPLETED權(quán)限
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//設(shè)置Job依賴的網(wǎng)絡(luò)類型
.setRequiresCharging(false)//是否要求設(shè)備處于充電狀態(tài)
.setRequiresDeviceIdle(false)//是否要求設(shè)備處于空閑狀態(tài)
.build()JobInfo其他的設(shè)置方法:
.setMinimumLatency(5000)//5秒 最小延時(shí)、
.setOverrideDeadline(60000)//maximum最多執(zhí)行時(shí)間
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//免費(fèi)的網(wǎng)絡(luò)---wifi 藍(lán)牙 USB
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)//任意網(wǎng)絡(luò)---
/**
設(shè)置重試/退避策略,當(dāng)一個(gè)任務(wù)調(diào)度失敗的時(shí)候執(zhí)行什么樣的測(cè)量采取重試。
initialBackoffMillis:第一次嘗試重試的等待時(shí)間間隔ms
*backoffPolicy:對(duì)應(yīng)的退避策略。比如等待的間隔呈指數(shù)增長(zhǎng)。
*/
.setBackoffCriteria(long initialBackoffMillis, int backoffPolicy)
.setBackoffCriteria(JobInfo.MAX_BACKOFF_DELAY_MILLIS, JobInfo.BACKOFF_POLICY_LINEAR)
//.setPeriodic (long intervalMillis)//設(shè)置執(zhí)行周期,每隔一段時(shí)間間隔任務(wù)最多可以執(zhí)行一次。
//.setPeriodic(long intervalMillis,long flexMillis)//在周期執(zhí)行的末端有一個(gè)flexMiliis長(zhǎng)度的窗口期,任務(wù)就可以在這個(gè)窗口期執(zhí)行。
//設(shè)置設(shè)備重啟后,這個(gè)任務(wù)是否還要保留。需要權(quán)限: RECEIVE_BOOT_COMPLETED //ctrl+shift+y/u x
//.setPersisted(boolean isPersisted);
// .setRequiresCharging(boolean )//是否需要充電
// .setRequiresDeviceIdle(boolean)//是否需要等設(shè)備出于空閑狀態(tài)的時(shí)候
// .addTriggerContentUri(uri)//監(jiān)聽(tīng)uri對(duì)應(yīng)的數(shù)據(jù)發(fā)生改變,就會(huì)觸發(fā)任務(wù)的執(zhí)行。
// .setTriggerContentMaxDelay(long duration)//設(shè)置Content發(fā)生變化一直到任務(wù)被執(zhí)行中間的最大延遲時(shí)間
//設(shè)置Content發(fā)生變化一直到任務(wù)被執(zhí)行中間的延遲。如果在這個(gè)延遲時(shí)間內(nèi)content發(fā)生了改變,延遲時(shí)間會(huì)重寫(xiě)計(jì)算。
// .setTriggerContentUpdateDelay(long durationMilimms)3 執(zhí)行任務(wù)
最后通過(guò)getSystemService獲取JobScheduler服務(wù)執(zhí)行任務(wù)就可以了
//執(zhí)行任務(wù) val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler jobScheduler.schedule(jobInfo) //取消任務(wù) jobScheduler.cancel(1)
4 最后
JobScheduler上手還是比較簡(jiǎn)單的,由于本文是基于api23的源碼,因此還有很多新功能沒(méi)有在源碼中展示出來(lái),如setRequiresBatteryNotLow(設(shè)置Job對(duì)電量的要求)、setRequiresStorageNotLow(設(shè)置Job對(duì)存儲(chǔ)空間的要求)等.
特性&適用
- 支持在一個(gè)任務(wù)上組合多個(gè)條件
- 內(nèi)置條件:設(shè)備待機(jī)、設(shè)備充電和連接網(wǎng)絡(luò)
- 支持持續(xù)的job,這意味著設(shè)備重啟后,之前被中斷的job可以繼續(xù)執(zhí)行
- 支持設(shè)置job的最后執(zhí)行期限
- 根據(jù)你的配置,可以設(shè)置job在后臺(tái)運(yùn)行還是在主線程中運(yùn)行
適用
需要在Android設(shè)備滿足某種場(chǎng)合才需要去執(zhí)行處理數(shù)據(jù):
- 應(yīng)用具有可以推遲的非面向用戶的工作(定期數(shù)據(jù)庫(kù)數(shù)據(jù)更新)
- 應(yīng)用具有當(dāng)插入設(shè)備時(shí)希望優(yōu)先執(zhí)行的工作(充電時(shí)才希望執(zhí)行的工作備份數(shù)據(jù))
- 需要訪問(wèn)網(wǎng)絡(luò)或 Wi-Fi 連接時(shí)需要進(jìn)行的任務(wù)(如向服務(wù)器拉取內(nèi)置數(shù)據(jù))
- 希望作為一個(gè)批次定期運(yùn)行的許多任務(wù)(s)
特征
- Job Scheduler只有在Api21或以上的系統(tǒng)支持。
- Job Scheduler是將多個(gè)任務(wù)打包在一個(gè)場(chǎng)景下執(zhí)行。
- 在系統(tǒng)重啟以后,任務(wù)會(huì)依然保留在Job Scheduler當(dāng)中,因此不需要監(jiān)聽(tīng)系統(tǒng)啟動(dòng)狀態(tài)重復(fù)設(shè)定。
- 如果在一定期限內(nèi)還沒(méi)有滿足特定執(zhí)行所需情況,Job Scheduler會(huì)將這些任務(wù)加入隊(duì)列,并且隨后會(huì)進(jìn)行執(zhí)行
使用Job Scheduler,應(yīng)用需要做的事情就是判斷哪些任務(wù)是不緊急的,可以交給Job Scheduler來(lái)處理,Job Scheduler集中處理收到的任務(wù),選擇合適的時(shí)間,合適的網(wǎng)絡(luò),再一起進(jìn)行執(zhí)行。把時(shí)效性不強(qiáng)的工作丟給它做。
源碼分析
JobScheduler是系統(tǒng)服務(wù)JobSchedulerService,JobScheduler是一個(gè)抽象類;以下是JobScheduler的源碼:
public abstract class JobScheduler {
public static final int RESULT_FAILURE = 0;
public static final int RESULT_SUCCESS = 1;
//這個(gè)是提交一個(gè)工作任務(wù)JobInfo
public abstract int schedule(JobInfo job);
public abstract int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag);
//這個(gè)是取消一個(gè)工作任務(wù)
public abstract void cancel(int jobId);
//這個(gè)是取消所有的工作任務(wù)
public abstract void cancelAll();
//得到所有將來(lái)要執(zhí)行的工作任務(wù)
public abstract @NonNull List<JobInfo> getAllPendingJobs();
//根據(jù)Jobid獲得將來(lái)要執(zhí)行的工作任務(wù)
public abstract @Nullable JobInfo getPendingJob(int jobId);
}JobScheduler的實(shí)現(xiàn)類是JobSchedulerImpl:
public class JobSchedulerImpl extends JobScheduler {
IJobScheduler mBinder;
/* package */ JobSchedulerImpl(IJobScheduler binder) {
mBinder = binder;
}
@Override
public int schedule(JobInfo job) {
try {
//這個(gè)mBinder是JobSchedulerService中的IJobScheduler.Stub,
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;
}
}
}在代碼中 調(diào)用mJobScheduler.schedule(job);其實(shí)是調(diào)了JobScheduler的實(shí)現(xiàn)類JobSchedulerImpl中的schedule方法;然后再調(diào)了mBinder.schedule(job);這個(gè)mBinder就是JobSchedulerService,調(diào)用了JobSchedulerService類里面IJobScheduler.Stub內(nèi)部類的schedule方法; JobSchedulerService是在哪里啟動(dòng)的呢?先看一下的源碼,從源碼分析JobSchedulerService是一個(gè)系統(tǒng)服務(wù);
public class JobSchedulerService extends com.android.server.SystemService
implements StateChangedListener, JobCompletedListener {
static final boolean DEBUG = false;
/** The number of concurrent jobs we run at one time. */
private static final int MAX_JOB_CONTEXTS_COUNT
= ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
static final String TAG = "JobSchedulerService";
/** Master list of jobs. */
final JobStore mJobs;
static final int MSG_JOB_EXPIRED = 0;
static final int MSG_CHECK_JOB = 1;
//....省略了n多代碼
}是系統(tǒng)服務(wù)那應(yīng)該就是在 SystemServer啟動(dòng)的,先看一下SystemServer的源碼;
public final class SystemServer {
private static final String TAG = "SystemServer";
//手機(jī)開(kāi)機(jī)啟動(dòng)后會(huì)走這個(gè)main方法,然后調(diào)用run方法
public static void main(String[] args) {
new SystemServer().run();
}手機(jī)開(kāi)機(jī)啟動(dòng)會(huì)走SystemServer中的主函數(shù)main方法,main方法調(diào)用了run方法,下面是run方法中的代碼:
private void run() {
//....省略了n多代碼
// Start services.
//這里啟動(dòng)一些系統(tǒng)服務(wù)
try {
startBootstrapServices();
startCoreServices();
//會(huì)走這個(gè)
startOtherServices();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
}
//....省略了n多代碼
}從run方法可以看出,這里啟動(dòng)了一系列的系統(tǒng)服務(wù),里面調(diào)用了startOtherServices()方法,那接下看一下startOtherServices方法,向下看:
private void startOtherServices() {
//....省略了n多代碼
mSystemServiceManager.startService(TwilightService.class);
//這里就是啟動(dòng)JobSchedulerService
mSystemServiceManager.startService(JobSchedulerService.class);
if (!disableNonCoreServices) {
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
}
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
}
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
}
}
//....省略了n多代碼
}在startOtherServices方法方法中調(diào)用了mSystemServiceManager.startService(JobSchedulerService.class);這里就啟動(dòng)了JobSchedulerService服務(wù);接下來(lái)我們分析JobSchedulerService的源碼; 先看一下JobSchedulerService的構(gòu)造方法:
public JobSchedulerService(Context context) {
super(context);
// Create the controllers.
mControllers = new ArrayList<StateController>();
//以下控制類都是繼承了StateController類
//網(wǎng)絡(luò)聯(lián)接控制類
mControllers.add(ConnectivityController.get(this));
//時(shí)間控制類
mControllers.add(TimeController.get(this));
//延時(shí)控制類
mControllers.add(IdleController.get(this));
//電量控制類
mControllers.add(BatteryController.get(this));
mControllers.add(AppIdleController.get(this));
mHandler = new JobHandler(context.getMainLooper());
//new了IJobScheduler.Stub,這個(gè)其實(shí)就是前面JobSchedulerImpl類中mBinder,這里用到了進(jìn)程間通信binder機(jī)制,不明白的可能先學(xué)習(xí)一下進(jìn)程間通信機(jī)制;
mJobSchedulerStub = new JobSchedulerStub();
mJobs = JobStore.initAndGet(this);
}創(chuàng)建了5個(gè)不同的StateController,分別添加到mControllers。
| 類型 | 說(shuō)明 |
|---|---|
| ConnectivityController | 注冊(cè)監(jiān)聽(tīng)網(wǎng)絡(luò)連接狀態(tài)的廣播 |
| TimeController | 注冊(cè)監(jiān)聽(tīng)job時(shí)間到期的廣播 |
| IdleController | 注冊(cè)監(jiān)聽(tīng)屏幕亮/滅,dream進(jìn)入/退出,狀態(tài)改變的廣播 |
| BatteryController | 注冊(cè)監(jiān)聽(tīng)電池是否充電,電量狀態(tài)的廣播 |
| AppIdleController | 監(jiān)聽(tīng)app是否空閑 |
前面提到調(diào)用mBinder.schedule(job);其實(shí)是調(diào)用了JobSchedulerService類里面IJobScheduler.Stub內(nèi)部類的schedule方法;接下看一下這個(gè)方法:
final class JobSchedulerStub extends IJobScheduler.Stub {
//....省略了n多代碼
@Override
public int schedule(JobInfo job) throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "Scheduling job: " + job.toString());
}
//這里做了一個(gè)檢驗(yàn),非關(guān)鍵代碼
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
//....省略了n多代碼
long ident = Binder.clearCallingIdentity();
try {
//這個(gè)是關(guān)鍵代碼,調(diào)用JobSchedulerService中的schedule
return JobSchedulerService.this.schedule(job, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}從上面代碼看出,是調(diào)用了調(diào)用JobSchedulerService中的schedule方法,好了,看一下JobSchedulerService中的schedule方法;
public int schedule(JobInfo job, int uId) {
JobStatus jobStatus = new JobStatus(job, uId);
cancelJob(uId, job.getId());
startTrackingJob(jobStatus);
//通過(guò)handler發(fā)消息
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
return JobScheduler.RESULT_SUCCESS;
}從上面代碼可以看出是通過(guò)Handler發(fā)消息,MSG_CHECK_JOB是目標(biāo)源,看一下JobHandler 中的方法
private class JobHandler extends Handler {
public JobHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message message) {
synchronized (mJobs) {
if (!mReadyToRock) {
return;
}
}
switch (message.what) {
case MSG_JOB_EXPIRED:
synchronized (mJobs) {
JobStatus runNow = (JobStatus) message.obj;
if (runNow != null && !mPendingJobs.contains(runNow)
&& mJobs.containsJob(runNow)) {
mPendingJobs.add(runNow);
}
queueReadyJobsForExecutionLockedH();
}
break;
case MSG_CHECK_JOB:
synchronized (mJobs) {
maybeQueueReadyJobsForExecutionLockedH();
}
break;
}
maybeRunPendingJobsH();
removeMessages(MSG_CHECK_JOB);
}通過(guò)Handler發(fā)消息,然后調(diào)用 maybeQueueReadyJobsForExecutionLockedH()方法,
private void queueReadyJobsForExecutionLockedH() {
ArraySet<JobStatus> jobs = mJobs.getJobs();
if (DEBUG) {
Slog.d(TAG, "queuing all ready jobs for execution:");
}
//遍歷要處理的目標(biāo)任務(wù),把目標(biāo)任務(wù)加到集合PendingJobs中
for (int i=0; i<jobs.size(); i++) {
JobStatus job = jobs.valueAt(i);
if (isReadyToBeExecutedLocked(job)) {
if (DEBUG) {
Slog.d(TAG, " queued " + job.toShortString());
}
//把目標(biāo)任務(wù)加到待處理目標(biāo)任務(wù)集合中
mPendingJobs.add(job);
} else if (isReadyToBeCancelledLocked(job)) {
stopJobOnServiceContextLocked(job);
}
}
if (DEBUG) {
final int queuedJobs = mPendingJobs.size();
if (queuedJobs == 0) {
Slog.d(TAG, "No jobs pending.");
} else {
Slog.d(TAG, queuedJobs + " jobs queued.");
}
}
}這個(gè)方法主要是遍歷將來(lái)要處理的工作任務(wù)然后一個(gè)個(gè)加到待處理工作任務(wù)集合中去;這個(gè)方法執(zhí)行完后就會(huì)執(zhí)行JobHandler中的maybeRunPendingJobsH()方法;
private void maybeRunPendingJobsH() {
synchronized (mJobs) {
if (mDeviceIdleMode) {
// If device is idle, we will not schedule jobs to run.
return;
}
Iterator<JobStatus> it = mPendingJobs.iterator();
if (DEBUG) {
Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
}
//通過(guò)遍歷待處理任務(wù)集合,一個(gè)個(gè)處理待處理任務(wù)
while (it.hasNext()) {
JobStatus nextPending = it.next();
JobServiceContext availableContext = null;
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
final JobStatus running = jsc.getRunningJob();
if (running != null && running.matches(nextPending.getUid(),
nextPending.getJobId())) {
// Already running this job for this uId, skip.
availableContext = null;
break;
}
if (jsc.isAvailable()) {
availableContext = jsc;
}
}
if (availableContext != null) {
if (DEBUG) {
Slog.d(TAG, "About to run job "
+ nextPending.getJob().getService().toString());
}
//這個(gè)方法就是處理待處理任務(wù)的方法
if (!availableContext.executeRunnableJob(nextPending)) {
if (DEBUG) {
Slog.d(TAG, "Error executing " + nextPending);
}
mJobs.remove(nextPending);
}
it.remove();
}
}
}
}
}從上面源碼分析可以得出,這個(gè)方法通過(guò)遍歷待處理任務(wù)集合,處理任務(wù),這里調(diào)用了availableContext.executeRunnableJob(nextPending)方法,這個(gè)就是處理待處理任務(wù)的方法,接下來(lái)我們一起看看這個(gè)方法的源碼,分析下:
public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (!name.equals(mRunningJob.getServiceComponent())) {
mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
return;
}
this.service = IJobService.Stub.asInterface(service);
final PowerManager pm =
(PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mRunningJob.getTag());
//這里也用WakeLock鎖,防止手機(jī)休眠
mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid()));
mWakeLock.setReferenceCounted(false);
mWakeLock.acquire();
mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
}
}JobServiceContext是ServiceConnection,這個(gè)是進(jìn)程間通訊ServiceConnection,通過(guò)調(diào)用availableContext.executeRunnableJob(nextPending)方法,會(huì)觸發(fā)調(diào)用onServiceConnected,看到這里應(yīng)該明白了,onServiceConnected方法中的service就是jobservice,里面還用了WakeLock鎖,防止手機(jī)休眠,然后通過(guò)Handler發(fā)消息 調(diào)用了handleServiceBoundH()方法,
/** Start the job on the service. */
private void handleServiceBoundH() {
if (DEBUG) {
Slog.d(TAG, "MSG_SERVICE_BOUND for " + mRunningJob.toShortString());
}
if (mVerb != VERB_BINDING) {
Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
+ VERB_STRINGS[mVerb]);
closeAndCleanupJobH(false /* reschedule */);
return;
}
if (mCancelled.get()) {
if (DEBUG) {
Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
+ mRunningJob);
}
closeAndCleanupJobH(true /* reschedule */);
return;
}
try {
mVerb = VERB_STARTING;
scheduleOpTimeOut();
//我們就是要找這個(gè)方法, 看到這里明白了吧,這個(gè)就是調(diào)用了jobService中的startjob
service.startJob(mParams);
} catch (RemoteException e) {
Slog.e(TAG, "Error sending onStart message to '" +
mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
}
}從上面源碼可以看出,最終是調(diào)用了jobService中的startjob方法, 這樣就明白了,是如何觸發(fā)調(diào)用jobService中的startjob方法的;
前面在JobSchedulerService中提到了控件類StateController類,這個(gè)是一個(gè)抽象類,有很多實(shí)現(xiàn)類,這個(gè)我只分析一個(gè)ConnectivityController實(shí)現(xiàn)類,其他都差不多,接下來(lái)分析一下ConnectivityController源碼:
public class ConnectivityController extends StateController implements
ConnectivityManager.OnNetworkActiveListener {
private static final String TAG = "JobScheduler.Conn";
//工作任務(wù)狀態(tài)集合
private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>();
//這個(gè)是手機(jī)網(wǎng)絡(luò)連接改變廣播,網(wǎng)絡(luò)發(fā)生改變,會(huì)觸發(fā)這個(gè)廣播
private final BroadcastReceiver mConnectivityChangedReceiver =
new ConnectivityChangedReceiver();
/** Singleton. */
private static ConnectivityController mSingleton;
private static Object sCreationLock = new Object();
/** Track whether the latest active network is metered. */
private boolean mNetworkUnmetered;
/** Track whether the latest active network is connected. */
private boolean mNetworkConnected;
public static ConnectivityController get(JobSchedulerService jms) {
synchronized (sCreationLock) {
if (mSingleton == null) {
mSingleton = new ConnectivityController(jms, jms.getContext());
}
return mSingleton;
}
}
private ConnectivityController(StateChangedListener stateChangedListener, Context context) {
super(stateChangedListener, context);
// Register connectivity changed BR.
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.registerReceiverAsUser(
mConnectivityChangedReceiver, UserHandle.ALL, intentFilter, null, null);
ConnectivityService cs =
(ConnectivityService)ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
if (cs != null) {
if (cs.getActiveNetworkInfo() != null) {
mNetworkConnected = cs.getActiveNetworkInfo().isConnected();
}
mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered();
}
}
@Override
public void maybeStartTrackingJob(JobStatus jobStatus) {
if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
synchronized (mTrackedJobs) {
jobStatus.connectivityConstraintSatisfied.set(mNetworkConnected);
jobStatus.unmeteredConstraintSatisfied.set(mNetworkUnmetered);
mTrackedJobs.add(jobStatus);
}
}
}
@Override
public void maybeStopTrackingJob(JobStatus jobStatus) {
if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
synchronized (mTrackedJobs) {
mTrackedJobs.remove(jobStatus);
}
}
}
/**
* @param userId Id of the user for whom we are updating the connectivity state.
*/
private void updateTrackedJobs(int userId) {
synchronized (mTrackedJobs) {
boolean changed = false;
for (JobStatus js : mTrackedJobs) {
if (js.getUserId() != userId) {
continue;
}
boolean prevIsConnected =
js.connectivityConstraintSatisfied.getAndSet(mNetworkConnected);
boolean prevIsMetered = js.unmeteredConstraintSatisfied.getAndSet(mNetworkUnmetered);
if (prevIsConnected != mNetworkConnected || prevIsMetered != mNetworkUnmetered) {
changed = true;
}
}
if (changed) {
mStateChangedListener.onControllerStateChanged();
}
}
}
/**
* We know the network has just come up. We want to run any jobs that are ready.
*/
public synchronized void onNetworkActive() {
synchronized (mTrackedJobs) {
for (JobStatus js : mTrackedJobs) {
if (js.isReady()) {
if (DEBUG) {
Slog.d(TAG, "Running " + js + " due to network activity.");
}
mStateChangedListener.onRunJobNow(js);
}
}
}
}
class ConnectivityChangedReceiver extends BroadcastReceiver {
/**
* We'll receive connectivity changes for each user here, which we process independently.
* We are only interested in the active network here. We're only interested in the active
* network, b/c the end result of this will be for apps to try to hit the network.
* @param context The Context in which the receiver is running.
* @param intent The Intent being received.
*/
// TODO: Test whether this will be called twice for each user.
@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) {
//網(wǎng)絡(luò)未聯(lián)接
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();
}
//更新工作任務(wù)
updateTrackedJobs(userid);
}
} else {
if (DEBUG) {
Slog.d(TAG, "Unrecognised action in intent: " + action);
}
}
}
};
@Override
public void dumpControllerState(PrintWriter pw) {
pw.println("Conn.");
pw.println("connected: " + mNetworkConnected + " unmetered: " + mNetworkUnmetered);
for (JobStatus js: mTrackedJobs) {
pw.println(String.valueOf(js.hashCode()).substring(0, 3) + ".."
+ ": C=" + js.hasConnectivityConstraint()
+ ", UM=" + js.hasUnmeteredConstraint());
}
}
}上面是網(wǎng)絡(luò)聯(lián)接控制類ConnectivityController,當(dāng)網(wǎng)絡(luò)發(fā)生改變時(shí),會(huì)觸發(fā)網(wǎng)絡(luò)連接改變廣播,然后調(diào)用updateTrackedJobs(userid)方法,在updateTrackedJobs方法中,會(huì)判斷網(wǎng)絡(luò)是否有改變,有改變的會(huì)調(diào) mStateChangedListener.onControllerStateChanged()方法;這樣又調(diào)用了JobSchedulerService類中onControllerStateChanged方法:
@Override
public void onControllerStateChanged() {
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}在onControllerStateChanged方法中通過(guò)handler發(fā)消息,然后調(diào)用了maybeQueueReadyJobsForExecutionLockedH();
private void maybeQueueReadyJobsForExecutionLockedH() {
int chargingCount = 0;
int idleCount = 0;
int backoffCount = 0;
int connectivityCount = 0;
List<JobStatus> runnableJobs = new ArrayList<JobStatus>();
ArraySet<JobStatus> jobs = mJobs.getJobs();
for (int i=0; i<jobs.size(); i++) {
JobStatus job = jobs.valueAt(i);
if (isReadyToBeExecutedLocked(job)) {
if (job.getNumFailures() > 0) {
backoffCount++;
}
if (job.hasIdleConstraint()) {
idleCount++;
}
if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) {
connectivityCount++;
}
if (job.hasChargingConstraint()) {
chargingCount++;
}
runnableJobs.add(job);
} else if (isReadyToBeCancelledLocked(job)) {
stopJobOnServiceContextLocked(job);
}
}
if (backoffCount > 0 ||
idleCount >= MIN_IDLE_COUNT ||
connectivityCount >= MIN_CONNECTIVITY_COUNT ||
chargingCount >= MIN_CHARGING_COUNT ||
runnableJobs.size() >= MIN_READY_JOBS_COUNT) {
if (DEBUG) {
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
}
for (int i=0; i<runnableJobs.size(); i++) {
//把待處理的工作任務(wù)加到集合中去
mPendingJobs.add(runnableJobs.get(i));
}
} else {
if (DEBUG) {
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
}
}
if (DEBUG) {
Slog.d(TAG, "idle=" + idleCount + " connectivity=" +
connectivityCount + " charging=" + chargingCount + " tot=" +
runnableJobs.size());
}
}通過(guò)上面方法,把待處理的工作任務(wù)加到集合中,然后再調(diào) maybeRunPendingJobsH();這個(gè)前面已提到過(guò),就不再說(shuō)了,一樣的;
到此這篇關(guān)于Android JobScheduler介紹的文章就介紹到這了,更多相關(guān)Android JobScheduler介紹內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android自定義EditText實(shí)現(xiàn)登錄界面
這篇文章主要為大家詳細(xì)介紹了Android自定義EditText實(shí)現(xiàn)登錄界面,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android實(shí)現(xiàn)搜索本地音樂(lè)的方法
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)搜索本地音樂(lè)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
Android使用BroadcastReceiver監(jiān)聽(tīng)網(wǎng)絡(luò)連接狀態(tài)的改變
這篇文章主要為大家詳細(xì)介紹了Android使用BroadcastReceiver監(jiān)聽(tīng)網(wǎng)絡(luò)連接狀態(tài)的改變,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
超過(guò)百萬(wàn)的StackOverflow Flutter 20大問(wèn)題(推薦)
這篇文章主要介紹了超過(guò)百萬(wàn)的StackOverflow Flutter 問(wèn)題,有的問(wèn)題在stackoverflow上有幾十萬(wàn)的閱讀量,說(shuō)明很多人都遇到了這些問(wèn)題,把這些問(wèn)題整理分享給大家需要的朋友可以參考下2020-04-04
Android使用http請(qǐng)求手機(jī)號(hào)碼歸屬地查詢代碼分享
這篇文章主要介紹了Android使用http請(qǐng)求手機(jī)號(hào)碼歸屬地查詢代碼分享的相關(guān)資料,需要的朋友可以參考下2016-06-06
Android ImageView實(shí)現(xiàn)圖片裁剪和顯示功能
這篇文章主要介紹了Android ImageView實(shí)現(xiàn)圖片裁剪和顯示功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02
Android新建水平節(jié)點(diǎn)進(jìn)度條示例
這篇文章主要為大家介紹了Android新建水平節(jié)點(diǎn)進(jìn)度條示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Jsoup 抓取頁(yè)面的數(shù)據(jù)實(shí)例詳解
這篇文章主要介紹了Jsoup 抓取頁(yè)面的數(shù)據(jù)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2016-12-12

