Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A hard-fought road to JobScheduler and back

A hard-fought road to JobScheduler and back

By November 2018 we'll have to switch to targetSdk 26+, which means almost every app, whether explicitly or not, will use platform JobScheduler. However, this component is far away from being perfect, as it has lots of problems you don’t expect or probably wouldn’t even face till the component reaches the users. In this talk, we’ll discuss such problems, as well as mistakes in Android code, and see how we can switch to JobScheduler as painless as possible.

Artur Vasilov

April 20, 2018
Tweet

More Decks by Artur Vasilov

Other Decks in Programming

Transcript

  1. Планируем новую задачу JobInfo jobInfo = new JobInfo.Builder( SAMPLE_JOB_ID, SampleJobService.getComponentName()

    ) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) //... .build(); JobScheduler jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE); jobScheduler.schedule(jobInfo);
  2. JobScheduler перезаписывает задачи JobInfo jobInfo = //... .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setMinimumLatency(TimeUnit.DAYS.toMillis(1)) //...

    .build(); JobScheduler jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE); jobScheduler.schedule(jobInfo);
  3. Можно проверять List<JobInfo> pendingJobs = jobScheduler.getAllPendingJobs(); for (JobInfo pendingJob :

    pendingJobs) { if (pendingJob.getId() == SAMPLE_JOB_ID) { return; } } jobScheduler.schedule(jobInfo);
  4. Ох уж эти булевские флаги @Override public boolean onStartJob(JobParameters params)

    { // ... return false; } @Override public boolean onStopJob(JobParameters params) { return false; }
  5. Ох уж эти булевские флаги @Override public boolean onStartJob(JobParameters params)

    { // ... return false; } @Override public boolean onStopJob(JobParameters params) { return false; }
  6. Ох уж эти булевские флаги @Override public boolean onStartJob(JobParameters params)

    { // ... return false; } @Override public boolean onStopJob(JobParameters params) { return false; }
  7. Ох уж эти булевские флаги (2) @Override public boolean onStartJob(JobParameters

    params) { executorService.execute(() -> { // ... jobFinished(params, false); }); return true; }
  8. ВСЕГДА может что-то пойти не так @Nullable private static List<JobInfo>

    getAllPendingJobs(@NonNull JobScheduler jobScheduler) { try { return jobScheduler.getAllPendingJobs(); } catch (Exception e) { // IllegalStateException, IllegalArgumentException, NPE return null; } }
  9. Android-Job об этом знает public void cancel(int jobId) { try

    { getJobScheduler().cancel(jobId); } catch (Exception e) { mCat.e(e); } }
  10. Ошибка синхронизации java.lang.NullPointerException: Attempt to invoke virtual method 'int com.android.server.job.controllers.JobStatus.getUid()'

    on a null object reference at android.os.Parcel.readException(Parcel.java:1546) at android.os.Parcel.readException(Parcel.java:1493) at android.app.job.IJobCallback$Stub$Proxy.jobFinished(IJobCallback.java:167) at android.app.job.JobService$JobHandler.handleMessage(JobService.java:147) at android.os.Handler.dispatchMessage(Handler.java:102)
  11. Отчаяние и рефлексия interface IJobCallback { /* * Tell the

    job manager that the client is done with its execution, so that it can go on to * the next one and stop attributing wakelock time to us etc. */ void jobFinished(int jobId, boolean reschedule); }
  12. JobIntentService public void onCreate() { super.onCreate(); if (Build.VERSION.SDK_INT >= 26)

    { mJobImpl = new JobServiceEngineImpl(this); mCompatWorkEnqueuer = null; } else { mJobImpl = null; ComponentName cn = new ComponentName(this, this.getClass()); mCompatWorkEnqueuer = getWorkEnqueuer(this, cn, false, 0); } }
  13. Отключаем ресиверы public static void setReceiverEnabled(@NonNull Context context, @NonNull Class<?

    extends BroadcastReceiver> receiverClass, boolean enable) { PackageManager pm = context.getPackageManager(); ComponentName component = new ComponentName(context, receiverClass); int enabledFlag = enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; pm.setComponentEnabledSetting(component, enabledFlag, PackageManager.DONT_KILL_APP); }
  14. Я тебя породил, я тебя и убью @Override public int

    onStartCommand(Intent intent, int flags, int startId) { executorService.execute(() -> { // do some work System.exit(-1); }); return START_NOT_STICKY; }