JobScheduler 发布作业两次(不期望)
JobScheduler posting Jobs twice (not expected)
我正在编写关于 JobScheduler 的教程,我发现了一个 st运行ge 行为。我要求在 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
谢谢你 - 我在 JobScheduler 上工作。基于您的应用程序(谢谢!)我设法很容易地重现了这个问题并找到了错误的原因。
tl;dr,这种情况在教程应用程序之外不会经常发生。要在您的教程中解决它,请将您的工作的截止日期增加到大于每个后台线程的时间 运行。
发生的事情是你连续安排你的工作,并且 JobScheduler 运行 几乎是在他们被安排的时候立即发送它们。
然而,一秒后(这一秒是 "real" 应用程序不会发生的部分)覆盖截止日期警报触发,jobscheduler 非常积极地决定任何截止日期已过期的作业需要 运行 再次(API 合同声明 "deadline expiry" 胜过所有其他考虑因素),因此它将其放入待处理队列中。
一旦执行作业完成,就会检查挂起队列,那里有一个作业,所以它是 运行.
因此,如果在作业 运行ning 时截止日期到期,作业将触发 2 次。确保截止日期在作业 运行s 之前(导致作业 运行ning)或之后(警报实际上不会降落 b/c 作业已经完成)到期,并且一切按预期工作。
我已经在 Android N 中修复了这个问题(遗憾的是 M 已经发布),并添加了 CTS 测试以确保它保持修复。
感谢您提请我们注意
我正在编写关于 JobScheduler 的教程,我发现了一个 st运行ge 行为。我要求在 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
谢谢你 - 我在 JobScheduler 上工作。基于您的应用程序(谢谢!)我设法很容易地重现了这个问题并找到了错误的原因。
tl;dr,这种情况在教程应用程序之外不会经常发生。要在您的教程中解决它,请将您的工作的截止日期增加到大于每个后台线程的时间 运行。
发生的事情是你连续安排你的工作,并且 JobScheduler 运行 几乎是在他们被安排的时候立即发送它们。 然而,一秒后(这一秒是 "real" 应用程序不会发生的部分)覆盖截止日期警报触发,jobscheduler 非常积极地决定任何截止日期已过期的作业需要 运行 再次(API 合同声明 "deadline expiry" 胜过所有其他考虑因素),因此它将其放入待处理队列中。 一旦执行作业完成,就会检查挂起队列,那里有一个作业,所以它是 运行.
因此,如果在作业 运行ning 时截止日期到期,作业将触发 2 次。确保截止日期在作业 运行s 之前(导致作业 运行ning)或之后(警报实际上不会降落 b/c 作业已经完成)到期,并且一切按预期工作。
我已经在 Android N 中修复了这个问题(遗憾的是 M 已经发布),并添加了 CTS 测试以确保它保持修复。 感谢您提请我们注意