API 31下如何在后台延迟启动长时任务?
API 31+ 后台延迟启动长时任务的解决方案
问题背景
在API 31之前,我们通常通过WorkManager设置初始延迟,结合Worker内启动前台服务的方式,实现用户退出应用时延迟触发长时任务。但Android 12(API 31)新增后台启动前台服务的限制,直接在后台启动前台服务会抛出ForegroundServiceStartNotAllowedException。尝试用setExpedited替代时,又会因「加急任务不能设置延迟」触发java.lang.IllegalArgumentException: Expedited jobs cannot be delayed报错。
解决方案:拆分任务流程
核心思路是把「延迟等待」和「执行长时任务」拆分为两个独立的Work任务:
- 第一个普通Work仅负责延迟等待,不加急,到点后触发第二个任务
- 第二个任务使用
setExpedited标记为加急任务,执行真正的长时操作(此时系统允许后台启动前台服务)
代码实现
- 延迟触发Worker:仅负责到点后启动长时任务
public class DelayTriggerWorker extends Worker { public DelayTriggerWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { // 创建并执行加急的长时任务 OneTimeWorkRequest longRunningWork = new OneTimeWorkRequest.Builder(CloudWorker.class) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .build(); WorkManager.getInstance(getApplicationContext()).enqueue(longRunningWork); return Result.success(); } }
- 修改启动逻辑:启动延迟触发任务,而非直接启动长时任务
public static void startCloudWorker() { // 设置需要的延迟时间,这里以5分钟为例 OneTimeWorkRequest delayRequest = new OneTimeWorkRequest.Builder(DelayTriggerWorker.class) .setInitialDelay(5, TimeUnit.MINUTES) .build(); WorkManager.getInstance(getApplicationContext()).enqueue(delayRequest); }
- 保留原长时任务Worker:无需修改,正常处理前台服务和业务逻辑
public class CloudWorker extends Worker { public CloudWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { final ForegroundInfo foregroundInfo = createForegroundInfo(...); setForegroundAsync(foregroundInfo); // 执行你的长时业务逻辑 return Result.success(); } }
关键注意事项
- 权限要求:Android 13及以上需申请
POST_NOTIFICATIONS权限,否则前台服务通知无法显示,任务可能被系统终止 - 配额处理:
OutOfQuotaPolicy参数需根据业务选择:RUN_AS_NON_EXPEDITED_WORK_REQUEST:配额不足时转为普通任务(可能延迟执行)DROP_WORK_REQUEST:配额不足时直接丢弃任务
- 延迟准确性:WorkManager的延迟为近似值,系统会根据电池优化、系统负载等情况调整实际触发时间
内容的提问来源于stack exchange,提问作者Cheok Yan Cheng
相关产品推荐
相关产品推荐

