2015-10-20 20 views
7

Sto scrivendo un tutorial su JobScheduler e trovo uno strano comportamento. Chiedo per 3 posti di lavoro differenti per essere programmati in 1 secondo (.setOverrideDeadline (1000)), ma sono tutti presentati e corse due volte ... Quindi, ecco il codice:JobScheduler postare lavori due volte (non previsto)

public class MyApplication extends Application { 
    private static final int JOB_ID_HanlderThread = 100; 
    private static final int JOB_ID_ExecutorService = 200; 
    private static final int JOB_ID_AsyncTask = 300; 
    JobScheduler mJobScheduler; 
    ExecutorService myExecutorServiceForJobs=null; 
    private static MyApplication INSTANCE; 
    public static MyApplication getInstance(){ 
     return INSTANCE; 
    } 


    /** 
    * Called when the application is starting, before any activity, service, 
    * or receiver objects (excluding content providers) have been created. 
    * Implementations should be as quick as possible (for example using 
    * lazy initialization of state) since the time spent in this function 
    * directly impacts the performance of starting the first activity, 
    * service, or receiver in a process. 
    * If you override this method, be sure to call super.onCreate(). 
    */ 
    @Override 
    public void onCreate() { 
     Log.e("MyApplication", "*********************** onCreate *****************************"); 
     super.onCreate(); 
     //use only for the ExceutorService case 
     INSTANCE=this; 
     //instanciate your JobScheduler 
     mJobScheduler= (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE); 
     Log.e("MyApplication", "onCreate: JobScheduler instanciate"); 

     //this first example use the HandlerThread (no need of executor service) 
     //--------------------------------------------------------------------- 
     //define your JobServices here 
     JobInfo.Builder builder = new JobInfo.Builder(JOB_ID_HanlderThread, 
       new ComponentName(getPackageName(), 
         MyJobServiceUsingHandlerThread.class.getName())); 
     //begin in one second 
     builder.setOverrideDeadline(1000); 
     int returnedValue; 
     //the return value is failure(0) or success(1) not the JobId if success (Javadoc wrong) 
     returnedValue=mJobScheduler.schedule(builder.build()); 
     //launch it 
     if(returnedValue <= 0) { 
      //If something goes wrong (manage exception/error is better than logging them) 
      Log.e("MyApplication", "onCreate: JobScheduler launch the task failure"); 
     }else{ 
      //nothing goes wrong 
      Log.e("MyApplication", "onCreate: JobScheduler launch the task suceess JOB_ID_HanlderThread "+returnedValue); 
     } 

     //this second example use ExecutorService 
     //--------------------------------------- 
     //then again define your Job and launch it 
     JobInfo.Builder builder1 = new JobInfo.Builder(JOB_ID_ExecutorService, 
       new ComponentName(getPackageName(), 
         MyJobServiceUsingExecutor.class.getName())); 
     //begin in one second 
     builder1.setOverrideDeadline(1000); 
     //launch it 
     returnedValue=mJobScheduler.schedule(builder1.build()); 
     if(returnedValue <= 0) { 
      //If something goes wrong (manage exception/error is better than logging them) 
      Log.e("MyApplication", "onCreate: JobScheduler launch the task failure"); 
     }else{ 
      //nothing goes wrong 
      Log.e("MyApplication", "onCreate: JobScheduler launch the task suceess JOB_ID_ExecutorService "+returnedValue); 
     } 

     //this third example use AsyncTask 
     //-------------------------------- 
     //then again define your Job and launch it 
     JobInfo.Builder builder2 = new JobInfo.Builder(JOB_ID_AsyncTask, 
       new ComponentName(getPackageName(), 
         MyJobServiceUsingAsyncTask.class.getName())); 
     //begin in one second 
     builder2.setOverrideDeadline(1000); 
     //launch it 
     returnedValue=mJobScheduler.schedule(builder2.build()); 
     if(returnedValue <= 0) { 
      //If something goes wrong (manage exception/error is better than logging them) 
      Log.e("MyApplication", "onCreate: JobScheduler launch the task failure"); 
     }else{ 
      //nothing goes wrong 
      Log.e("MyApplication", "onCreate: JobScheduler launch the task suceess JOB_ID_AsyncTask "+returnedValue); 
     } 
    } 

Utilizzando questo codice mi aspetto che i miei lavori Esegui una volta, ma se guardo il registro ottengo:

10-20 06:45:13.118 13041-13041/? E/MyApplication: *********************** onCreate ***************************** 
10-20 06:45:13.122 13041-13041/? E/MyApplication: onCreate: JobScheduler instanciate 
10-20 06:45:13.126 13041-13041/? E/MyApplication: onCreate: JobScheduler launch the task suceess JOB_ID_HanlderThread 1 
10-20 06:45:13.127 13041-13041/? E/MyApplication: onCreate: JobScheduler launch the task suceess JOB_ID_ExecutorService 1 
10-20 06:45:13.130 13041-13041/? E/MyApplication: onCreate: JobScheduler launch the task suceess JOB_ID_AsyncTask 1 
10-20 06:45:13.559 13041-13041/? E/MyJobServiceHandler: onStartJob called <-------------------------------- 
10-20 06:45:13.572 13041-13041/? E/MyJobServiceExecutor: onStartJob called <-------------------------------- 
10-20 06:45:14.133 13041-13041/? E/MyJobServiceAsync: onStartJob called <-------------------------------- 
10-20 06:45:14.141 13041-13041/? E/MyJobServiceAsync: onStartJob called <-------------------------------- 
10-20 06:45:18.571 13041-13066/? E/MyHandler: The work is done in a separate thread called MyJobServiceUsingHandlerThread 
10-20 06:45:18.573 13041-13041/? E/MyJobServiceHandler: onDestroy called, Looper is dead <****************************************** 
10-20 06:45:18.574 13041-13041/? E/MyJobServiceHandler: onStartJob called <-------------------------------- 
10-20 06:45:18.576 13041-13067/? E/MyRunnable: The work is done in a separate thread called MyJobServiceUsingExecutorService 
10-20 06:45:18.577 13041-13041/? E/MyJobServiceExecutor: onDestroy called, executor service is dead <****************************************** 
10-20 06:45:18.577 13041-13041/? E/MyApplication: killMyExecutorServiceForJob called 
10-20 06:45:18.577 13041-13041/? E/MyApplication: myExecutorServiceForJobs isShutDown 
10-20 06:45:18.580 13041-13041/? E/MyJobServiceExecutor: onStartJob called <-------------------------------- 
10-20 06:45:19.145 13041-13070/? E/MyAsyncTask: The work is done in a separate thread called AsyncTask #1 
10-20 06:45:19.145 13041-13041/? E/MyAsyncTask: The work is finished <****************************************** 
10-20 06:45:23.576 13041-13075/? E/MyHandler: The work is done in a separate thread called MyJobServiceUsingHandlerThread 
10-20 06:45:23.577 13041-13041/? E/MyJobServiceHandler: onDestroy called, Looper is dead <****************************************** 
10-20 06:45:23.582 13041-13076/? E/MyRunnable: The work is done in a separate thread called MyJobServiceUsingExecutorService 
10-20 06:45:23.584 13041-13041/? E/MyJobServiceExecutor: onDestroy called, executor service is dead <****************************************** 
10-20 06:45:23.584 13041-13041/? E/MyApplication: killMyExecutorServiceForJob called 
10-20 06:45:23.584 13041-13041/? E/MyApplication: myExecutorServiceForJobs isShutDown 
10-20 06:45:24.147 13041-13077/? E/MyAsyncTask: The work is done in a separate thread called AsyncTask #2 
10-20 06:45:24.148 13041-13041/? E/MyAsyncTask: The work is finished <****************************************** 

quello che faccio nel tutorial è corro un lavoro con HandlerThread, un altro con ExecutorService e l'ultimo con AsyncTask per spiegare come fare la lavorare in un thread in background. Mostro queste diverse tecniche perché ci possono essere casi d'uso in cui si desidera accodare i propri lavori nello stesso thread (HandlerThread) o gestire un pool di Thread (ExecutorService) o semplicemente utilizzare thread non gestiti (AsyncTask).

Definisco i lavori e li programma in MyApplication: metodo onCreate. Per dare un'occhiata più approfondita al codice, l'ho messo su GitHub qui: https://github.com/MathiasSeguy-Android2EE/JobSchedulerForGitHub

risposta

15

Grazie per questo - ho lavorato su JobScheduler. In base alla tua app (grazie!) Sono riuscito a riproporlo abbastanza facilmente e rintracciare la causa del bug.

tl; dr, Questo è un caso che non si verifica molto spesso al di fuori di un'app tutorial. Per aggirare il problema nel tuo tutorial, aumenta la scadenza del tuo lavoro a un tempo superiore a quello di esecuzione dei thread in background.

Quello che sta accadendo è che pianifichi i tuoi lavori in successione e che JobScheduler li esegua praticamente immediatamente come pianificato. Tuttavia, un secondo dopo (l'un secondo è la parte che non si verifica per un'app "reale"), l'allarme di scadenza si attiva e il responsabile del processo decide in modo molto aggressivo che qualsiasi lavoro la cui scadenza è scaduta deve essere eseguito di nuovo (il Il contratto API afferma che "scadenza scadenza" supera tutte le altre considerazioni), quindi lo inserisce nella coda in sospeso. Non appena il lavoro di esecuzione è terminato, la coda in sospeso è spuntata, e lì c'è un lavoro, quindi è eseguito.

Quindi, il lavoro verrà generato 2 volte se la scadenza scade mentre il lavoro è in esecuzione. Assicurarsi che la scadenza scada prima che il lavoro venga eseguito (il che si traduce nell'esecuzione del lavoro) o dopo (l'allarme non sarà effettivamente atterrato b/c il lavoro è già finito) e tutto funziona come previsto.

Ho risolto questo problema in Android N (purtroppo M è già stato spedito) e ho aggiunto un test CTS per assicurarmi che rimanga fisso. Grazie per averlo portato alla nostra attenzione

+0

Grazie mille Matteo per le spiegazioni. Fisserò il mio tutorial e capirò il problema. Cordiali saluti. –

+1

@Matthew, Questo fuoco multiplo di onStartJob sembra essere in atto anche per i lavori setPeriodic. La documentazione dice che setDeadline non può essere usato con setPeriodic. Sto impostando 3 lavori con setPeriodic di circa 5 minuti. Ma dopo un paio di periodi, i lavori multipli avvengono in pochi secondi. Dovrei usare oneshot e riprogrammare? o c'è qualcosa che mi manca? – Thupten

+0

@Thupten, ho anche questo problema quando utilizzo setPeriodic. – peacepassion

Problemi correlati