2015-10-20 5 views
7

Ich schreibe ein Tutorial auf JobScheduler und ich finde ein seltsames Verhalten. Ich bitte um 3 verschiedene Jobs in 1 Sekunde (.setOverrideDeadline (1000)) geplant werden, aber sie sind alle eingereichten und lief zweimal ... Also hier den Code:JobScheduler Buchung Jobs zweimal (nicht erwartet)

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); 
     } 
    } 

diesen Code verwenden ich meine Jobs erwarten laufen einmal, aber wenn ich auf das Protokoll sehe ich erhalten:

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 <****************************************** 

Was muss ich im Tutorial ich betreibe einen Job mit HandlerThread, eine andere mit ExecutorService und dem letzten AsyncTask mit zu erklären, wie das zu tun arbeite in einem Hintergrundthread. Ich zeige diese verschiedenen Techniken, weil es Fälle geben kann, in denen Sie Ihre Jobs in den gleichen Thread (HandlerThread) einreihen oder einen Thread-Pool (ExecutorService) verwalten oder einfach nicht verwaltete Threads (AsyncTask) verwenden möchten.

Ich definiere diese Jobs und plane sie in MyApplication: onCreate Methode. Um einen tieferen Blick auf den Code zu werfen, habe ich ihn hier auf GitHub gesetzt: https://github.com/MathiasSeguy-Android2EE/JobSchedulerForGitHub

Antwort

15

Danke dafür - ich arbeitete am JobScheduler. Basierend auf Ihrer App (danke!) Habe ich es geschafft, dies recht einfach nachzuvollziehen und die Ursache des Fehlers aufzuspüren.

tl; dr, Dies ist ein Fall, der nicht sehr oft außerhalb einer Tutorial-App passieren wird. Um es in Ihrem Lernprogramm zu umgehen, erhöhen Sie den Stichtag für Ihren Job auf einen größeren Wert als die Zeit, die jeder Ihrer Hintergrundthreads benötigt.

Was passiert ist, dass Sie Ihre Aufträge nacheinander planen, und der JobScheduler führt sie so ziemlich sofort aus, wie sie geplant sind. Eine Sekunde später (die Sekunde ist der Teil, der für eine "echte" App nicht passieren wird) wird der Überschreitungsalarm ausgelöst, und der Jobscheduler entscheidet sehr aggressiv, dass jeder Job, dessen Frist abgelaufen ist, erneut ausgeführt werden muss Der API-Vertrag besagt, dass "Ablauf der Ablauffrist" alle anderen Überlegungen übertrumpft, sodass er in die Warteschlange für ausstehende Anfragen gestellt wird. Sobald der auszuführende Job beendet ist, wird die ausstehende Warteschlange überprüft, und es gibt dort einen Job, der ausgeführt wird.

Der Job wird also 2x ausgelöst, wenn die Frist abläuft, während der Job ausgeführt wird. Stellen Sie sicher, dass die Frist abläuft, entweder bevor der Job ausgeführt wird (was dazu führt, dass der Job ausgeführt wird) oder danach (der Alarm wird nicht tatsächlich landen, wenn der Job bereits beendet ist) und alles funktioniert wie beabsichtigt.

Ich habe dies in Android N behoben (leider hat M bereits ausgeliefert), und fügte einen CTS-Test, um sicherzustellen, dass es behoben bleibt. Danke, dass Sie uns darauf aufmerksam gemacht haben

+0

Vielen Dank Matthew für die Erklärungen. Ich werde mein Tutorial fixieren und das Problem verstehen. Mit freundlichen Grüßen. –

+1

@Matthew, Diese Mehrfachfeuer von onStartJob scheint auch für setPeriodic Jobs geschehen. Die Dokumentation besagt, dass setDeadline nicht mit setPeriodic verwendet werden kann. Ich setze 3 Jobs mit setPeriodic von ca. 5 Minuten. Aber nach ein paar Perioden laufen die verschiedenen Jobs in nur wenigen Sekunden. Soll ich Oneshot verwenden und neu planen? oder fehlt mir etwas? – Thupten

+0

@Thupten, ich habe auch ein solches Problem bei der Verwendung von setPeriodic. – peacepassion

Verwandte Themen