private void waitForJobRescheduleService() throws InterruptedException { new JobRescheduleService().rescheduleJobs(mJobManagerRule.getManager()); } }
/*package*/ static void startService(Context context) { try { enqueueWork(context, JobRescheduleService.class, JobIdsInternal.JOB_ID_JOB_RESCHEDULE_SERVICE, new Intent()); latch = new CountDownLatch(1); } catch (Exception e) { /* * Caused by: java.lang.SecurityException: Unable to start service Intent * { cmp=com.evernote/.android.job.JobRescheduleService (has extras) }: Unable to launch * app com.evernote/1210016 for service Intent { cmp=com.evernote/.android.job.JobRescheduleService }: * user 12 is stopped * * It's bad to catch all exceptions. But this service is only a safety check and * if it fails, then better try next time and don't handle the exception upstream * where it's hard to deal with this case. */ CAT.e(e); } }
private JobManager(final Context context) { mContext = context; mJobCreatorHolder = new JobCreatorHolder(); mJobExecutor = new JobExecutor(); if (!JobConfig.isSkipJobReschedule()) { JobRescheduleService.startService(mContext); } mJobStorageLatch = new CountDownLatch(1); new Thread("AndroidJob-storage-init") { @Override public void run() { mJobStorage = new JobStorage(context); mJobStorageLatch.countDown(); } }.start(); }
@SuppressWarnings("UnusedReturnValue") /*package*/ int rescheduleJobs(JobManager manager) { return rescheduleJobs(manager, manager.getAllJobRequests(null, true, true)); }
@Override protected void onHandleWork(@NonNull Intent intent) { /* * Delay this slightly. This avoids a race condition if the app was launched by the * AlarmManager. Then the alarm was already removed, but the JobRequest might still * be available in the storage. We still catch this case, because we never execute * a job with the same ID twice. Nonetheless, add the delay to save resources. */ try { CAT.d("Reschedule service started"); SystemClock.sleep(JobConfig.getJobReschedulePause()); JobManager manager; try { manager = JobManager.create(this); } catch (Exception e) { return; } Set<JobRequest> requests = manager.getAllJobRequests(null, true, true); int rescheduledCount = rescheduleJobs(manager, requests); CAT.d("Reschedule %d jobs of %d jobs", rescheduledCount, requests.size()); } finally { if (latch != null) { // latch can be null, if the service was restarted after a process death latch.countDown(); } } }
@Test @Config(sdk = 21) public void verifyPeriodicJobRescheduled() throws Exception { assertThat(manager().getAllJobRequests()).isEmpty(); ContentValues contentValues = new JobRequest.Builder("tag") .setPeriodic(TimeUnit.HOURS.toMillis(1)) .build() .toContentValues(); manager().getJobStorage().getDatabase() .insert(JobStorage.JOB_TABLE_NAME, null, contentValues); Set<JobRequest> requests = manager().getAllJobRequests(); assertThat(requests).isNotEmpty(); JobScheduler scheduler = (JobScheduler) context().getSystemService(Context.JOB_SCHEDULER_SERVICE); assertThat(scheduler.getAllPendingJobs()).isEmpty(); int rescheduledJobs = new JobRescheduleService().rescheduleJobs(manager()); assertThat(rescheduledJobs).isEqualTo(1); }
@SuppressWarnings("UnusedReturnValue") /*package*/ int rescheduleJobs(JobManager manager) { return rescheduleJobs(manager, manager.getAllJobRequests(null, true, true)); }
private JobManager(Context context) { mContext = context; mJobCreatorHolder = new JobCreatorHolder(); mJobStorage = new JobStorage(context); mJobExecutor = new JobExecutor(); if (!JobConfig.isSkipJobReschedule()) { JobRescheduleService.startService(mContext); } }
/*package*/ static void startService(Context context) { try { enqueueWork(context, JobRescheduleService.class, JobIdsInternal.JOB_ID_JOB_RESCHEDULE_SERVICE, new Intent()); latch = new CountDownLatch(1); } catch (Exception e) { /* * Caused by: java.lang.SecurityException: Unable to start service Intent * { cmp=com.evernote/.android.job.JobRescheduleService (has extras) }: Unable to launch * app com.evernote/1210016 for service Intent { cmp=com.evernote/.android.job.JobRescheduleService }: * user 12 is stopped * * It's bad to catch all exceptions. But this service is only a safety check and * if it fails, then better try next time and don't handle the exception upstream * where it's hard to deal with this case. */ CAT.e(e); } }
@Test @Config(sdk = 21) public void verifyExactJobRescheduled() throws Exception { assertThat(manager().getAllJobRequests()).isEmpty(); ContentValues contentValues = new JobRequest.Builder("tag") .setExact(TimeUnit.HOURS.toMillis(1)) .build() .toContentValues(); manager().getJobStorage().getDatabase() .insert(JobStorage.JOB_TABLE_NAME, null, contentValues); Set<JobRequest> requests = manager().getAllJobRequests(); assertThat(requests).isNotEmpty(); final int jobId = 1; Intent intent = new Intent(context(), PlatformAlarmReceiver.class); assertThat(PendingIntent.getBroadcast(context(), jobId, intent, PendingIntent.FLAG_NO_CREATE)).isNull(); int rescheduledJobs = new JobRescheduleService().rescheduleJobs(manager()); assertThat(rescheduledJobs).isEqualTo(1); assertThat(PendingIntent.getBroadcast(context(), jobId, intent, PendingIntent.FLAG_NO_CREATE)).isNotNull(); }
@Override protected void onHandleWork(@NonNull Intent intent) { /* * Delay this slightly. This avoids a race condition if the app was launched by the * AlarmManager. Then the alarm was already removed, but the JobRequest might still * be available in the storage. We still catch this case, because we never execute * a job with the same ID twice. Nonetheless, add the delay to save resources. */ try { CAT.d("Reschedule service started"); SystemClock.sleep(JobConfig.getJobReschedulePause()); JobManager manager; try { manager = JobManager.create(this); } catch (JobManagerCreateException e) { return; } Set<JobRequest> requests = manager.getAllJobRequests(null, true, true); int rescheduledCount = rescheduleJobs(manager, requests); CAT.d("Reschedule %d jobs of %d jobs", rescheduledCount, requests.size()); } finally { if (latch != null) { // latch can be null, if the service was restarted after a process death latch.countDown(); } } }