introduction
After Android 5.0 System, in order to improve the use fluency and extend the battery life, Google introduced the mechanism that the system will recycle the application and automatically destroy the Service pulled up by the application in the application background / lock screen. At the same time, in order to meet the needs of triggering the execution of some tasks under specific conditions (such as network, charging state, power, time, cycle, etc.), JobScheduler mechanism came into being. In a word, JobScheduler is the best choice for tasks triggered by certain predetermined conditions.
1, Overview of JobScheduler mechanism
In the jobscheduler mechanism, each business requiring background is abstracted as a Job. Through system management Job, we can improve resource utilization and reduce unnecessary wake-up, so as to improve performance and save power. When the system starts, it will pass the system_ The server process starts the * * JobSchedulerService * * service. When using this mechanism, first construct a specific background task object through JobInfo and pass it to the background task scheduler through jobscheduler. When the configured conditions are met, the system will execute the corresponding Job on the corresponding JobService. In short, the system provides a background task that can be executed periodically without the developer waking up. It will be executed automatically when the configured conditions are met.
2, JobSchedulerService service
The JobSchedulerService knows nothing about constraints, or the state of active jobs. It receives callbacks from the various controllers and completed jobs and operates accordingly.
From the JobScheduler instance obtained by (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE), we can know that JobSchedulerService also runs in the background in the form of system service. JobSchedulerService does not know the status and constraints of jobs, and processes all kinds of jobs through the callback of various controller s.
1. The start of the schedulerservice is triggered
On COM android. server. Systemserver #startotherservices method
mSystemServiceManager.startService(JobSchedulerService.class);
The way in which SystemServiceManager starts all system core services is similar. Basically, it constructs related objects according to the incoming class bytecode type newInstance reflection, registers them in the system service list, and then triggers its corresponding onStart method to start the corresponding services.
com.android.server.SystemServiceManager#startService(java.lang.Class)
public <T extends SystemService> T startService(Class<T> serviceClass) { try { final String name = serviceClass.getName(); final T service; Constructor<T> constructor = serviceClass.getConstructor(Context.class); service = constructor.newInstance(mContext);//The mscontext passed into the SystemServiceManager reflects the construction of the JobSchedulerService object ... // {@ link ArrayList<SystemService> mServices}Register it. mServices.add(service); // Start it. Start JobSchedulerService service.onStart(); return service; } }
2. Construction of JobSchedulerService object
public final class JobSchedulerService extends com.android.server.SystemService implements StateChangedListener, JobCompletedListener{ ... public JobSchedulerService(Context context) { super(context); mHandler = new JobHandler(context.getMainLooper());//Using system_ The Looper of the main thread in the server process initializes the JobHandler mConstants = new Constants(mHandler); mJobSchedulerStub = new JobSchedulerStub();//Create corresponding Binder server mJobs = JobStore.initAndGet(this); // Create the controllers. mControllers = new ArrayList<StateController>(); mControllers.add(ConnectivityController.get(this));//Register to listen for broadcast of network connection status mControllers.add(TimeController.get(this));//Register to listen to the broadcast when the Job time expires mControllers.add(IdleController.get(this));//Registration monitoring screen on / off, dream in / out, broadcast of status change mBatteryController = BatteryController.get(this);//Register to monitor whether the battery is charged and the broadcast of power status mControllers.add(mBatteryController); mStorageController = StorageController.get(this); mControllers.add(mStorageController); mControllers.add(AppIdleController.get(this));//Monitor whether the app is idle mControllers.add(ContentObserverController.get(this));//Listen for ContentObserver event broadcast mControllers.add(DeviceIdleJobsController.get(this));//Monitoring device idle broadcast } @Override public void onControllerStateChanged() { mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); } @Override public void onRunJobNow(JobStatus jobStatus) { mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget(); } @Override public void onStart() { publishLocalService(JobSchedulerInternal.class, new LocalService()); publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub); }
JobSchedulerService inherits from SystemService class and implements StateChangedListener and JobCompletedListener interfaces. The construction method mainly completes four things during execution.
2.1. Use system_ The JobHandler was initialized by Looper, the main thread of the server process
The process runs on the main thread, so it can't do time-consuming operations.
//com.android.server.job.JobSchedulerService.JobHandler final private class JobHandler extends Handler { public JobHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message message) { synchronized (mLock) { if (!mReadyToRock) {//phase == PHASE_ THIRD_ PARTY_ APPS_ CAN_ mReadyToRock is true at start to run the third-party App return; } switch (message.what) { case MSG_JOB_EXPIRED: { ... } break; case MSG_CHECK_JOB: if (mReportedActive) { // if jobs are currently being run, queue all ready jobs for execution. queueReadyJobsForExecutionLocked(); } else { // Check the list of jobs and run some of them if we feel inclined. maybeQueueReadyJobsForExecutionLocked(); } break; case MSG_CHECK_JOB_GREEDY: queueReadyJobsForExecutionLocked(); break; case MSG_STOP_JOB: cancelJobImplLocked((JobStatus) message.obj, null, "app no longer allowed to run"); break; } maybeRunPendingJobsLocked(); // Don't remove JOB_EXPIRED in case one came along while processing the queue. removeMessages(MSG_CHECK_JOB); } } }
2.2. Create the corresponding Binder server of JobSchedulerService
/** * Binder stub trampoline implementation */ final class JobSchedulerStub extends IJobScheduler.Stub { private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); // IJobScheduler implementation @Override public int schedule(JobInfo job) throws RemoteException { . . . try { return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null); } finally { Binder.restoreCallingIdentity(ident); } } // IJobScheduler implementation @Override public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException { ... long ident = Binder.clearCallingIdentity(); try { return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, -1, null); } finally { Binder.restoreCallingIdentity(ident); } } @Override public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag) throws RemoteException { ... try { return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid, packageName, userId, tag); } finally { Binder.restoreCallingIdentity(ident); } } @Override public List<JobInfo> getAllPendingJobs() throws RemoteException { try { return JobSchedulerService.this.getPendingJobs(uid); } finally { Binder.restoreCallingIdentity(ident); } } @Override public JobInfo getPendingJob(int jobId) throws RemoteException { final int uid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); try { return JobSchedulerService.this.getPendingJob(uid, jobId); } finally { Binder.restoreCallingIdentity(ident); } } @Override public void cancelAll() throws RemoteException { ... try { JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app"); } finally { Binder.restoreCallingIdentity(ident); } } @Override public void cancel(int jobId) throws RemoteException { ... try { JobSchedulerService.this.cancelJob(uid, jobId); } finally { Binder.restoreCallingIdentity(ident); } } };
2.3. Create a persistence related JobStore
When the JobStore object is constructed, it will create / data / system / job / jobs XML files, which may have been stored before, will also parse the XML files, create JobInfo and, and convert them into corresponding JobStatus. Finally, all jobstatuses are saved to the JobSet set set, which is why the JobScheduler can be persistent.
The JobStatus object records the jobId, ComponentName, uid, tag and failure times of the task.
private JobStore(Context context, Object lock, File dataDir) { File systemDir = new File(dataDir, "system"); File jobDir = new File(systemDir, "job"); jobDir.mkdirs(); mJobsFile = new AtomicFile(new File(jobDir, "jobs.xml")); mJobSet = new JobSet(); readJobMapFromDisk(mJobSet); } // frameworks/base/services/core/java/com/android/server/job/JobStore.java /** Used by the {@link JobSchedulerService} to instantiate the JobStore. */ static JobStore initAndGet(JobSchedulerService jobManagerService) { synchronized (sSingletonLock) { if (sSingleton == null) { sSingleton = new JobStore(jobManagerService.getContext(), jobManagerService.getLock(), Environment.getDataDirectory()); } return sSingleton; } }
2.4. Create and register listener StateController of preset conditions
Create and register listeners with preset conditions. Statecontroller is still implemented internally through broadcasting. It listens to the corresponding broadcasting and notifies the listener. When the conditions are met, it will send corresponding messages through the Handler to trigger task execution.
Statecontroller type | explain |
---|---|
ConnectivityController | Register to listen for broadcast of network connection status |
TimeController | Register to listen to the broadcast when the job time expires |
IdleController | Registration monitoring screen on / off, dream in / out, broadcast of status change |
BatteryController | Register to monitor whether the battery is charged and the broadcast of power status |
AppIdleController | Monitor whether the app is idle |
ContentObserverController | Monitor the change of content URIs through ContentObserver |
DeviceIdleJobsController | Set constraints for app according to doze status. |
public class ConnectivityController extends StateController implements ConnectivityManager.OnNetworkActiveListener { private final ConnectivityManager mConnManager; /** Singleton. */ private static ConnectivityController mSingleton; private ConnectivityController(StateChangedListener stateChangedListener, Context context, Object lock) { super(stateChangedListener, context, lock); mConnManager = mContext.getSystemService(ConnectivityManager.class); mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class); final IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); mContext.registerReceiverAsUser( mConnectivityReceiver, UserHandle.SYSTEM, intentFilter, null, null); mNetPolicyManager.registerListener(mNetPolicyListener);//Active monitoring network } ... /** * Update all jobs tracked by this controller. * @param uid only update jobs belonging to this UID, or {@code -1} to update all tracked jobs. */ private void updateTrackedJobs(int uid) { synchronized (mLock) { boolean changed = false; for (int i = 0; i < mTrackedJobs.size(); i++) { final JobStatus js = mTrackedJobs.get(i); if (uid == -1 || uid == js.getSourceUid()) { changed |= updateConstraintsSatisfied(js); } } if (changed) { mStateChangedListener.onControllerStateChanged(); } } } /** * We know the network has just come up. We want to run any jobs that are ready. */ @Override public synchronized void onNetworkActive() { synchronized (mLock) { for (int i = 0; i < mTrackedJobs.size(); i++) { final JobStatus js = mTrackedJobs.get(i); if (js.isReady()) { mStateChangedListener.onRunJobNow(js); } } } } private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateTrackedJobs(-1); } }; }
After Android O banned the sending of some broadcasts, these controllers dynamically registered the broadcasts. These controllers triggered the corresponding preset callback interface and handed it over to JobScheduler for processing
/** * Is it charging */ public static boolean isCharging(Context context){ //Register a broadcast that contains the state of charge and is a continuous broadcast IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent intent = context.registerReceiver(null,filter); //Get charge status int isPlugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); boolean acPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_AC; boolean usbPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_USB; boolean wifiPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_WIRELESS; return acPlugged || usbPlugged || wifiPlugged; }
3. Real start of JobSchedulerService
Like other system services, it performs publishing so that other applications can use the service directly through Binder.
@Override public void onStart() { publishLocalService(JobSchedulerInternal.class, new LocalService()); publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub); }
Finally, the registration is completed in the static code block of system service registry. You can see that when the client requests to obtain a JOB_SCHEDULER_SERVICE service, which returns the JobSchedulerImpl instance inherited from JobScheduler.
registerService(Context.JOB_SCHEDULER_SERVICE, JobScheduler.class, new StaticServiceFetcher<JobScheduler>() { @Override public JobScheduler createService() { IBinder b = ServiceManager.getService(Context.JOB_SCHEDULER_SERVICE); return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b)); }});
At this point, the JobSchedulerService service is started.
3, JobService
Entry point for the callback from the {@link android.app.job.JobScheduler}
The abstract class JobService inherits from the Service class. When the JobScheduler detects that the system state reaches the corresponding start condition, it will start JobService to execute tasks. Therefore, we need to inherit JobService and create a Service inherited from JobService, and we must implement two methods: onStartJob(JobParameters params) and onStopJob(JobParameters params).
public abstract class JobService extends Service { final JobHandler mHandler; final JobSchedulerStub mJobSchedulerStub; IJobService mBinder = new IJobService.Stub() { public void startJob(JobParameters jobParams) { ensureHandler(); //Send msg to the Handler of the main thread_ EXECUTE_ Job message Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams); m.sendToTarget(); } public void stopJob(JobParameters jobParams) { ensureHandler(); //Send msg to the Handler of the main thread_ STOP_ Job message Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams); m.sendToTarget(); } }; void ensureHandler() { synchronized (mHandlerLock) { if (mHandler == null) { mHandler = new JobHandler(getMainLooper()); } } } public final IBinder onBind(Intent intent) { return mBinder.asBinder(); } ... }
When JobService runs in the App process, mHandler is the Handler associated with the main thread of the App process. When sending a message to the main thread to start and stop the task, the onStartJob and onStopJob methods will be called back respectively.
1,boolean OnStartJob(JobParameters params)
When the job starts to execute, the onStartJob(JobParameters params) method (used by the system to trigger the executed task) will be triggered and a boolean value will be returned. If the value is false, the system will think that the task has been executed when it returns; If true is returned, the system task is about to be executed. Therefore, when the task is completed, you need to call jobFinished(JobParameters params, boolean needsRescheduled) to notify the system.
- If the return value is false, the system assumes that any task does not take a long time to run and has been completed by the time the method returns.
- **If the return value is true, the system assumes that the task takes some time and needs to be executed in our own application** When a given task is completed, you need to call the jobFinished(JobParameters params, boolean needsRescheduled) method to stop the task and inform the system that the task has been processed.
In other words, the onStartJob method is called when the system determines that the constraints are met, and we can execute our business logic here.
2,boolean OnStopJob(JobParameters params)
When the system receives a cancellation request, it will trigger the onStopJob(JobParameters params) method to cancel the task waiting to be executed, and also return a boolean value. It is important that if onStartJob(JobParameters params) returns false, the system assumes that there is no running task when it receives a cancellation request. In other words, onStopJob(JobParameters params) will not be called in this case. When receiving a cancellation request, onStopJob(JobParameters params) is used by the system to cancel the suspended task. If onStartJob(JobParameters params) returns false, when the cancellation request is received, the system assumes that there is no currently running work, and it does not call onStopJob(JobParameters params) at all. Therefore, we need to manually call the jobFinished (JobParameters params, boolean needsReschedule) method.
It should be noted that onStartJob and onStopJob methods run in the main thread. We can't do time-consuming operations in them, otherwise it may lead to ANR. We can use another thread Handler or tasks with longer running time to process asynchronous tasks. Therefore, a Handler or AsyncTask is usually created in the above customized JobService class to handle the Job that needs to be performed.
3,void jobFinished (JobParameters params, boolean needsReschedule)
The callback notifies the JobManager that execution has completed, which can be called from any thread because it will eventually run on the main thread of the application. When the system receives this message, it will release the wakeup being saved.
- params -- the param passed in should be consistent with the param in onStartJob
- Reschedule the work according to the condition of reschedule. Otherwise, pass in false to let the system know whether the task should be repeated under the initial conditions**
When the task is completed, you need to call jobfinished (jobparameters params, Boolean needs rescheduled) to let the system know which task has been completed, and it can start queuing the next operation. If this is not done, the work will run only once and the application will not be allowed to perform additional work.
4, JobScheduler
This is an API for scheduling various types of jobs against the framework that will be executed in your application's own process.
From the perspective of code, JobScheduler provides us developers with a series of API s for managing and scheduling JobInfo. At the same time, from another perspective, it is also an Android system service - the binder object related to JobScheduler service. From the above, we know that the implementation class of JobScheduler is JobSchedulerImpl, which mainly calls com.com through binder android. server. job. JobSchedulerService. Binder remote interface method in jobschedulerstub.
method | parameter | explain |
---|---|---|
int schedule(JobInfo job) | Corresponding task information | Add the task to be executed to the scheduling set. If the jobId carried in the passed in parameter already exists, the job with the old ID will be overwritten. If the job with the passed in jobId is currently running, it will be stopped. If the schedule method fails, it will return an error code less than 0. Otherwise it will return to us in jobinfo ID defined in builder. |
void cancel(int jobId) | jobId | Cancel the job corresponding to jobId. If the task is currently executing, it will stop immediately and the return value of its Job Service#onStopjob (Job Parameters) method will be ignored. |
void cancelAll() | \ | Cancel all the jobs configured by the current application. Cancelall() is cautious, because the function of this method is to cancel all the jobs under the uid. That is, when multiple apps pass through shareUid, executing cancelall() in any app will cancel all the jobs in the apps under the same uid |
List< JobInfo > getAllPendingJobs() | \ | Get all jobs configured by the current application |
JobInfo getPendingJob(int jobId) | jobId | Get the Job corresponding to jobId |
public class JobSchedulerImpl extends JobScheduler { IJobScheduler mBinder; /* package */ JobSchedulerImpl(IJobScheduler binder) { mBinder = binder; } @Override public int schedule(JobInfo job) { try { //{@link com.android.server.job.JobSchedulerService.JobSchedulerStub#schedule} return mBinder.schedule(job); } catch (RemoteException e) { return JobScheduler.RESULT_FAILURE; } } @Override public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag) { return mBinder.scheduleAsPackage(job, packageName, userId, tag); } @Override public void cancel(int jobId) { mBinder.cancel(jobId); } @Override public void cancelAll() { mBinder.cancelAll(); } @Override public List<JobInfo> getAllPendingJobs() { return mBinder.getAllPendingJobs(); } @Override public JobInfo getPendingJob(int jobId) { return mBinder.getPendingJob(jobId); } }
1,JobSchedulerService.JobSchedulerStub#schedule
In JobSchedulerImpl, call the schedule method through mBinder, and then pass it to the Binder object JobSchedulerStub of JobSchedulerService server
// IJobScheduler implementation @Override public int schedule(JobInfo job) throws RemoteException { return JobSchedulerService.this.schedule(job, uid); }
Call jobschedulerservice in JobSchedulerStub this. Schedule method,
//@link com.android.server.job.JobSchedulerService.JobSchedulerStub#schedule public int schedule(JobInfo job, int uId) { return scheduleAsPackage(job, uId, null, -1, null); } public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId, String tag) { //Create JobStatus JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag); JobStatus toCancel; synchronized (mLock) { //Cancel the task under the uid first toCancel = mJobs.getJobByUidAndJobId(uId, job.getId()); if (toCancel != null) { cancelJobImpl(toCancel, jobStatus); } //Start tracking the task startTrackingJob(jobStatus, toCancel); } //To system_ The main thread of the server process sends a message mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); return JobScheduler.RESULT_SUCCESS; }
Then the Message is processed through JobHandler, and other processes are similar.
5, Jobinfo & jobinfo Builder
JobInfo is the encapsulation of task subject information, such as task execution conditions, bound JobService class name, policy, retry policy, task execution time, persistence, etc. Through the constructor pattern JobInfo When builder constructs JobInfo, it needs to pass in a jobId and the bound JobService class name, where jobId is the unique flag of the Job, and then cancel the Job through this jobId.
Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the parameters required to schedule work against the calling application.
JobInfo.Builder member method | parameter | explain | remarks |
---|---|---|---|
addTriggerContentUri(JobInfo.TriggerContentUri uri) | Add a TriggerContentUri (the principle is to use ContentObserver to monitor a Content Uri), which will trigger the execution of the task when and only when it changes. | In order to continuously monitor the change of Content, a new task needs to be scheduled after the latest task is triggered (trigger URI cannot be combined with setPeriodic(long) or setpersistent (Boolean)). To continuously monitor changes in Content, you need to schedule new JobInfo to monitor the same URI before completing JobService's latest changes. Because setting this property is not compatible with periodic or persistent jobs, doing so will throw an IllegalArgumentException exception when calling build() | |
setBackoffCriteria(long mills, int policy) | mills indicates the time interval of the first retry attempt, and policy indicates the retry policy | Set the fallback / retry strategy, which is similar to the conflict backoff in the network principle. When a task fails to be scheduled, it needs to be retried | The preset time interval is: DEFAULT_INITIAL_BACKOFF_MILLIS 30000 and MAX_BACKOFF_DELAY_MILLIS 18000000 The default strategies are: backoff_ POLICY_ Explicit binary backoff, the waiting interval increases exponentially and BACKOFF_POLICY_LINEAR |
setExtras(PersistableBundle extras) | Data attached to Job | The additional data attached to the setting, similar to the function of Bundle, is persistent, so only the original type is allowed. | You can use pendingjob Getextras() get |
setMinimumLatency(long minLatencyMillis) | Set the delayed execution time (MS) of the task, which is equivalent to post delay. | ||
setOverrideDeadline(long maxDeadlineMillis) | Set the latest delay time of the task. If other conditions are not met at the specified time, the task will also be started. | ||
setPeriodic(long ms) | Set the running cycle of the task, i.e. once every X milliseconds | ||
setPeriodic(long intervalMillis, long flexMillis) | Set a flex length window at the end of the Job cycle, and tasks may be executed. | Android API 24 and above | |
setPersisted(boolean isPersisted) | Set whether persistence is supported. After the device is restarted, the system determines whether to continue the corresponding task according to the value. | ||
setRequiredNetworkType(int networkType) | Configure network conditions for task execution | NETWORK_TYPE_NONE - the default choice. This task will be executed regardless of whether there is a network or not NETWORK_TYPE_ANY -- any kind of network is needed to make the task executable. NETWORK_TYPE_UNMETERED - the task will be executed only when it is not a cellular network (such as when WIFI is connected) | |
setRequiresCharging(boolean requiresCharging) | This task will only be performed when the device is charged. This is not only inserted into the charger, but also triggered when the battery is in a healthy state, | Generally speaking, the power of mobile phone is more than 15% | |
setRequiresDeviceIdle(boolean isDeviceIdle) | Specify that the Job can run only in the idle state, and execute the work after the device is in the screen off or dreaming state (similar to the sleep animation state of window) for 71 minutes. | ||
setTransientExtras(Bundle extras) | Set the additional data that the job can carry, similar to the function of Intent carrying Bundle. | Android API 26 and above | |
setTriggerContentMaxDelay(long durationMs) | Set the maximum total delay (in milliseconds) allowed from the first detected Content change to the Job, and set the maximum delay between the Content change and the execution of the task. | Android API 24 and above | |
setTriggerContentMaxDelay(long durationMs) | Set the maximum total delay (in milliseconds) allowed from the first detected Content change to the Job, and set the maximum delay between the Content change and the execution of the task. | Android API 24 and above | |
setExtras(PersistableBundle extra) | Set the additional data that the job can carry, similar to the function of Intent carrying Bundle. |
6, Steps for using JobScheduler
1. Inherit JobService and override onStartJob and onStopJob methods to implement customized JobService
After the job is started, the onStartJob method will be called. Because the JobService runs on the main thread, it is necessary to use a sub thread, Handler or an asynchronous task to run time-consuming operations to prevent blocking the main thread. If you want to perform time-consuming operations, you need to create a thread to do it; If onStartJob executes a time-consuming task, it can return false, indicating that the task execution is over. If onStartJob starts a thread to execute a time-consuming task, it must return true, indicating that the task is still executing. After the task is really finished, manually call the JobFinished() method to tell the system that the task has ended.
1.1. Handle time-consuming operations in the form of Handler
public class JobServiceWithHandler extends JobService { private Messenger mActivityMessenger; @Override public int onStartCommand(Intent intent, int flags, int startId) { mActivityMessenger = intent.getParcelableExtra(MESSENGER_INTENT_KEY); return START_NOT_STICKY; } private final Handler mJobHandler = new Handler( new Handler.Callback() { @Override public boolean handleMessage( Message msg ) { //TODO business operation //... jobFinished( (JobParameters) msg.obj, false );//Notify after task execution return true; } } ); @Override public boolean onStartJob(JobParameters params) { mJobHandler.sendMessage( Message.obtain( mJobHandler, 1, params ) ); /* GOOGLE The official website demo simulates cross process communication through Messenger sendMessage(MSG_COLOR_START, params.getJobId()); long duration = params.getExtras().getLong(WORK_DURATION_KEY); // 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);*/ /* new Handler().postDelayed(new Runnable() { @Override public void run() { //Do . . . . jobFinished(params, false); } }, duration); */ //When onStartJob returns true, it means that the time-consuming operation takes longer events than the execution of onStartJob, and it means that the jobFinished method must be called manually at the right time, otherwise other jobs in the application will not be executed return true; } @Override public boolean onStopJob(JobParameters params) { mJobHandler.removeMessages( 1 ); //sendMessage(MSG_COLOR_STOP, params.getJobId()); // When the system receives a request to cancel a Job and the Job is still executing (onStartJob returns true), the system will call the onStopJob method. However, no matter whether onStopJob is called or not, the system will cancel the Job as long as it receives the cancellation request // true requires retrying, false does not retry discarding the job return false; } private void sendMessage(int messageID, @Nullable Object params) { if (mActivityMessenger == null) { return; } Message m = Message.obtain(); m.what = messageID; m.obj = params; mActivityMessenger.send(m); } }
Need to go to androidmanifest Add a service node in XML to let your application have the permission to bind and use this JobService.
<service android:name=".JobServiceWithHandler" android:permission="android.permission.BIND_JOB_SERVICE" />
1.2. Handle time-consuming operations in combination with AsyncTask
public class JobServiceWithAsyncTask extends JobService { private JobParameters mJobParameters; private final AsyncTask<Void, Void, Void> mTask = new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { // TODO time consuming operation return null; } @Override protected void onPostExecute(Void result) { // Inform the system after the TODO time-consuming operation is completed jobFinished(mJobParameters, true); super.onPostExecute(result); } }; @Override public boolean onStartJob(JobParameters params) { // Return true, indicating that the work is time-consuming and needs to call jobFinished to destroy after the work processing is completed mJobParameters = params; mTask.execute(); return true; } @Override public boolean onStopJob(JobParameters params) { return false; } }
You also need to register in the manifest file
<service android:name=".JobServiceWithAsyncTask" android:permission="android.permission.BIND_JOB_SERVICE" />
2. Get instance of JobScheduler
JobScheduler mJobScheduler = (JobScheduler) getSystemService( Context.JOB_SCHEDULER_SERVICE );
3. Build job JobInfo object preset trigger condition binding JobService
Use JobInfo Builder to build a JobInfo object and bind the customized JobService.
// jobId : 0 PersistableBundle extras = new PersistableBundle(); extras.putString("DATA","xxxx"); //Create a job JobInfo jobInfo = new JobInfo.Builder(0,new ComponentName(context, JobServiceWithHandler.class)) //bInfo.Builder(0,new ComponentName(context, JobServiceWithAsyncTask.class)) //Only when charging .setRequiresCharging(true) //Not a cellular network .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setExtras(extras).build();
4. Schedule the specified job through the JobScheduler object instance
//Submit task if( mJobScheduler.schedule( jobInfo) <= 0 ) { //If something goes wrong }
After submitting, wait for the conditions to be met, and the system will execute automatically.