Android?JobScheduler詳細(xì)介紹
Android JobScheduler介紹
介紹
JobScheduler是在Android 5.0添加的,它可以檢測網(wǎng)絡(luò)狀態(tài)、設(shè)備是否充電中、低電量、低存儲等狀態(tài),當(dāng)所有條件都滿足時就會觸發(fā)執(zhí)行對應(yīng)的JobService來完成任務(wù)。同時具備了重試、定時執(zhí)行、持久化任務(wù)(設(shè)備重啟后可恢復(fù)任務(wù))等功能??芍^是十分強(qiáng)大
JobScheduler的使用大致分為三步:
- 創(chuàng)建JobService類
- 創(chuàng)建一個JobInfo
- 獲取JobScheduler系統(tǒng)服務(wù)執(zhí)行任務(wù)
1 創(chuàng)建JobService
JobScheduler的原理大致是是通過監(jiān)聽手機(jī)狀態(tài),當(dāng)條件滿足時啟動Service執(zhí)行任務(wù)。
因此在使用JobScheduler之前,我們需要定義一個Service,當(dāng)然并不是隨隨便便定義,而是需要派生自JobService。
只需要重寫onStopJob和onStartJob即可。
class DemoJobService: JobService() { override fun onStartJob(params: JobParameters?): Boolean { //do sth... //這個返回值是有講究的 //true表示Service的工作在一個獨立線程中執(zhí)行,工作完成之后需要調(diào)用jobFinish方法通知JobScheduler工作完成 //false表示Service的工作已經(jīng)完成,JobScheduler收到通知之后會釋放資源 return false } //該方法會在一些極端場景下觸發(fā) //比如當(dāng)前的Job需要有Wifi連接的場景下才可執(zhí)行,但是在執(zhí)行期間 //用戶關(guān)閉Wifi,那么就會觸發(fā)該方法 override fun onStopJob(params: JobParameters?): Boolean { //do sth... //true表示需要進(jìn)行重試 //false表示不再進(jìn)行重試,Job將會被丟棄 return true } }
同時,在manifest中注冊時,還需要設(shè)置一個權(quán)限(否則會報錯),如下
<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是對任務(wù)的描述,比如說需要監(jiān)聽哪些狀態(tài)、重試策略、任務(wù)執(zhí)行時間、是否持久化等等。
JobInfo.Builder的構(gòu)造函數(shù)需要傳入一個jobId,是Job的唯一標(biāo)志,后續(xù)通過該jobId來取消Job。
通過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è)置延遲時間 .setOverrideDeadline(10000)//設(shè)置最后期限,如果達(dá)到該時間點,Job還沒被執(zhí)行,那么會強(qiáng)制執(zhí)行一次 // .setPeriodic(2000)//每隔2s執(zhí)行一次,跟上面兩個會沖突 .setPersisted(true)//持久化,就算手機(jī)關(guān)機(jī),啟動之后也可以恢復(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秒 最小延時、 .setOverrideDeadline(60000)//maximum最多執(zhí)行時間 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//免費的網(wǎng)絡(luò)---wifi 藍(lán)牙 USB .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)//任意網(wǎng)絡(luò)--- /** 設(shè)置重試/退避策略,當(dāng)一個任務(wù)調(diào)度失敗的時候執(zhí)行什么樣的測量采取重試。 initialBackoffMillis:第一次嘗試重試的等待時間間隔ms *backoffPolicy:對應(yīng)的退避策略。比如等待的間隔呈指數(shù)增長。 */ .setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) .setBackoffCriteria(JobInfo.MAX_BACKOFF_DELAY_MILLIS, JobInfo.BACKOFF_POLICY_LINEAR) //.setPeriodic (long intervalMillis)//設(shè)置執(zhí)行周期,每隔一段時間間隔任務(wù)最多可以執(zhí)行一次。 //.setPeriodic(long intervalMillis,long flexMillis)//在周期執(zhí)行的末端有一個flexMiliis長度的窗口期,任務(wù)就可以在這個窗口期執(zhí)行。 //設(shè)置設(shè)備重啟后,這個任務(wù)是否還要保留。需要權(quán)限: RECEIVE_BOOT_COMPLETED //ctrl+shift+y/u x //.setPersisted(boolean isPersisted); // .setRequiresCharging(boolean )//是否需要充電 // .setRequiresDeviceIdle(boolean)//是否需要等設(shè)備出于空閑狀態(tài)的時候 // .addTriggerContentUri(uri)//監(jiān)聽uri對應(yīng)的數(shù)據(jù)發(fā)生改變,就會觸發(fā)任務(wù)的執(zhí)行。 // .setTriggerContentMaxDelay(long duration)//設(shè)置Content發(fā)生變化一直到任務(wù)被執(zhí)行中間的最大延遲時間 //設(shè)置Content發(fā)生變化一直到任務(wù)被執(zhí)行中間的延遲。如果在這個延遲時間內(nèi)content發(fā)生了改變,延遲時間會重寫計算。 // .setTriggerContentUpdateDelay(long durationMilimms)
3 執(zhí)行任務(wù)
最后通過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上手還是比較簡單的,由于本文是基于api23的源碼,因此還有很多新功能沒有在源碼中展示出來,如setRequiresBatteryNotLow(設(shè)置Job對電量的要求)、setRequiresStorageNotLow(設(shè)置Job對存儲空間的要求)等.
特性&適用
- 支持在一個任務(wù)上組合多個條件
- 內(nèi)置條件:設(shè)備待機(jī)、設(shè)備充電和連接網(wǎng)絡(luò)
- 支持持續(xù)的job,這意味著設(shè)備重啟后,之前被中斷的job可以繼續(xù)執(zhí)行
- 支持設(shè)置job的最后執(zhí)行期限
- 根據(jù)你的配置,可以設(shè)置job在后臺運行還是在主線程中運行
適用
需要在Android設(shè)備滿足某種場合才需要去執(zhí)行處理數(shù)據(jù):
- 應(yīng)用具有可以推遲的非面向用戶的工作(定期數(shù)據(jù)庫數(shù)據(jù)更新)
- 應(yīng)用具有當(dāng)插入設(shè)備時希望優(yōu)先執(zhí)行的工作(充電時才希望執(zhí)行的工作備份數(shù)據(jù))
- 需要訪問網(wǎng)絡(luò)或 Wi-Fi 連接時需要進(jìn)行的任務(wù)(如向服務(wù)器拉取內(nèi)置數(shù)據(jù))
- 希望作為一個批次定期運行的許多任務(wù)(s)
特征
- Job Scheduler只有在Api21或以上的系統(tǒng)支持。
- Job Scheduler是將多個任務(wù)打包在一個場景下執(zhí)行。
- 在系統(tǒng)重啟以后,任務(wù)會依然保留在Job Scheduler當(dāng)中,因此不需要監(jiān)聽系統(tǒng)啟動狀態(tài)重復(fù)設(shè)定。
- 如果在一定期限內(nèi)還沒有滿足特定執(zhí)行所需情況,Job Scheduler會將這些任務(wù)加入隊列,并且隨后會進(jìn)行執(zhí)行
使用Job Scheduler,應(yīng)用需要做的事情就是判斷哪些任務(wù)是不緊急的,可以交給Job Scheduler來處理,Job Scheduler集中處理收到的任務(wù),選擇合適的時間,合適的網(wǎng)絡(luò),再一起進(jìn)行執(zhí)行。把時效性不強(qiáng)的工作丟給它做。
源碼分析
JobScheduler是系統(tǒng)服務(wù)JobSchedulerService,JobScheduler是一個抽象類;以下是JobScheduler的源碼:
public abstract class JobScheduler { public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; //這個是提交一個工作任務(wù)JobInfo public abstract int schedule(JobInfo job); public abstract int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag); //這個是取消一個工作任務(wù) public abstract void cancel(int jobId); //這個是取消所有的工作任務(wù) public abstract void cancelAll(); //得到所有將來要執(zhí)行的工作任務(wù) public abstract @NonNull List<JobInfo> getAllPendingJobs(); //根據(jù)Jobid獲得將來要執(zhí)行的工作任務(wù) public abstract @Nullable JobInfo getPendingJob(int jobId); }
JobScheduler的實現(xiàn)類是JobSchedulerImpl:
public class JobSchedulerImpl extends JobScheduler { IJobScheduler mBinder; /* package */ JobSchedulerImpl(IJobScheduler binder) { mBinder = binder; } @Override public int schedule(JobInfo job) { try { //這個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);其實是調(diào)了JobScheduler的實現(xiàn)類JobSchedulerImpl中的schedule方法;然后再調(diào)了mBinder.schedule(job);這個mBinder就是JobSchedulerService,調(diào)用了JobSchedulerService類里面IJobScheduler.Stub內(nèi)部類的schedule方法; JobSchedulerService是在哪里啟動的呢?先看一下的源碼,從源碼分析JobSchedulerService是一個系統(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啟動的,先看一下SystemServer的源碼;
public final class SystemServer { private static final String TAG = "SystemServer"; //手機(jī)開機(jī)啟動后會走這個main方法,然后調(diào)用run方法 public static void main(String[] args) { new SystemServer().run(); }
手機(jī)開機(jī)啟動會走SystemServer中的主函數(shù)main方法,main方法調(diào)用了run方法,下面是run方法中的代碼:
private void run() { //....省略了n多代碼 // Start services. //這里啟動一些系統(tǒng)服務(wù) try { startBootstrapServices(); startCoreServices(); //會走這個 startOtherServices(); } catch (Throwable ex) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting system services", ex); throw ex; } //....省略了n多代碼 }
從run方法可以看出,這里啟動了一系列的系統(tǒng)服務(wù),里面調(diào)用了startOtherServices()方法,那接下看一下startOtherServices方法,向下看:
private void startOtherServices() { //....省略了n多代碼 mSystemServiceManager.startService(TwilightService.class); //這里就是啟動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);這里就啟動了JobSchedulerService服務(wù);接下來我們分析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)); //時間控制類 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()); //new了IJobScheduler.Stub,這個其實就是前面JobSchedulerImpl類中mBinder,這里用到了進(jìn)程間通信binder機(jī)制,不明白的可能先學(xué)習(xí)一下進(jìn)程間通信機(jī)制; mJobSchedulerStub = new JobSchedulerStub(); mJobs = JobStore.initAndGet(this); }
創(chuàng)建了5個不同的StateController,分別添加到mControllers。
類型 | 說明 |
---|---|
ConnectivityController | 注冊監(jiān)聽網(wǎng)絡(luò)連接狀態(tài)的廣播 |
TimeController | 注冊監(jiān)聽job時間到期的廣播 |
IdleController | 注冊監(jiān)聽屏幕亮/滅,dream進(jìn)入/退出,狀態(tài)改變的廣播 |
BatteryController | 注冊監(jiān)聽電池是否充電,電量狀態(tài)的廣播 |
AppIdleController | 監(jiān)聽app是否空閑 |
前面提到調(diào)用mBinder.schedule(job);其實是調(diào)用了JobSchedulerService類里面IJobScheduler.Stub內(nèi)部類的schedule方法;接下看一下這個方法:
final class JobSchedulerStub extends IJobScheduler.Stub { //....省略了n多代碼 @Override public int schedule(JobInfo job) throws RemoteException { if (DEBUG) { Slog.d(TAG, "Scheduling job: " + job.toString()); } //這里做了一個檢驗,非關(guān)鍵代碼 final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); //....省略了n多代碼 long ident = Binder.clearCallingIdentity(); try { //這個是關(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); //通過handler發(fā)消息 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); return JobScheduler.RESULT_SUCCESS; }
從上面代碼可以看出是通過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); }
通過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."); } } }
這個方法主要是遍歷將來要處理的工作任務(wù)然后一個個加到待處理工作任務(wù)集合中去;這個方法執(zhí)行完后就會執(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."); } //通過遍歷待處理任務(wù)集合,一個個處理待處理任務(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()); } //這個方法就是處理待處理任務(wù)的方法 if (!availableContext.executeRunnableJob(nextPending)) { if (DEBUG) { Slog.d(TAG, "Error executing " + nextPending); } mJobs.remove(nextPending); } it.remove(); } } } } }
從上面源碼分析可以得出,這個方法通過遍歷待處理任務(wù)集合,處理任務(wù),這里調(diào)用了availableContext.executeRunnableJob(nextPending)方法,這個就是處理待處理任務(wù)的方法,接下來我們一起看看這個方法的源碼,分析下:
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,這個是進(jìn)程間通訊ServiceConnection,通過調(diào)用availableContext.executeRunnableJob(nextPending)方法,會觸發(fā)調(diào)用onServiceConnected,看到這里應(yīng)該明白了,onServiceConnected方法中的service就是jobservice,里面還用了WakeLock鎖,防止手機(jī)休眠,然后通過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(); //我們就是要找這個方法, 看到這里明白了吧,這個就是調(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類,這個是一個抽象類,有很多實現(xiàn)類,這個我只分析一個ConnectivityController實現(xiàn)類,其他都差不多,接下來分析一下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>(); //這個是手機(jī)網(wǎng)絡(luò)連接改變廣播,網(wǎng)絡(luò)發(fā)生改變,會觸發(fā)這個廣播 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ā)生改變時,會觸發(fā)網(wǎng)絡(luò)連接改變廣播,然后調(diào)用updateTrackedJobs(userid)方法,在updateTrackedJobs方法中,會判斷網(wǎng)絡(luò)是否有改變,有改變的會調(diào) mStateChangedListener.onControllerStateChanged()方法;這樣又調(diào)用了JobSchedulerService類中onControllerStateChanged方法:
@Override public void onControllerStateChanged() { mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); }
在onControllerStateChanged方法中通過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()); } }
通過上面方法,把待處理的工作任務(wù)加到集合中,然后再調(diào) maybeRunPendingJobsH();這個前面已提到過,就不再說了,一樣的;
到此這篇關(guān)于Android JobScheduler介紹的文章就介紹到這了,更多相關(guān)Android JobScheduler介紹內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VerticalBannerView仿淘寶頭條實現(xiàn)垂直輪播廣告
這篇文章主要為大家詳細(xì)介紹了VerticalBannerView仿淘寶頭條實現(xiàn)垂直輪播廣告,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-08-08Android Studio配置(Android Studio4.1為例)
這篇文章主要介紹了Android Studio配置(Android Studio4.1為例),文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Android 開發(fā)使用PopupWindow實現(xiàn)彈出警告框的復(fù)用類示例
這篇文章主要介紹了Android 開發(fā)使用PopupWindow實現(xiàn)彈出警告框的復(fù)用類,結(jié)合實例形式分析了Android基于PopupWindow彈出警告框的復(fù)用類具體布局與功能實現(xiàn)技巧,需要的朋友可以參考下2020-05-05利用Android中BitmapShader制作自帶邊框的圓形頭像
這篇文章給大家介紹了一下如何利用BitmapShader制作圓形頭像,可以自定義要顯示的圖片,邊框顏色和邊框?qū)挾鹊龋行枰呐笥褌兛梢詤⒖冀梃b。2016-09-09Android 4.4.2 橫屏應(yīng)用隱藏狀態(tài)欄和底部虛擬鍵的方法
這篇文章主要介紹了Android 4.4.2 橫屏應(yīng)用隱藏狀態(tài)欄和底部虛擬鍵的方法,需要的朋友可以參考下2017-01-01android 限制某個操作每天只能操作指定的次數(shù)(示例代碼詳解)
這篇文章主要介紹了android 限制某個操作每天只能操作指定的次數(shù),本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06Android LinearLayout實現(xiàn)自動換行效果
這篇文章主要為大家詳細(xì)介紹了Android LinearLayout實現(xiàn)自動換行效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-08-08Android仿Iphone屏幕底部彈出半透明PopupWindow效果
這篇文章主要為大家詳細(xì)介紹了Android仿Iphone屏幕底部彈出半透明PopupWindow效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07Android動畫之逐幀動畫(Frame Animation)實例詳解
這篇文章主要介紹了Android動畫之逐幀動畫(Frame Animation),結(jié)合實例形式較為詳細(xì)的分析了逐幀動畫的原理,注意事項與相關(guān)使用技巧,需要的朋友可以參考下2016-01-01