欢迎光临
我们一直在努力

JobScheduler与WorkManager:任务调度机制

Android 15 核心子系统系列 – 第25篇

本篇深入分析Android的任务调度系统,理解JobScheduler的约束控制机制和WorkManager的封装实现,掌握后台任务的最佳实践。

引言

想象一个场景:你的应用需要在WiFi连接、电量充足、设备空闲时上传日志到服务器。如果自己实现,需要监听网络变化、电池状态、空闲检测…代码会非常复杂。

这就是为什么Android提供了JobScheduler——一个智能的任务调度系统,让你用简单的API描述"什么条件下执行什么任务",系统自动帮你处理所有复杂的条件判断和优化。

而WorkManager则是Google推荐的现代化封装,它在JobScheduler基础上提供了更友好的API、自动降级支持和协程集成。

在上一篇通知管理中,我们看到Android如何管理前台交互;今天我们将看到,Android如何智能调度后台任务,在保证功能的同时最大化省电。

一、JobScheduler整体架构

1.1 架构设计哲学

JobScheduler的设计遵循几个核心原则:

延迟执行:非紧急任务延迟到合适时机批量执行 约束控制:只在满足所有约束条件时才执行任务 系统优化:自动合并同类任务,减少设备唤醒次数 电池友好:与Doze模式、App Standby深度集成

1.2 五层架构

在这里插入图片描述

核心流程:

  • 应用层:通过JobInfo描述任务和约束
  • Framework层:JobScheduler作为Binder代理
  • System Server层:JobSchedulerService核心调度逻辑
  • Controllers层:各种约束控制器监听系统状态
  • Execution层:满足条件时通过AMS启动应用执行任务
  • 1.3 核心组件分析

    JobStore – 持久化存储

    // frameworks/base/services/core/java/com/android/server/job/JobStore.java
    public class JobStore {
    // XML文件存储任务信息
    private static final String XML_TAG_JOB = "job";

    // 持久化JobStatus到磁盘
    public void writePersistentJobs() {
    synchronized (mLock) {
    for (JobStatus job : mJobSet.getAllJobs()) {
    if (job.isPersisted()) {
    writeJobToXml(job);
    }
    }
    }
    }

    // 启动时恢复任务
    public ArraySet<JobStatus> readJobMapFromDisk() {
    ArraySet<JobStatus> jobs = new ArraySet<>();
    File jobsFile = new File(JOB_PERSIST_DIR, "jobs.xml");
    if (jobsFile.exists()) {
    jobs.addAll(parseJobsFromXml(jobsFile));
    }
    return jobs;
    }
    }

    特点:

    • 使用XML格式存储在 /data/system/job/jobs.xml
    • 设备重启后自动恢复未完成任务
    • 支持任务持久化标志(JobInfo.setPersisted())
    JobConcurrencyManager – 并发控制

    // frameworks/base/services/core/java/com/android/server/job/JobConcurrencyManager.java
    public class JobConcurrencyManager {
    // 最大并发任务数
    private static final int MAX_REGULAR_JOB_COUNT = 16;
    private static final int MAX_BGUSER_IMPORTANT_JOB_COUNT = 4;

    // 任务分配策略
    void assignJobsToContextsLocked() {
    // 1. 优先级排序
    List<JobStatus> jobs = getPendingJobs();
    Collections.sort(jobs, mJobComparator);

    // 2. 检查可用执行上下文
    for (JobServiceContext context : mActiveServices) {
    if (!context.isAvailable()) continue;

    // 3. 匹配最高优先级任务
    JobStatus job = findNextJobLocked(jobs);
    if (job != null) {
    context.executeRunnableJob(job);
    jobs.remove(job);
    }
    }
    }
    }

    Android 15优化:

    • 根据设备性能动态调整并发数
    • 前台应用任务优先级提升
    • 低内存时自动降低并发数

    二、约束控制器(Controllers)机制

    2.1 Controller基类设计

    // frameworks/base/services/core/java/com/android/server/job/controllers/StateController.java
    public abstract class StateController {
    protected final JobSchedulerService mService;

    // 当系统状态改变时调用
    public abstract void maybeStartTrackingJobLocked(JobStatus jobStatus);
    public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus);

    // 通知JobSchedulerService状态变化
    protected void onControllerStateChanged() {
    mService.onControllerStateChanged();
    }
    }

    2.2 核心约束控制器详解

    BatteryController – 电池约束

    // frameworks/base/services/core/java/com/android/server/job/controllers/BatteryController.java
    public class BatteryController extends StateController {
    private boolean mBatteryNotLow; // 电量非低电
    private boolean mCharging; // 充电中

    @Override
    public void maybeStartTrackingJobLocked(JobStatus jobStatus) {
    if (jobStatus.hasBatteryNotLowConstraint() ||
    jobStatus.hasChargingConstraint()) {
    mTrackedJobs.add(jobStatus);
    // 立即检查当前状态
    updateStatusLocked(jobStatus);
    }
    }

    private void updateStatusLocked(JobStatus jobStatus) {
    boolean satisfied = true;

    // 检查充电约束
    if (jobStatus.hasChargingConstraint()) {
    satisfied &= mCharging;
    }

    // 检查电量约束
    if (jobStatus.hasBatteryNotLowConstraint()) {
    satisfied &= mBatteryNotLow;
    }

    if (jobStatus.setBatteryConstraintSatisfied(satisfied)) {
    onControllerStateChanged(); // 触发重新调度
    }
    }

    // 监听电池状态变化
    private final BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
    if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
    int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 1);
    int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, 1);

    mCharging = (status == BatteryManager.BATTERY_STATUS_CHARGING);
    mBatteryNotLow = (level > LOW_BATTERY_THRESHOLD);

    // 更新所有跟踪任务
    for (JobStatus job : mTrackedJobs) {
    updateStatusLocked(job);
    }
    }
    }
    };
    }

    约束条件:

    • BATTERY_NOT_LOW:电量 > 15%
    • CHARGING:正在充电(包括USB/AC/无线)
    ConnectivityController – 网络约束

    // frameworks/base/services/core/java/com/android/server/job/controllers/ConnectivityController.java
    public class ConnectivityController extends StateController {
    private final ConnectivityManager mConnectivityManager;

    @Override
    public void maybeStartTrackingJobLocked(JobStatus jobStatus) {
    if (jobStatus.hasConnectivityConstraint()) {
    NetworkRequest request = jobStatus.getJob().getRequiredNetwork();

    // 注册网络回调
    mConnectivityManager.registerNetworkCallback(
    request,
    new JobNetworkCallback(jobStatus)
    );
    }
    }

    private class JobNetworkCallback extends NetworkCallback {
    private final JobStatus mJobStatus;

    @Override
    public void onAvailable(Network network) {
    // 检查网络是否满足要求
    NetworkCapabilities caps =
    mConnectivityManager.getNetworkCapabilities(network);

    boolean satisfied = evaluateNetworkConstraints(mJobStatus, caps);
    if (mJobStatus.setConnectivityConstraintSatisfied(satisfied)) {
    onControllerStateChanged();
    }
    }

    @Override
    public void onLost(Network network) {
    mJobStatus.setConnectivityConstraintSatisfied(false);
    onControllerStateChanged();
    }
    }

    private boolean evaluateNetworkConstraints(
    JobStatus job, NetworkCapabilities caps) {
    NetworkRequest request = job.getJob().getRequiredNetwork();

    // 检查网络类型
    if (request.hasCapability(NET_CAPABILITY_NOT_METERED)) {
    // 要求非计费网络(WiFi)
    return caps.hasCapability(NET_CAPABILITY_NOT_METERED);
    }

    if (request.hasCapability(NET_CAPABILITY_VALIDATED)) {
    // 要求已验证网络(能上网)
    return caps.hasCapability(NET_CAPABILITY_VALIDATED);
    }

    return true; // 任意网络即可
    }
    }

    网络约束类型(Android 15增强):

    • NETWORK_TYPE_NONE:无需网络
    • NETWORK_TYPE_ANY:任意网络连接
    • NETWORK_TYPE_UNMETERED:非计费网络(WiFi)
    • NETWORK_TYPE_NOT_ROAMING:非漫游网络
    • NETWORK_TYPE_CELLULAR:蜂窝网络(Android 15新增)
    IdleController – 空闲约束

    // frameworks/base/services/core/java/com/android/server/job/controllers/IdleController.java
    public class IdleController extends StateController {
    private boolean mIdleState; // 设备空闲状态

    IdleController(JobSchedulerService service) {
    super(service);

    // 监听Doze模式的IDLE状态
    IntentFilter filter = new IntentFilter();
    filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
    mContext.registerReceiver(mIdleReceiver, filter);
    }

    private final BroadcastReceiver mIdleReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
    PowerManager pm = context.getSystemService(PowerManager.class);
    boolean idle = pm.isDeviceIdleMode();

    if (mIdleState != idle) {
    mIdleState = idle;
    updateAllTrackedJobsLocked();
    }
    }
    };

    private void updateAllTrackedJobsLocked() {
    for (JobStatus job : mTrackedJobs) {
    if (job.setIdleConstraintSatisfied(mIdleState)) {
    onControllerStateChanged();
    }
    }
    }
    }

    触发条件:

    • 屏幕关闭 + 无充电 + 静止超过30分钟(进入Doze IDLE状态)
    • 主要用于低优先级后台同步任务
    TimeController – 时间约束

    // frameworks/base/services/core/java/com/android/server/job/controllers/TimeController.java
    public class TimeController extends StateController {
    private final AlarmManager mAlarmManager;

    @Override
    public void maybeStartTrackingJobLocked(JobStatus jobStatus) {
    if (jobStatus.hasTimingDelayConstraint()) {
    // 设置延迟执行闹钟
    long delayTime = jobStatus.getEarliestRunTime();
    setAlarm(jobStatus, delayTime);
    }

    if (jobStatus.hasDeadlineConstraint()) {
    // 设置截止时间闹钟
    long deadlineTime = jobStatus.getLatestRunTimeElapsed();
    setDeadlineAlarm(jobStatus, deadlineTime);
    }
    }

    private void setAlarm(JobStatus job, long triggerTime) {
    Intent intent = new Intent(ACTION_JOB_DELAY_EXPIRED);
    intent.putExtra(EXTRA_JOB_ID, job.getJobId());

    PendingIntent pi = PendingIntent.getBroadcast(
    mContext, job.getJobId(), intent, 0);

    // 使用非唤醒闹钟,节省电量
    mAlarmManager.set(
    AlarmManager.ELAPSED_REALTIME, // 不唤醒设备
    triggerTime,
    pi
    );
    }

    private void setDeadlineAlarm(JobStatus job, long deadlineTime) {
    // 截止时间必须执行,使用唤醒闹钟
    mAlarmManager.setExact(
    AlarmManager.ELAPSED_REALTIME_WAKEUP, // 唤醒设备
    deadlineTime,
    getDeadlinePendingIntent(job)
    );
    }
    }

    时间约束API:

    JobInfo.Builder builder = new JobInfo.Builder(jobId, componentName);

    // 最早执行时间(延迟)
    builder.setMinimumLatency(5 * 60 * 1000); // 5分钟后

    // 最晚执行时间(截止)
    builder.setOverrideDeadline(15 * 60 * 1000); // 15分钟内必须执行

    // 周期性任务
    builder.setPeriodic(24 * 60 * 60 * 1000); // 每24小时

    2.3 约束满足判定

    // frameworks/base/services/core/java/com/android/server/job/JobStatus.java
    public class JobStatus {
    // 各种约束状态标志
    private boolean mChargingConstraintSatisfied;
    private boolean mBatteryNotLowConstraintSatisfied;
    private boolean mConnectivityConstraintSatisfied;
    private boolean mIdleConstraintSatisfied;
    private boolean mStorageNotLowConstraintSatisfied;

    // 判断是否所有约束都满足
    public boolean isReady() {
    // 1. 检查应用是否被限制(App Standby)
    if (isAppStandbyRestricted()) {
    return false;
    }

    // 2. 检查所有约束
    return (!hasChargingConstraint() || mChargingConstraintSatisfied)
    && (!hasBatteryNotLowConstraint() || mBatteryNotLowConstraintSatisfied)
    && (!hasConnectivityConstraint() || mConnectivityConstraintSatisfied)
    && (!hasIdleConstraint() || mIdleConstraintSatisfied)
    && (!hasStorageNotLowConstraint() || mStorageNotLowConstraintSatisfied)
    && isTimingConstraintSatisfied(); // 时间约束
    }

    private boolean isTimingConstraintSatisfied() {
    long now = SystemClock.elapsedRealtime();

    // 检查最早执行时间
    if (mEarliestRunTimeElapsedMillis > 0 && now < mEarliestRunTimeElapsedMillis) {
    return false;
    }

    // 超过截止时间,强制执行
    if (mLatestRunTimeElapsedMillis > 0 && now >= mLatestRunTimeElapsedMillis) {
    return true;
    }

    return true;
    }
    }

    三、任务调度与执行流程

    3.1 任务注册流程

    // 应用代码示例
    class UploadJobService : JobService() {
    override fun onStartJob(params: JobParameters): Boolean {
    // 在后台线程执行任务
    CoroutineScope(Dispatchers.IO).launch {
    try {
    uploadLogs()
    jobFinished(params, false) // 成功完成
    } catch (e: Exception) {
    jobFinished(params, true) // 失败,需要重试
    }
    }
    return true // 返回true表示任务在后台执行
    }

    override fun onStopJob(params: JobParameters): Boolean {
    // 系统要求停止任务(如条件不再满足)
    return true // 返回true表示需要重新调度
    }
    }

    // 调度任务
    fun scheduleUploadJob(context: Context) {
    val jobScheduler = context.getSystemService(JobScheduler::class.java)

    val jobInfo = JobInfo.Builder(
    UPLOAD_JOB_ID,
    ComponentName(context, UploadJobService::class.java)
    ).apply {
    setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) // WiFi
    setRequiresCharging(true) // 充电中
    setRequiresDeviceIdle(true) // 设备空闲
    setPersisted(true) // 重启后保留
    setBackoffCriteria(30000, JobInfo.BACKOFF_POLICY_EXPONENTIAL) // 失败重试
    }.build()

    val result = jobScheduler.schedule(jobInfo)
    if (result == JobScheduler.RESULT_SUCCESS) {
    Log.d(TAG, "Job scheduled successfully")
    }
    }

    3.2 任务调度核心逻辑

    // frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java
    public class JobSchedulerService extends SystemService {
    private final JobStore mJobs;
    private final List<StateController> mControllers = new ArrayList<>();

    // 应用调用JobScheduler.schedule()
    public int schedule(JobInfo job) {
    synchronized (mLock) {
    // 1. 创建JobStatus
    JobStatus jobStatus = JobStatus.createFromJobInfo(
    job, callingUid, sourcePkg, userId);

    // 2. 持久化存储
    if (job.isPersisted()) {
    mJobs.add(jobStatus);
    }

    // 3. 通知所有Controller开始跟踪
    for (StateController controller : mControllers) {
    controller.maybeStartTrackingJobLocked(jobStatus);
    }

    // 4. 立即尝试调度
    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();

    return JobScheduler.RESULT_SUCCESS;
    }
    }

    // Controller状态变化回调
    @Override
    public void onControllerStateChanged() {
    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    }

    // 检查并运行准备好的任务
    private void maybeRunPendingJobsLocked() {
    // 1. 获取所有准备好的任务
    List<JobStatus> readyJobs = new ArrayList<>();
    for (JobStatus job : mJobs.getJobs()) {
    if (job.isReady()) {
    readyJobs.add(job);
    }
    }

    if (readyJobs.isEmpty()) {
    return;
    }

    // 2. 分配执行上下文
    mConcurrencyManager.assignJobsToContextsLocked(readyJobs);
    }
    }

    3.3 任务执行流程

    // frameworks/base/services/core/java/com/android/server/job/JobServiceContext.java
    public class JobServiceContext {
    private JobStatus mRunningJob;
    private IJobCallback mCallback;

    // 执行任务
    boolean executeRunnableJob(JobStatus job) {
    synchronized (mLock) {
    if (!mAvailable) {
    return false;
    }

    mRunningJob = job;
    mAvailable = false;

    // 1. 绑定JobService
    Intent intent = new Intent();
    intent.setComponent(job.getServiceComponent());

    boolean bound = mContext.bindServiceAsUser(
    intent,
    this, // ServiceConnection
    Context.BIND_AUTO_CREATE,
    UserHandle.of(job.getUserId())
    );

    if (!bound) {
    return false;
    }

    // 2. 启动超时检测(10分钟)
    mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MILLIS);

    return true;
    }
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
    IJobService jobService = IJobService.Stub.asInterface(service);

    try {
    // 3. 调用JobService.onStartJob()
    jobService.startJob(mRunningJob.getJobParams());
    } catch (RemoteException e) {
    handleJobFinished(false, "remote exception");
    }
    }

    // 任务完成回调
    @Override
    public void jobFinished(int jobId, boolean needsReschedule) {
    synchronized (mLock) {
    mHandler.removeCallbacks(mTimeoutRunnable);

    if (needsReschedule) {
    // 应用请求重新调度(失败)
    mJobSchedulerService.rescheduleJobLocked(mRunningJob);
    } else {
    // 任务成功完成,移除
    mJobSchedulerService.removeJobLocked(mRunningJob);
    }

    // 解绑服务
    mContext.unbindService(this);
    mRunningJob = null;
    mAvailable = true;

    // 尝试运行下一个任务
    mJobSchedulerService.maybeRunPendingJobsLocked();
    }
    }
    }

    四、WorkManager架构与实现

    4.1 WorkManager设计理念

    WorkManager是Google推荐的现代任务调度方案,它在JobScheduler基础上提供:

    简洁API:Builder模式,链式调用 自动降级:API 23+用JobScheduler,API 14-22用AlarmManager 协程支持:CoroutineWorker原生支持协程 链式任务:支持任务依赖和顺序执行 LiveData观察:实时监听任务状态

    4.2 WorkManager核心架构

    在这里插入图片描述

    4.3 WorkManager核心实现

    WorkManagerImpl – 单例实现

    // androidx/work/impl/WorkManagerImpl.java
    public class WorkManagerImpl extends WorkManager {
    private static WorkManagerImpl sInstance;

    private final WorkDatabase mWorkDatabase;
    private final Processor mProcessor;
    private final Scheduler mScheduler;

    public static WorkManagerImpl getInstance(Context context) {
    if (sInstance == null) {
    synchronized (WorkManagerImpl.class) {
    if (sInstance == null) {
    sInstance = new WorkManagerImpl(
    context,
    new Configuration.Builder().build()
    );
    }
    }
    }
    return sInstance;
    }

    @Override
    public Operation enqueue(WorkRequest request) {
    // 1. 写入数据库
    WorkSpec workSpec = request.getWorkSpec();
    mWorkDatabase.workSpecDao().insertWorkSpec(workSpec);

    // 2. 调度任务
    mScheduler.schedule(workSpec);

    return new OperationImpl();
    }
    }

    Scheduler选择策略

    // androidx/work/impl/Schedulers.java
    public class Schedulers {
    public static Scheduler createBestAvailableScheduler(Context context) {
    if (Build.VERSION.SDK_INT >= 23) {
    // API 23+ 使用JobScheduler
    return new SystemJobScheduler(context);
    } else {
    // API 14-22 使用AlarmManager
    return new SystemAlarmScheduler(context);
    }
    }
    }

    SystemJobScheduler – 适配JobScheduler

    // androidx/work/impl/background/systemjob/SystemJobScheduler.java
    public class SystemJobScheduler implements Scheduler {
    private final JobScheduler mJobScheduler;

    @Override
    public void schedule(WorkSpec... workSpecs) {
    for (WorkSpec workSpec : workSpecs) {
    JobInfo jobInfo = convertToJobInfo(workSpec);
    mJobScheduler.schedule(jobInfo);
    }
    }

    private JobInfo convertToJobInfo(WorkSpec workSpec) {
    JobInfo.Builder builder = new JobInfo.Builder(
    workSpec.id.hashCode(),
    mJobServiceComponent
    );

    // 转换约束条件
    Constraints constraints = workSpec.constraints;

    if (constraints.requiresCharging()) {
    builder.setRequiresCharging(true);
    }

    if (constraints.requiresDeviceIdle()) {
    builder.setRequiresDeviceIdle(true);
    }

    NetworkType networkType = constraints.getRequiredNetworkType();
    builder.setRequiredNetworkType(convertNetworkType(networkType));

    // 设置触发时间
    if (workSpec.initialDelay > 0) {
    builder.setMinimumLatency(workSpec.initialDelay);
    }

    // 周期性任务
    if (workSpec.isPeriodic()) {
    builder.setPeriodic(workSpec.intervalDuration);
    }

    return builder.build();
    }

    private int convertNetworkType(NetworkType workNetworkType) {
    switch (workNetworkType) {
    case NOT_REQUIRED:
    return JobInfo.NETWORK_TYPE_NONE;
    case CONNECTED:
    return JobInfo.NETWORK_TYPE_ANY;
    case UNMETERED:
    return JobInfo.NETWORK_TYPE_UNMETERED;
    case NOT_ROAMING:
    return JobInfo.NETWORK_TYPE_NOT_ROAMING;
    default:
    return JobInfo.NETWORK_TYPE_NONE;
    }
    }
    }

    4.4 Worker实现

    Worker基类

    // androidx/work/Worker.java
    public abstract class Worker extends ListenableWorker {

    @WorkerThread
    public abstract Result doWork();

    @Override
    public final ListenableFuture<Result> startWork() {
    // 在后台线程执行doWork()
    return mWorkExecutor.executeAsync(() -> {
    return doWork();
    });
    }
    }

    // 结果类型
    public abstract static class Result {
    public static Result success() { ... }
    public static Result success(Data outputData) { ... }
    public static Result retry() { ... } // 重试
    public static Result failure() { ... }
    }

    CoroutineWorker – 协程支持

    // androidx/work/CoroutineWorker.kt
    abstract class CoroutineWorker(
    context: Context,
    params: WorkerParameters
    ) : ListenableWorker(context, params) {

    // 协程上下文(默认Dispatchers.Default)
    open val coroutineContext: CoroutineContext
    get() = Dispatchers.Default

    // 挂起函数,子类实现
    abstract suspend fun doWork(): Result

    // 封装为ListenableFuture
    final override fun startWork(): ListenableFuture<Result> {
    val future = SettableFuture.create<Result>()

    CoroutineScope(coroutineContext).launch {
    try {
    val result = doWork()
    future.set(result)
    } catch (e: CancellationException) {
    future.cancel(true)
    } catch (e: Exception) {
    future.setException(e)
    }
    }

    return future
    }

    // 取消支持
    final override fun onStopped() {
    super.onStopped()
    future.cancel(true)
    }
    }

    4.5 WorkManager使用示例

    一次性任务

    // 定义Worker
    class UploadWorker(
    context: Context,
    params: WorkerParameters
    ) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
    return try {
    // 从输入数据获取参数
    val fileUri = inputData.getString(KEY_FILE_URI)

    // 执行上传
    uploadFile(fileUri)

    // 返回成功结果
    Result.success()
    } catch (e: IOException) {
    // 网络错误,重试
    Result.retry()
    } catch (e: Exception) {
    // 其他错误,失败
    Result.failure()
    }
    }

    private suspend fun uploadFile(uri: String?) {
    // 显示进度通知
    setForeground(createForegroundInfo())

    // 上传逻辑
    withContext(Dispatchers.IO) {
    // … 实际上传代码
    }
    }

    private fun createForegroundInfo(): ForegroundInfo {
    val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
    .setContentTitle("上传文件")
    .setContentText("正在上传…")
    .setSmallIcon(R.drawable.ic_upload)
    .build()

    return ForegroundInfo(NOTIFICATION_ID, notification)
    }
    }

    // 创建并入队任务
    fun scheduleUpload(fileUri: String) {
    // 1. 构建输入数据
    val inputData = workDataOf(KEY_FILE_URI to fileUri)

    // 2. 构建WorkRequest
    val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
    .setInputData(inputData)
    .setConstraints(
    Constraints.Builder()
    .setRequiredNetworkType(NetworkType.UNMETERED) // WiFi
    .setRequiresCharging(true) // 充电
    .build()
    )
    .setBackoffCriteria(
    BackoffPolicy.EXPONENTIAL,
    30, TimeUnit.SECONDS // 失败后30秒重试,指数退避
    )
    .addTag("upload") // 添加标签便于管理
    .build()

    // 3. 入队
    WorkManager.getInstance(context).enqueue(uploadRequest)

    // 4. 观察任务状态
    WorkManager.getInstance(context)
    .getWorkInfoByIdLiveData(uploadRequest.id)
    .observe(lifecycleOwner) { workInfo ->
    when (workInfo.state) {
    WorkInfo.State.ENQUEUED -> Log.d(TAG, "任务已入队")
    WorkInfo.State.RUNNING -> Log.d(TAG, "任务执行中")
    WorkInfo.State.SUCCEEDED -> Log.d(TAG, "任务成功")
    WorkInfo.State.FAILED -> Log.d(TAG, "任务失败")
    WorkInfo.State.CANCELLED -> Log.d(TAG, "任务取消")
    else -> {}
    }
    }
    }

    周期性任务

    class SyncWorker(
    context: Context,
    params: WorkerParameters
    ) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
    return try {
    syncData()
    Result.success()
    } catch (e: Exception) {
    Result.retry()
    }
    }

    private suspend fun syncData() {
    // 同步逻辑
    }
    }

    // 调度周期性任务
    fun scheduleDailySync() {
    val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(
    1, TimeUnit.DAYS // 每天执行一次
    )
    .setConstraints(
    Constraints.Builder()
    .setRequiredNetworkType(NetworkType.CONNECTED)
    .build()
    )
    .setInitialDelay(2, TimeUnit.HOURS) // 首次执行延迟2小时
    .build()

    // 使用唯一名称,避免重复调度
    WorkManager.getInstance(context).enqueueUniquePeriodicWork(
    "daily_sync",
    ExistingPeriodicWorkPolicy.KEEP, // 已存在则保留
    syncRequest
    )
    }

    链式任务

    fun scheduleImageProcessing(imageUri: String) {
    val downloadWork = OneTimeWorkRequestBuilder<DownloadWorker>()
    .setInputData(workDataOf("uri" to imageUri))
    .build()

    val compressWork = OneTimeWorkRequestBuilder<CompressWorker>()
    .build()

    val uploadWork = OneTimeWorkRequestBuilder<UploadWorker>()
    .build()

    // 链式执行:下载 -> 压缩 -> 上传
    WorkManager.getInstance(context)
    .beginWith(downloadWork)
    .then(compressWork)
    .then(uploadWork)
    .enqueue()
    }

    五、Doze模式下的任务调度

    5.1 Doze对JobScheduler的影响

    在Doze模式下,JobScheduler的行为会受到限制:

    // frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java
    private boolean shouldRunInDoze(JobStatus job) {
    // 1. 系统任务不受限制
    if (job.getUid() < Process.FIRST_APPLICATION_UID) {
    return true;
    }

    // 2. 白名单应用可以执行
    DeviceIdleController idleController = LocalServices.getService(
    DeviceIdleController.class);
    if (idleController.isAppIdleWhitelisted(job.getUid())) {
    return true;
    }

    // 3. 设置了IMPORTANT_WHILE_FOREGROUND标志
    if (job.getJob().isImportantWhileForeground() &&
    job.getSourceUid() == mActivityManagerInternal.getForegroundUid()) {
    return true;
    }

    return false;
    }

    限制策略:

    • Light Doze:延迟执行普通任务,仅白名单应用可执行
    • Deep Doze:维护窗口内才能执行任务
    • Maintenance Window:30分钟窗口,指数退避(30min→1h→2h→4h→6h)

    5.2 绕过Doze限制的方式

    使用白名单

    // 检查是否在白名单
    fun isIgnoringBatteryOptimizations(context: Context): Boolean {
    val pm = context.getSystemService(PowerManager::class.java)
    return pm.isIgnoringBatteryOptimizations(context.packageName)
    }

    // 请求加入白名单(需要用户确认)
    fun requestIgnoreBatteryOptimizations(context: Context) {
    val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
    data = Uri.parse("package:${context.packageName}")
    }
    context.startActivity(intent)
    }

    ⚠️ 注意:仅在必要时使用,Google Play审核严格限制白名单权限。

    使用setImportantWhileForeground

    val jobInfo = JobInfo.Builder(jobId, componentName)
    .setImportantWhileForeground(true) // 前台时重要
    .build()

    当应用在前台时,Doze不影响该任务。

    六、调试与问题诊断

    6.1 dumpsys查看任务状态

    # 查看JobScheduler状态
    adb shell dumpsys jobscheduler

    # 输出示例
    JOB #u0a123/1001 from uid 10123: com.example.app/.UploadJobService
    Source package: com.example.app
    Constraints: CHARGING UNMETERED IDLE TIMING_DELAY
    Required constraints: CHARGING UNMETERED
    Satisfied constraints: CHARGING TIMING_DELAY
    Unsatisfied constraints: UNMETERED IDLE
    Earliest run time: 2026-02-28 15:30:00
    Latest run time: 2026-02-28 16:00:00
    Ready: false (waiting for UNMETERED, IDLE)

    # 查看所有待执行任务
    adb shell dumpsys jobscheduler | grep "Pending queue"

    # 查看WorkManager任务
    adb shell dumpsys jobscheduler | grep "androidx.work"

    6.2 强制触发任务

    # 运行指定任务
    adb shell cmd jobscheduler run -f <package> <job-id>

    # 示例
    adb shell cmd jobscheduler run -f com.example.app 1001

    # 进入Doze模式测试任务行为
    adb shell dumpsys deviceidle force-idle

    # 退出Doze模式
    adb shell dumpsys deviceidle unforce

    # 模拟维护窗口
    adb shell dumpsys deviceidle step

    6.3 WorkManager调试

    // 开启WorkManager日志
    WorkManager.getInstance(context).setConfiguration(
    Configuration.Builder()
    .setMinimumLoggingLevel(Log.DEBUG)
    .build()
    )

    // 查询任务状态
    val workInfo = WorkManager.getInstance(context)
    .getWorkInfoById(workRequest.id)
    .get()

    Log.d(TAG, "State: ${workInfo.state}")
    Log.d(TAG, "Progress: ${workInfo.progress}")
    Log.d(TAG, "Run attempt: ${workInfo.runAttemptCount}")

    // 取消任务
    WorkManager.getInstance(context).cancelWorkById(workRequest.id)

    // 取消所有任务
    WorkManager.getInstance(context).cancelAllWork()

    6.4 常见问题诊断

    任务不执行

    症状:任务已调度但从不执行

    排查步骤:

    # 1. 检查任务状态
    adb shell dumpsys jobscheduler | grep -A 20 "com.example.app"

    # 2. 查看未满足的约束
    # Unsatisfied constraints: UNMETERED IDLE

    # 3. 检查应用是否被限制
    adb shell dumpsys deviceidle whitelist
    adb shell dumpsys usagestats | grep -A 10 "com.example.app"

    # 4. 查看是否在Doze模式
    adb shell dumpsys deviceidle

    常见原因:

    • 约束条件未满足(如要求WiFi但使用移动网络)
    • 应用处于App Standby RESTRICTED bucket
    • 设备在Doze模式且应用不在白名单
    任务执行超时

    症状:任务被系统强制停止

    class LongRunningWorker(context: Context, params: WorkerParameters)
    : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
    // 对于长时间运行的任务,使用setForeground提升为前台任务
    setForeground(createForegroundInfo())

    // 执行耗时操作
    performLongTask()

    return Result.success()
    }
    }

    解决方案:

    • JobScheduler任务限制10分钟,使用setForeground()
    • 将大任务拆分为多个小任务
    • 使用前台服务处理真正长时间运行的任务

    七、最佳实践

    7.1 选择合适的调度方案

    场景推荐方案原因
    简单后台任务 WorkManager API简洁,自动降级
    复杂约束控制 JobScheduler 更底层,控制更精细
    周期性同步 WorkManager PeriodicWork 支持灵活时间窗口
    即时执行 Foreground Service 不适合JobScheduler
    精确定时 AlarmManager JobScheduler不保证精确时间

    7.2 约束条件配置建议

    fun scheduleOptimalJob() {
    val constraints = Constraints.Builder()
    // ✅ 优先使用WiFi节省流量
    .setRequiredNetworkType(NetworkType.UNMETERED)

    // ✅ 充电时执行节省电量
    .setRequiresCharging(true)

    // ⚠️ 谨慎使用IDLE约束(可能长时间不满足)
    .setRequiresDeviceIdle(false)

    // ✅ 检查存储空间
    .setRequiresStorageNotLow(true)

    .build()

    val workRequest = OneTimeWorkRequestBuilder<SyncWorker>()
    .setConstraints(constraints)
    .build()

    WorkManager.getInstance(context).enqueue(workRequest)
    }

    7.3 任务重试策略

    val retryWork = OneTimeWorkRequestBuilder<UploadWorker>()
    .setBackoffCriteria(
    BackoffPolicy.EXPONENTIAL, // 指数退避
    WorkRequest.MIN_BACKOFF_MILLIS, // 初始延迟10秒
    TimeUnit.MILLISECONDS
    )
    .build()

    // 重试时间:10s -> 20s -> 40s -> 80s -> … (最长5小时)

    7.4 避免电量消耗

    // ❌ 错误:频繁周期性任务
    val badWork = PeriodicWorkRequestBuilder<SyncWorker>(
    15, TimeUnit.MINUTES // 每15分钟一次,太频繁!
    ).build()

    // ✅ 正确:合理的周期 + 合并请求
    val goodWork = PeriodicWorkRequestBuilder<SyncWorker>(
    6, TimeUnit.HOURS // 每6小时一次
    )
    .setConstraints(
    Constraints.Builder()
    .setRequiredNetworkType(NetworkType.UNMETERED)
    .setRequiresBatteryNotLow(true)
    .build()
    )
    .build()

    7.5 链式任务最佳实践

    // 并行执行多个下载任务,然后合并处理
    val download1 = OneTimeWorkRequestBuilder<DownloadWorker>().build()
    val download2 = OneTimeWorkRequestBuilder<DownloadWorker>().build()
    val download3 = OneTimeWorkRequestBuilder<DownloadWorker>().build()
    val merge = OneTimeWorkRequestBuilder<MergeWorker>().build()

    WorkManager.getInstance(context)
    .beginWith(listOf(download1, download2, download3)) // 并行
    .then(merge) // 合并
    .enqueue()

    八、Android 15新特性

    8.1 改进的网络约束

    Android 15增强了网络约束控制:

    // Android 15新增:蜂窝网络约束
    val cellularConstraint = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.CELLULAR) // 新增
    .build()

    // 结合网络计费判断
    val unmeteredOrCellular = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.TEMPORARILY_UNMETERED) // 临时免费
    .build()

    8.2 优化的Doze集成

    // frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java
    // Android 15改进:更智能的维护窗口预测
    private long predictNextMaintenanceWindow() {
    // 基于用户使用习惯预测最佳执行时间
    UsageStatsManager usm = getService(UsageStatsManager.class);
    long[] idlePeriods = usm.queryIdlePeriods(
    System.currentTimeMillis() DAY_IN_MILLIS,
    System.currentTimeMillis()
    );

    // 选择用户通常不使用的时间段
    return calculateOptimalWindow(idlePeriods);
    }

    8.3 增强的并发控制

    // Android 15:根据设备性能动态调整并发数
    private int calculateMaxConcurrentJobs() {
    ActivityManager am = getService(ActivityManager.class);
    long totalRam = am.getMemoryInfo().totalMem;
    int cpuCores = Runtime.getRuntime().availableProcessors();

    // 6GB+内存且8核+: 20个并发
    if (totalRam >= 6 * GB && cpuCores >= 8) {
    return 20;
    }

    // 4GB+内存: 16个并发
    if (totalRam >= 4 * GB) {
    return 16;
    }

    // 默认: 12个并发
    return 12;
    }

    九、总结

    核心要点回顾

  • JobScheduler架构

    • 五层架构:Application → Framework → System Server → Controllers → Execution
    • 核心组件:JobStore持久化、JobConcurrencyManager并发控制
    • 约束控制器:Battery、Connectivity、Idle、Storage、Time等
  • 约束机制

    • 每个约束由独立Controller监听系统状态
    • 必须所有约束满足才能执行任务
    • 支持最早执行时间和最晚执行时间(deadline强制执行)
  • WorkManager封装

    • 自动选择最优调度器(JobScheduler/AlarmManager)
    • 简洁的Builder API和协程支持
    • Room数据库持久化 + LiveData状态观察
  • Doze模式集成

    • Light/Deep Doze限制任务执行
    • 维护窗口指数退避(30min→6h)
    • 白名单或setImportantWhileForeground绕过限制
  • 最佳实践

    • 优先使用WorkManager(除非需要精细控制)
    • 合理设置约束条件(WiFi+充电最佳)
    • 使用指数退避重试策略
    • 避免频繁周期性任务
    • 链式任务实现复杂工作流
  • 与其他系统的协作

    • PowerManagerService:Doze模式限制任务执行时机
    • NotificationManagerService:前台Worker显示进度通知
    • ActivityManagerService:启动应用进程执行JobService
    • NetworkPolicyManagerService:网络约束判定

    参考源码(基于Android 15 AOSP):

    • frameworks/base/services/core/java/com/android/server/job/
    • frameworks/base/core/java/android/app/job/
    • androidx/work/

    调试命令速查:

    # JobScheduler状态
    adb shell dumpsys jobscheduler

    # 强制运行任务
    adb shell cmd jobscheduler run -f <package> <job-id>

    # Doze模式测试
    adb shell dumpsys deviceidle force-idle
    adb shell dumpsys deviceidle step

    系列导航:

    • 上一篇:NotificationManagerService:通知管理与优先级控制

    本文基于Android 15 (API Level 35)源码分析,不同厂商的定制ROM可能存在差异。 欢迎来我中的个人主页找到更多有用的知识和有趣的产品

    赞(0)
    未经允许不得转载:171主机测评 » JobScheduler与WorkManager:任务调度机制
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址