【发布时间】:2017-01-14 18:53:07
【问题描述】:
我正在使用 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() 时似乎有一个烦人的错误可能导致作业运行两次:请参阅here 和here。
Firebase JobDispatcher 呢?
我看到Firebase JobDispatcher 有一个Trigger.NOW trigger(“意味着作业应在其运行时约束得到满足后立即运行”)。如果JobScheduler本身不支持这个,也许这就是要走的路?我被 Firebase JobDispatcher 推迟了,因为它似乎是在使用大锤来解决问题......而且 Firebase 似乎都是关于云消息传递等,这与本地任务调度相距甚远(应该是完全本地关注)。而且它似乎需要 Google Play 服务,这对于本地任务调度来说似乎完全没有必要。此外,如果使用 Firebase 可以立即触发,并且 Firebase 仅在 Android L+ 上使用 JobScheduler,那么肯定可以直接使用 JobScheduler 而不依赖 Firebase 来执行此操作?
编辑:我现在已经尝试过了,即使Trigger.NOW 也不能保证立即响应......事实上,我发现我的设备上几乎正好有 30 秒的延迟,这很奇怪。
失败了……
目前,我认为确保立即执行(如果满足约束)的唯一方法是不使用JobScheduler。
或者可以手动进行初始约束检查,如果满足所有约束,则使用 0 的 setOverrideDeadline() 运行作业,否则在不使用 setOverrideDeadline() 的情况下运行。
如果能够控制JobScheduler 本身的时间似乎更可取,有点像使用AlarmManager 的setWindow() 方法。
【问题讨论】:
-
@OP 你能否将你的帖子组织成更小的子类别,关于你试图解决的问题你想说什么?
-
@JoxTraex 我已经尝试过(短版v长版)但我现在添加了更多子结构。没有人需要阅读短版以外的内容,但长版可以提供更多背景信息。
-
正如您所建议的,我怀疑这种 API 的缺乏是设计使然。当然,欢迎您将其作为一项功能增强来提出要求,尽管任何此类增强最多要到 Android O 才会出现(大概),这意味着要等到 2022 年,这样的功能才会变得司空见惯。 “也许手动进行初始约束检查,如果满足所有约束,则使用 setOverrideDeadline() 为 0 运行作业,否则在不使用 setOverrideDeadline() 的情况下运行作业”——或者,如果满足约束则只执行工作,然后跳过工作。使用
JobScheduler进行即时工作毫无意义。 -
所以@CommonsWare 如果我设置了
JobService来执行预定的作业,有没有办法直接运行它,而不是通过JobScheduler,如果我想让它运行现在?我讨厌代码重复,为此创建一个单独的Service(即使使用共享函数)似乎不优雅....直接启动JobService更简洁? -
"如果我想让它现在运行?" -- AFAIK,你可以自己打电话给
startService()JobService。将要完成的实际工作重构为一个单独的类(用于您的后台线程),该类可以从onStartJob()或startService()创建和使用。看起来你不能自己创建一个JobParameters,所以你不能让startService()调用onStartJob(),除非你通过null获得JobParameters。
标签: android alarmmanager android-jobscheduler