JobService extends Service { @Nullable @Override @MainThread public final IBinder onBind(Intent intent) { return binder; }  @MainThread abstract boolean onStartJob(@NonNull JobParameters job); @MainThread abstract boolean onStopJob(@NonNull JobParameters job); @AnyThread final void jobFinished(@NonNull JobParameters job, boolean needsReschedule); @Override @MainThread final int onStartCommand(Intent intent, int flags, int startId); @Nullable @Override @MainThread final IBinder onBind(Intent intent); @Override @MainThread final boolean onUnbind(Intent intent); @Override @MainThread final void onRebind(Intent intent); @Override @MainThread final void onStart(Intent intent, int startId); @Override @MainThread final void onConfigurationChanged(Configuration newConfig); @Override @MainThread final void onTaskRemoved(Intent rootIntent); static final int RESULT_SUCCESS; static final int RESULT_FAIL_RETRY; static final int RESULT_FAIL_NORETRY; }
@Test public void testOnStartCommand_handlesStartJob_validRequest() throws Exception { JobService service = spy(new ExampleJobService()); Job jobSpec = TestUtil.getBuilderWithNoopValidator() .setTag("tag") .setService(ExampleJobService.class) .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) .setTrigger(Trigger.NOW) .setLifetime(Lifetime.FOREVER) .build(); countDownLatch = new CountDownLatch(1); Bundle jobSpecData = getJobCoder().encode(jobSpec, new Bundle()); IRemoteJobService remoteJobService = IRemoteJobService.Stub.asInterface(service.onBind(new Intent(JobService.ACTION_EXECUTE))); remoteJobService.start(jobSpecData, noopCallback); flush(service); assertTrue("Expected job to run to completion", countDownLatch.await(5, TimeUnit.SECONDS)); }
@Test public void testOnStartCommand_handlesStartJob_doNotStartRunningJobAgain() throws Exception { StoppableJobService service = new StoppableJobService( false); Job jobSpec = TestUtil.getBuilderWithNoopValidator() .setTag("tag") .setService(StoppableJobService.class) .setTrigger(Trigger.NOW) .build(); Bundle jobSpecData = getJobCoder().encode(jobSpec, new Bundle()); IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, null); IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, null); flush(service); assertEquals(1, service.getNumberOfStartRequestsReceived()); }
@Test public void stop_withCallback_retry() throws Exception { StoppableJobService service = spy(new StoppableJobService( false)); JobInvocation job = new Builder() .setTag("Tag") .setTrigger(Trigger.NOW) .setService(StoppableJobService.class.getName()) .build(); Bundle jobSpecData = getJobCoder().encode(job, new Bundle()); FutureSettingJobCallback callback = new FutureSettingJobCallback(); IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, callback); IRemoteJobService.Stub.asInterface(service.onBind(null)).stop(jobSpecData, true); flush(service); assertEquals(1, service.getNumberOfStopRequestsReceived()); callback.verifyCalledWithJobAndResult(job, JobService.RESULT_SUCCESS); }
@Test public void stop_withCallback_done() throws Exception { StoppableJobService service = spy(new StoppableJobService( true)); JobInvocation job = new Builder() .setTag("Tag") .setTrigger(Trigger.NOW) .setService(StoppableJobService.class.getName()) .build(); Bundle jobSpecData = getJobCoder().encode(job, new Bundle()); FutureSettingJobCallback callback = new FutureSettingJobCallback(); IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, callback); IRemoteJobService.Stub.asInterface(service.onBind(null)).stop(jobSpecData, true); flush(service); assertEquals(1, service.getNumberOfStopRequestsReceived()); callback.verifyCalledWithJobAndResult(job, JobService.RESULT_FAIL_RETRY); }
@Test public void onStop_calledOnMainThread() throws Exception { final SettableFuture<Looper> looperFuture = SettableFuture.create(); final JobService service = new JobService() { @Override public boolean onStartJob(@NonNull JobParameters job) { return true; } @Override public boolean onStopJob(@NonNull JobParameters job) { looperFuture.set(Looper.myLooper()); return false; } }; Job jobSpec = TestUtil.getBuilderWithNoopValidator() .setTag("tag") .setService(service.getClass()) .setTrigger(Trigger.NOW) .build(); final Bundle jobSpecData = getJobCoder().encode(jobSpec, new Bundle()); IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, noopCallback); Executors.newSingleThreadExecutor() .submit( new Runnable() { @Override public void run() { try { IRemoteJobService.Stub.asInterface(service.onBind(null)).stop(jobSpecData, true); } catch (RemoteException e) { throw new AssertionError("calling stop on binder unexpectedly failed", e); } } }) .get(1, TimeUnit.SECONDS); flush(service); assertEquals( "onStopJob was not called on main thread", Looper.getMainLooper(), looperFuture.get(1, TimeUnit.SECONDS)); }