JobScheduler发布作业两次(不符合预期)

13

我正在撰写有关JobScheduler的教程,并发现了一种奇怪的行为。 我要求在1秒钟内安排3个不同的作业(.setOverrideDeadline(1000)),但它们都被提交并运行了两次...以下是代码:

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);
        }
    }   
使用这段代码,我希望我的任务只运行一次,但是如果我查看日志,我得到了:
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  <******************************************
在本教程中,我分别使用HandlerThread、ExecutorService和AsyncTask来运行三个任务,以解释如何在后台线程中执行工作。我展示了这些不同的技术,因为有些情况下您可能希望将作业排队到同一线程(HandlerThread)中,或者管理线程池(ExecutorService),或者只是使用未受管理的线程(AsyncTask)。 我在MyApplication:onCreate方法中定义了这些作业并安排它们的调度。 要深入了解代码,请访问GitHub:https://github.com/MathiasSeguy-Android2EE/JobSchedulerForGitHub
1个回答

24

谢谢您 - 我在JobScheduler上工作。基于您的应用程序(感谢!),我很容易地复制了这个问题并找到了错误的原因。

简而言之,这是一个案例,除了教程应用程序外,在其他情况下不会经常发生。为了解决这个问题,在您的教程中,将作业的截止时间增加到大于每个后台线程运行时间的数量。

正在发生的是,您按顺序安排工作,并且JobScheduler几乎立即运行它们,因为它们被调度了。 然而,一秒钟后(这一秒钟对于“真实”应用程序来说是不会发生的部分),覆盖截止日期警报触发,并且jobscheduler非常积极地决定任何截止日期已过期的任务都需要再次运行(API合同规定,“截止日期到期”优先于所有其他考虑因素),因此将其放入待处理队列中。 执行作业完成后,将检查待处理队列是否有作业,如果存在,则运行它。

因此,如果在作业运行时截止日期到期,则该作业将触发2次。确保截止日期在作业运行之前到期(这将导致作业运行),或者在之后到期(由于作业已经完成,警报实际上不会触发),一切都按预期工作。

我已在Android N中修复了此问题(不幸的是M已经发布),并添加了CTS测试以确保它保持修复状态。 感谢您提醒我们


非常感谢Matthew的解释。我会修复我的教程并理解问题。顺祝商祺。 - Mathias Seguy Android2ee
4
@Matthew,这个onStartJob的多次触发似乎也会发生在设置setPeriodic工作的情况下。文档说明setDeadline不能与setPeriodic一起使用。我正在设置3个setPeriodic工作,每个工作大约5分钟。但是经过几个周期之后,多个工作只运行了几秒钟。我应该使用oneshot并重新安排吗?还是有什么我错过的东西? - Thupten
@Thupten,当我使用setPeriodic时,也遇到了这样的问题。 - peacepassion
setPeriodic 有同样的问题。有解决的办法吗? - ShahrozKhan91

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接