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 - 持久化存储
java
// 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 - 并发控制
java
// 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基类设计
java
// 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 - 电池约束
java
// 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 - 网络约束
java
// 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 - 空闲约束
java
// 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 - 时间约束
java
// 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:
java
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 约束满足判定
java
// 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 任务注册流程
java
// 应用代码示例
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 任务调度核心逻辑
java
// 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 任务执行流程
java
// 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 - 单例实现
java
// 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选择策略
java
// 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
java
// 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基类
java
// 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 - 协程支持
kotlin
// 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使用示例
一次性任务
kotlin
// 定义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 -> {}
}
}
}
周期性任务
kotlin
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
)
}
链式任务
kotlin
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的行为会受到限制:
java
// 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限制的方式
使用白名单
kotlin
// 检查是否在白名单
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
kotlin
val jobInfo = JobInfo.Builder(jobId, componentName)
.setImportantWhileForeground(true) // 前台时重要
.build()
当应用在前台时,Doze不影响该任务。
六、调试与问题诊断
6.1 dumpsys查看任务状态
bash
# 查看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 强制触发任务
bash
# 运行指定任务
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调试
kotlin
// 开启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 常见问题诊断
任务不执行
症状:任务已调度但从不执行
排查步骤:
bash
# 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模式且应用不在白名单
任务执行超时
症状:任务被系统强制停止
kotlin
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 约束条件配置建议
kotlin
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 任务重试策略
kotlin
val retryWork = OneTimeWorkRequestBuilder<UploadWorker>()
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL, // 指数退避
WorkRequest.MIN_BACKOFF_MILLIS, // 初始延迟10秒
TimeUnit.MILLISECONDS
)
.build()
// 重试时间:10s -> 20s -> 40s -> 80s -> ... (最长5小时)
7.4 避免电量消耗
kotlin
// ❌ 错误:频繁周期性任务
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 链式任务最佳实践
kotlin
// 并行执行多个下载任务,然后合并处理
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增强了网络约束控制:
kotlin
// Android 15新增:蜂窝网络约束
val cellularConstraint = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CELLULAR) // 新增
.build()
// 结合网络计费判断
val unmeteredOrCellular = Constraints.Builder()
.setRequiredNetworkType(NetworkType.TEMPORARILY_UNMETERED) // 临时免费
.build()
8.2 优化的Doze集成
java
// 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 增强的并发控制
java
// 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/调试命令速查:
bash# 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
系列导航:
本文基于Android 15 (API Level 35)源码分析,不同厂商的定制ROM可能存在差异。 欢迎来我中的个人主页找到更多有用的知识和有趣的产品