Android Job Scheduler - 安排任务立即执行并仅执行一次

13

我正在尝试使用Android作业调度程序来安排一个任务立即执行并且只执行一次。

        JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);

        jobScheduler.cancel(1);

        PersistableBundle bundle = new PersistableBundle();
        bundle.putInt(JobFlags.KEY_PERIODIC_SYNC_JOB, JobFlags.JOB_TYPE_INITIAL_FETCH);

        jobScheduler.schedule(new JobInfo.Builder(1,
                new ComponentName(context, SyncJobLollipop.class))
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
                .setExtras(bundle)
                .setMinimumLatency(10)
                .setOverrideDeadline(24 * 3600 * 1000)
                .build());

但它运行了大约3-4次。这里出了什么问题?

这是作业类本身:

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class SyncJobLollipop extends JobService implements JobFinishedListener {

    @Inject
    SyncJobBackend jobBackend;

    private JobParameters jobParameters;

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        ((MyApplication)getApplication()).getAppComponent().inject(this);
        this.jobParameters = jobParameters;
        PersistableBundle bundle = jobParameters.getExtras();
        int type = bundle.getInt(JobFlags.KEY_PERIODIC_SYNC_JOB);
        jobBackend.onStartJob(type, this);
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters jobParameters) {
        jobBackend.onStopJob();
        return false;
    }

    @Override
    public void onJobFinished(boolean success) {
        jobFinished(jobParameters, !success);
    }

}

顺便说一句:我已经检查过每一次调用jobFinished时传递的值都是false。


2
你确定 jobScheduler.schedule 只会被调用一次吗? - orip
是的。它只被调用一次。我使用的是Android N版本。 - dumb_terminal
@dumb_terminal 你的bundle中有JobFlags.KEY_PERIODIC_SYNC_JOB,我猜这使它成为了“周期性”的。 - Akram
我同意这个名字很令人困惑,但它与使其周期性没有任何关系,所有与日程安排相关的代码都在这里发布。@Merka - dumb_terminal
1个回答

9

为了确保作业只运行一次,您应该在后台工作线程中的 onStartJob(...) 中运行您的代码,并在调用 onStopJob(...) 时删除其回调。

public class SyncJobLollipop extends JobService {
    final Handler workHandler = new Handler();
    Runnable workRunnable;

    @Override
    public boolean onStartJob(JobParameters params) {
        workRunnable = new Runnable() {
            @Override
            public void run() {
                // do your work here,
                // such as jobBackend.onStartJob(type, this)

                boolean reschedule = false;
                jobFinished(params, reschedule);
            }};
        workHandler.post(workRunnable);
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        workHandler.removeCallbacks(workRunnable);
        return false;
    }
}

要立即运行它,您应该将“最低延迟”和“覆盖截止日期”都设置为1。

jobScheduler.schedule(new JobInfo.Builder(1,
            new ComponentName(context, SyncJobLollipop.class))
            .setExtras(bundle)
            .setMinimumLatency(1)
            .setOverrideDeadline(1)
            .build());

请注意,操作系统的Doze模式可能会将JobScheduler任务推迟到“维护期”或设备以其他方式唤醒。然而,上述代码仅在Doze模式之外运行,因此作业应该几乎立即被安排。

2
调用 jobScheduler.schedule 后,是否保证立即运行 onJobStart? - dumb_terminal
我也添加了关于 setOverrideDeadline 的部分。是的,它将立即运行。 - intrepidis

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