JobScheduler:控制从满足约束到作业运行的延迟

JobScheduler: controlling delay from constraints being met to job being run

提问人:drmrbrewer 提问时间:9/7/2016 最后编辑:Communitydrmrbrewer 更新时间:3/16/2023 访问量:3041

问:

我正在使用 JobScheduler 来安排作业。我主要将它用于该方法,该方法允许您指定仅在建立网络连接(或更具体地说,未计量连接)时计划作业。.setRequiredNetworkType()

我使用以下非常简单的代码来安排我的工作:

PersistableBundle extras = new PersistableBundle();
extras.putInt("anExtraInt", someInt);
int networkConstraint = useUnmetered ? JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;

ComponentName componentName = new ComponentName(context, MyJobService.class);
JobInfo jobInfo = new JobInfo.Builder(jobId, componentName)
        .setRequiredNetworkType(networkConstraint)
        .setExtras(extras)
        .build();

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

因此,调度只有一个约束:网络连接(可能是“任何”或“未计量”)。

问题的简短版本

如何指定满足所有约束并实际运行作业的最大延迟,例如“在网络连接后 2 秒内运行作业”?

更长的版本(有漫无边际)

问题

我发现,在某些设备上,如果在已经满足网络约束的时间段内安排作业,则该作业将立即运行(或足够快以被用户感知)。

但在其他设备上,即使已经有合适的网络连接可用(以便作业可以立即运行),在实际运行之前也存在很大的延迟。因此,如果这是对用户操作的响应,则给人的印象是什么都没发生,应用程序已损坏。

现在,我很清楚这可能是......由系统来安排作业以最适合其他需求,并且不能保证在满足所有约束时作业将立即运行。JobScheduler

但是,如果能够在需要时对其进行一些控制,那就太好了。因此,对于按计划进行且没有用户参与的作业,让系统完全控制精确的时间是很好的事情。

但是,如果作业响应用户操作,我希望作业能够毫不延迟地运行......假设有网络连接。(如果没有连接,则会显示一条消息,指出在恢复网络连接时将执行该操作,然后负责确保在网络恢复时运行作业。JobScheduler

setOverrideDeadline() 不是解决方案吗?

我可以看到 JobInfo.Builder 确实有一个 setOverrideDeadline() 方法,这几乎是我想要的。但是,它指定了从计划作业时(即,即使未满足所有约束,也在 10 秒内运行作业)的最大延迟,而不是从满足所有约束时(即在满足所有约束的 10 秒内运行作业)开始的最大延迟。

编辑:并且似乎有一个烦人的错误,这可能导致作业在使用时运行两次:请参阅此处和此处setOverrideDeadline()

Firebase JobDispatcher 怎么样?

我看到 Firebase JobDispatcher 有一个 Trigger.NOW 触发器(“意味着作业应在满足其运行时约束后立即运行”)。如果本机不支持此功能,也许这就是要走的路?我被 Firebase JobDispatcher 推迟了,因为它似乎在用大锤敲碎坚果......而且 Firebase 似乎都是关于云消息传递等的,这与本地任务调度相去甚远(这应该完全是本地关注的问题)。而且它似乎需要 Google Play 服务,这对于本地任务调度来说似乎完全没有必要。此外,如果 Firebase 可以立即触发它,而 Firebase 只用于 Android L+,那么肯定可以在不依赖 Firebase 的情况下直接执行此操作吗?JobSchedulerJobSchedulerJobScheduler

编辑:我现在已经尝试过了,甚至不能保证立即响应......事实上,我发现我的设备上几乎正好有 30 秒的延迟,这很奇怪。Trigger.NOW

如果做不到这一点......

目前,我能看到确保立即执行(如果满足约束条件)的唯一方法是使用 .JobScheduler

Or maybe do the initial constraints check manually, and run the job with a of 0 if all constraints are met, otherwise run it without .setOverrideDeadline()setOverrideDeadline()

It would seem far preferable just to have the ability to control the timing of itself, a bit like you can with the setWindow() method of .JobSchedulerAlarmManager

警报管理器 Android-JobScheduler

评论

0赞 JoxTraex 9/8/2016
@OP could you please organize your post into smaller sub categories about what you are trying to say with respect to the problem you are trying to fix?
0赞 drmrbrewer 9/8/2016
@JoxTraex I had already attempted that (short version v long version) but I've added more substructure now. Nobody need read beyond the short version, but the long version is there for more background.
0赞 CommonsWare 9/11/2016
As you suggested, I suspect that this lack of API is by design. You're certainly welcome to ask for it as a feature enhancement, though any such enhancement would not show up until Android O at best (presumably), meaning it'll be 2022 before such a feature becomes commonplace. "maybe do the initial constraints check manually, and run the job with a setOverrideDeadline() of 0 if all constraints are met, otherwise run it without setOverrideDeadline()" -- or, just do the work if the constraints are met, and skip the job. No sense in using for immediate work.JobScheduler
0赞 drmrbrewer 9/11/2016
So @CommonsWare if I have a set up to carry out the scheduled job, is there any way of running that directly, not via a , if I want it just to run now? I hate code duplication, and to create a separate for this (even with shared functions) seems inelegant.... much neater just to start the directly?JobServiceJobSchedulerServiceJobService
0赞 CommonsWare 9/11/2016
"if I want it just to run now?" -- AFAIK, you can call on your own . Refactor the real work to be done into a separate class (for your background thread), one that can be created and used from or from . It doesn't look like you can create a yourself, so you cannot have call , unless you passed for the .startService()JobServiceonStartJob()startService()JobParametersstartService()onStartJob()nullJobParameters

答:

-2赞 jdesesquelles 2/3/2017 #1

A job scheduler is to schedule jobs: triggering periodically, with delay or with constraints to other jobs. If you want to fire a job instantly, it doesn't need to bee scheduled, just launch it.

ConnectivityManager cm =
            (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = cm.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnectedOrConnecting()) {

        // If there is connectivity, Launch the job directly here

    } else {

        PersistableBundle extras = new PersistableBundle();
        extras.putInt("anExtraInt", someInt);
        int networkConstraint = useUnmetered ?       
        JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;

        ComponentName componentName = new ComponentName(context,MyJobService.class);
        JobInfo jobInfo = new JobInfo.Builder(jobId, componentName)
                .setRequiredNetworkType(networkConstraint)
                .setExtras(extras)
                .build();

        JobScheduler jobScheduler = (JobScheduler)      context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        jobScheduler.schedule(jobInfo);
    }