【JobScheduler】Android 后台任务调度的核心组件指南

JobScheduler 是 Android 平台上原生支持在直接启动模式(Direct Boot Mode)下执行任务的调度器。 相比 WorkManager 需要复杂的配置才能勉强支持直接启动,JobScheduler 在这方面有着天生的优势和明确的 API 支持。

在这里插入图片描述

如果你面临的硬性要求是必须在用户解锁前就执行后台任务,那么从 WorkManager 切换到 JobScheduler 是一个非常明智且正确的选择。

JobScheduler 如何支持直接启动模式?

JobScheduler 通过其构建器 JobInfo.Builder 中的一个关键方法来实现:

  • .setPersisted(true): 这个方法用于设置任务在设备重启后是否依然有效。这是所有重启后任务的基础。
  • .setRequiresDeviceIdle(false).setRequiresCharging(false): 在直接启动模式下,设备通常不被认为是“空闲”的,所以需要放宽这些限制。
  • .setDirectBootAware(true) (API 28+, Android P): 从 Android 9.0 开始,JobInfo.Builder 增加了一个专门的方法,用于明确地将一个任务标记为支持直接启动
  • 对于 API 24-27 (Android N-O): 虽然没有 setDirectBootAware 方法,但只要你的应用组件(Service)被标记为 directBootAware="true",并且任务是 persisted 的,系统就会在直接启动模式下调度它。

如何使用 JobScheduler 实现你的需求

下面是一个完整的示例,展示了如何使用 JobScheduler 来替代 WorkManager,并确保任务能在直接启动模式下运行。

第 1 步:创建一个 JobService

JobServiceJobScheduler 任务的实际执行者。它是一个特殊的 Service

DeviceInfoUploadJobService.java

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.os.Build;
import android.os.UserManager;
import android.util.Log;// 必须在 AndroidManifest.xml 中注册这个 Service
public class DeviceInfoUploadJobService extends JobService {private static final String TAG = "UploadJobService";private volatile boolean isJobCancelled = false;@Overridepublic boolean onStartJob(JobParameters params) {Log.d(TAG, "Job started. Job ID: " + params.getJobId());// 任务在主线程上启动,必须手动开启一个后台线程来执行网络操作new Thread(() -> {doWork(params);}).start();// 返回 true 表示任务正在进行中(在另一个线程上),// 稍后你会手动调用 jobFinished() 来结束它。return true; }private void doWork(JobParameters params) {// 在这里执行你的设备信息获取和网络上报逻辑Log.d(TAG, "执行上报任务...");// 你可以在这里检测是否处于直接启动模式UserManager userManager = getSystemService(UserManager.class);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !userManager.isUserUnlocked()) {Log.w(TAG, "警告:当前在直接启动模式下运行!");// 注意:此时只能访问设备加密存储 (DES)} else {Log.i(TAG, "当前在正常模式下运行。");// 可以访问所有常规数据}// --- 模拟网络请求 ---try {// 假设这里是你的 HTTP 上报代码Thread.sleep(5000); // 模拟耗时操作if (isJobCancelled) {Log.w(TAG, "Job was cancelled before completion.");return;}Log.d(TAG, "上报成功!");// 任务成功完成后,必须调用 jobFinished// 第二个参数 false 表示不需要重新调度这个任务jobFinished(params, false); // 在这里可以调度下一次 24 小时的任务scheduleNextJob(this);} catch (Exception e) {Log.e(TAG, "上报失败: ", e);// 任务失败时,也需要调用 jobFinished// 第二个参数 true 表示希望系统根据退避策略重新调度这个任务jobFinished(params, true);}}// 当系统决定取消正在运行的任务时,这个方法会被调用@Overridepublic boolean onStopJob(JobParameters params) {Log.w(TAG, "Job stopped by system. Job ID: " + params.getJobId());isJobCancelled = true;// 返回 true 表示你希望在条件满足时重新调度这个任务return true; }// 一个辅助方法,用于调度下一次 24 小时的任务private void scheduleNextJob(Context context) {// ... (见下面的调度器代码)}
}
第 2 步:在 AndroidManifest.xml 中注册 JobService

这非常关键,并且需要声明正确的权限和属性。

<manifest ...><!-- JobService 需要这个权限 --><uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><applicationandroid:directBootAware="true"  <!-- 你的应用必须支持直接启动 -->...><serviceandroid:name=".DeviceInfoUploadJobService"android:permission="android.permission.BIND_JOB_SERVICE"android:directBootAware="true" <!-- 关键:将 Service 标记为支持直接启动 -->android:exported="true"/><!-- 你的 BootReceiver 也需要是 directBootAware 的 --><receiverandroid:name=".BootReceiver"android:directBootAware="true"><intent-filter><action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter></receiver></application>
</manifest>
第 3 步:创建一个任务调度器类

这个类将负责创建和调度 JobInfo

ReportJobScheduler.java

import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
import java.util.concurrent.TimeUnit;public class ReportJobScheduler {private static final int JOB_ID = 1001;public static void scheduleInitialJob(Context context) {JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);if (jobScheduler == null) return;ComponentName componentName = new ComponentName(context, DeviceInfoUploadJobService.class);JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, componentName).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) // 需要网络连接.setPersisted(true); // 重启后依然有效// 设置重试策略:30分钟后,线性退避if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {builder.setBackoffCriteria(TimeUnit.MINUTES.toMillis(30), JobInfo.BACKOFF_POLICY_LINEAR);}// 关键:为 Android P 及以上版本明确设置支持直接启动if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {builder.setDirectBootAware(true);}jobScheduler.schedule(builder.build());}// 你可以在 JobService 成功后调用这个方法来安排下一次public static void scheduleNext24HourJob(Context context) {// ... 类似上面的逻辑,但是可以添加 setMinimumLatency(TimeUnit.HOURS.toMillis(24))}
}
第 4 步:在 BootReceiver 中触发调度
public class BootReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 无论是在 LOCKED_BOOT_COMPLETED 还是 BOOT_COMPLETED 时,都去调度任务// JobScheduler 会根据网络状态来决定何时运行if (intent.getAction() != null) {ReportJobScheduler.scheduleInitialJob(context);}}
}

WorkManager vs JobScheduler (在直接启动场景下)

特性WorkManagerJobScheduler
Direct Boot 支持间接且复杂。需要手动、有条件地初始化,并管理不同存储区的上下文。原生支持。通过 setDirectBootAware(true) 明确声明,系统会自动处理。
API 简洁性较高。链式调用,API 更现代。较低。API 更偏向底层,需要自己管理 JobService 的生命周期和线程。
向后兼容性非常好。在旧版本 Android 上会自动回退到 AlarmManager+BroadcastReceiverJobScheduler仅 API 21+。在旧设备上不可用。
重试/约束非常强大和灵活。提供了基本的网络、充电、空闲等约束和退避策略。
线程管理自动doWork() 已经在后台线程上运行。手动onStartJob() 在主线程上,必须自己创建后台线程。

结论:
对于你的特定问题——必须在直接启动模式下运行——JobScheduler 是一个技术上更直接、更可靠的选择。它就是为了这种系统级的、需要在特殊设备状态下运行的任务而设计的。虽然它需要你手动处理更多的细节(如线程),但它能完美地解决 WorkManager 在这个场景下遇到的初始化和存储上下文的根本性难题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/95890.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/95890.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c# 调用basler 相机

目录 一联合halcon&#xff1a; 二 c# 原生 一联合halcon&#xff1a; 环境配置 下载安装pylon软件 下载安装halcon 创建 winform项目 test_basler 添加引用 打开pylon可以连接相机 可以看到我的相机id为23970642 &#xff08; c#联合halcon的基础教程&#xff08;案例…

《2025年AI产业发展十大趋势报告》四十六

《2025年AI产业发展十大趋势报告》四十六随着科技的迅猛发展&#xff0c;人工智能&#xff08;AI&#xff09;作为引领新一轮科技革命和产业变革的战略性技术&#xff0c;正逐步渗透到各个行业和领域&#xff0c;成为推动经济社会发展的重要引擎。2023年&#xff0c;生成式AI的…

c++ 杂记

1. 为什么返回*this?2. 3. 友元函数的使用&#xff1a;需要头文件中类内外声明&#xff0c;cpp文件中实现定义哦// Sales_data.h #ifndef SALES_DATA_H #define SALES_DATA_H#include <string>class Sales_data {std::string bookNo;int units_sold 0;double revenue …

PDF文件基础-计算机字体

计算机字体的原理包含了字符编码、字形渲染和字体文件存储三个关键技术。 字符编码负责将每个字符映射到一个唯一的数字码&#xff1b;字形渲染则将这些数字码转换成屏幕或纸张上可识别的图形&#xff1b;字体文件存储则包含了字符的编码、图形描述信息以及字体的其他属性&…

华为IP(9)

OSPF的基本配置OSPF路由计算前言&#xff1a;1)同一区域内的OSPF路由器拥有完全一致的LSDB&#xff0c;在区域内部&#xff0c;OSPF采用SPF算法完成路由计算。2&#xff09;随着网络规模不断扩大&#xff0c;路由器为了完成路由计算所消耗的内存、CPU资源也越来越多。通过区域划…

java.nio.file.InvalidPathException异常

一.问题概述 本人在ubuntu22.04的操作系统上&#xff0c;运行java程序时创建一个文件时&#xff0c;由于文件名称中包含了中文&#xff0c;所以导致了程序抛出了java.nio.file.InvalidPathException的异常。 java.nio.file.InvalidPathException: Malformed input or input co…

Next系统总结学习(一)

下面我按题号逐条 详细 解释并给出示例与最佳实践。为便于阅读&#xff0c;我会同时给出关键代码片段&#xff08;伪代码/实用例子&#xff09;&#xff0c;并指出常见坑与解决方案。 1. 你是如何理解服务端渲染&#xff08;SSR&#xff09;的&#xff1f;它的核心工作流程是怎…

房屋安全鉴定需要什么条件

房屋安全鉴定需要什么条件&#xff1a;专业流程与必备要素解析房屋安全鉴定是保障建筑使用安全的重要环节&#xff0c;它通过对建筑结构、材料性能及使用状况的全面评估&#xff0c;为房屋的安全使用、改造或维护提供科学依据。随着城市建筑老化及自然灾害频发&#xff0c;房屋…

现代C++:现代C++?

C语言正在走向完美&#xff0c;所以&#xff0c;C语言值得学习&#xff08;甚至研究&#xff09;&#xff0c;这些知识可以成为一切编程的基础。然而在实践中&#xff0c;不必全面的使用C语言的各种特性&#xff0c;而应根据工程项目的实际情况&#xff0c;适当取舍&#xff08…

【C++】哈希表实现

1. 哈希概念 哈希(hash)又称散列&#xff0c;是⼀种组织数据的方式。从译名来看&#xff0c;有散乱排列的意思。本质就是通过哈希 函数把关键字Key跟存储位置建立一个映射关系&#xff0c;查找时通过这个哈希函数计算出Key存储的位置&#xff0c;进行快速查找 1.1 直接定址法…

ai 玩游戏 llm玩街霸 大模型玩街霸 (3)

1. 开源代码地址&#xff1a; https://github.com/OpenGenerativeAI/llm-colosseum 2. 架构&#xff1a; 3. 图片&#xff1a; 4. 感觉还是下面的步骤&#xff1a; a. 实时理解游戏当前环境&#xff0c;英雄角色&#xff0c;英雄状态 b. 根据当前状态感知&#xff0c;生成英雄…

2025年渗透测试面试题总结-59(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 一、SQL注入全解 二、XSS与文件漏洞 三、服务端漏洞专题 四、职业经验与能力评估 1、注入攻击原理是什么…

GPT系列--类GPT2源码剖析

无需多言&#xff0c;大家应该都用过了&#xff0c;如今都更新到GPT-5了。1. GPT-1回到2018年的NLP&#xff0c;神仙打架&#xff0c;BERT与GPT不分先后。GPT是“Generative Pre-Training”的简称&#xff0c;生成式的预训练。BERT和GPT肯定是GPT难训练&#xff0c;引用量也是B…

这是一款没有任何限制的免费远程手机控制手机的软件

这是一款没有任何限制的免费远程手机控制手机的软件支持安卓和苹果1.安装1.1被控制端安装airdroid1.2控制端air mirror2.登录账号控制端和被控制端登录同一个账号3.控制打开控制端软件选择要控制的机器直接点“远程控制“

Observability:更智能的告警来了:更快的分诊、更清晰的分组和可操作的指导

作者&#xff1a;来自 Elastic Drew Post 探索 Elastic Stack 告警的最新增强功能&#xff0c;包括改进的相关告警分组、将仪表盘链接到告警规则&#xff0c;以及将调查指南嵌入到告警中。 在 9.1 版本中&#xff0c;我们对告警进行了重大升级&#xff0c;帮助 SRE 和运维人员更…

数智之光燃盛景 共同富裕创丰饶

8月29日&#xff0c;2025数博会“一带一路”国际大数据产业发展暨数智赋能新时代、共同富裕向未来的会议在贵阳国际生态会议中心隆重举行。作为全球大数据领域的重要盛会&#xff0c;此次活动吸引了来自联合国机构、国际组织、科研院所、知名企业等社会各界的百余位代表&#x…

【网络编程】recv函数的本质是什么?

一、为什么说recv函数的本质是 “copy”&#xff1f; recv是用于从网络连接&#xff08;或其他 IO 对象&#xff09;接收数据的函数&#xff0c;它的核心动作不是 “从网络上拉取数据”&#xff0c;而是 “把已经到达内核缓冲区的数据复制到用户程序的缓冲区”。 具体流程拆解&…

JSP程序设计之输入/输出对象 — out对象

目录1、out对象概述2.实例&#xff1a;out对象方法运用输入/输出对象&#xff0c;可以控制页面的输入和输出&#xff0c;用于访问与所有请求和响应有关的数据&#xff0c;包括out、request和response对象。 1、out对象概述 out对象是JspWriter类的一个实例&#xff0c;是一个…

UE里为什么要有提升变量

1、为了简洁当一个类里面的函数比较多&#xff0c;并且使用比较频繁的时候&#xff0c;就要不断的从这个类节点往外拉线&#xff0c;从而获取不同的函数节点&#xff0c;这样的蓝图就会看起来比较乱&#xff0c;这时候&#xff0c;就可以将这个常用的类提升为变量。2、为了存储…

玩转物联网只需十行代码,可它为何悄悄停止维护

文章目录玩转物联网只需十行代码&#xff0c;可它为何悄悄停止维护1 背景&#xff1a;MQTT 遇上 asyncio&#xff0c;为什么选 hbmqtt&#xff1f;2 hbmqtt 是什么&#xff1f;3 安装&#xff1a;一行命令&#xff0c;但别装最新4 五大核心 API&#xff1a;10 行代码跑通发布订…