好的,咱们用一个“快递公司”的故事来揭开JobScheduler的神秘面纱。我是这家公司(Android系统)的首席调度师(JobSchedulerService),而你的App就是个想寄快递的客户。
📦 第一章:客户下单(App提交任务)
客户(你的App)想寄个快递(后台任务),但要求很特别:
“必须在手机充电时 + 连着WiFi + 半夜没人用手机时才能发货!”
于是客户写了个快递订单(JobInfo):
java
Copy
// 客户用Builder模式创建订单(JobInfo)
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, new ComponentName(this, MyJobService.class))
.setRequiresCharging(true) // 充电时才发
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) // 用WiFi
.setRequiresDeviceIdle(true) // 手机闲着才发
.setPeriodic(24 * 60 * 60 * 1000) // 每24小时发一次
.build();
接着把订单交给前台柜台(JobScheduler):
java
Copy
JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
scheduler.schedule(jobInfo); // 前台把订单转给后台调度中心
💡 此时柜台只是个“传话筒”,真正处理订单的是藏在后院的调度中心(
JobSchedulerService)。
🏢 第二章:调度中心干脏活(系统服务层)
调度中心一收到订单,就干三件事:
-
持久化订单:立刻存进仓库(
JobStore),即使手机重启订单也不丢 。 -
检查条件:派一群“侦察兵”(
Controller)盯着手机状态:-
BatteryController:盯着是否充电 -
ConnectivityController:盯着WiFi -
IdleController:盯着手机是否在睡觉
-
-
条件满足时喊快递员:所有侦察兵同时点头时,调度中心大吼:“条件满足!快递员开工!”
🔍 源码亮点:
JobSchedulerService.schedule()会创建JobStatus对象(订单的加强版,含状态、重试次数等)
startTrackingJobLocked()把订单注册给所有侦察兵(Controller)
- 侦察兵们通过
maybeStartTrackingJobLocked()和maybeStopTrackingJobLocked()动态启停监听
🚚 第三章:快递员送货(任务执行层)
快递员(JobServiceContext)是真正的打工人,他接到指令后的动作:
-
开车到客户家:绑定到客户的
MyJobService(就像快递员找到客户的仓库地址):java Copy // 系统源码中的绑定调用 mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE, user); -
敲门喊干活:调用客户服务的
onStartJob():java Copy // 客户在JobService中实现的逻辑 public class MyJobService extends JobService { @Override public boolean onStartJob(JobParameters params) { // 客户在这里搬货(比如下载文件) new Thread(() -> { // 模拟搬货2秒 jobFinished(params, false); // 喊一声:货搬完了! }).start(); return true; // true表示“活还没干完,干完会喊你” } } -
超时或被中断:如果客户太久不开门(超时),或调度中心喊停(比如WiFi断了),快递员会触发
onStopJob(),客户需要清理现场
⚠️ 关键细节:
- 快递员有 超时机制(默认1分钟),防止客户磨蹭
客户在
onStartJob()里必须开线程干活,否则阻塞主线程会被系统“投诉”客户干完活得主动
jobFinished(),否则快递员会一直等(耗电)
♻️ 第四章:异常处理与省电秘籍
调度中心最擅长“见风使舵”:
-
条件突然不满足:比如正送货时WiFi断了,侦察兵立刻通知调度中心,调度中心喊停快递员(触发
onStopJob()) -
失败重试策略:客户在
onStopJob()中返回true,订单会被重新挂起等待条件满足 :java Copy @Override public boolean onStopJob(JobParameters params) { return true; // 快递员:“调度中心,这单回头再试!” } -
省电的精髓:把100个客户的“充电+WiFi发货”需求合并到同一时段,让手机只唤醒一次,避免反复“起床干活”耗电
🌟 总结:JobScheduler的智慧
-
解耦:客户(App)只提需求,调度中心(系统)负责时机。
-
条件组合:多个侦察兵(Controller)协同决策。
-
异步执行:快递员(JobServiceContext)和客户线程分离。
-
持久化:仓库(JobStore)确保任务永不丢。
-
省电:任务合并 + 条件触发 = 少醒少动
对比现实:就像你让美团跑腿买奶茶,不用自己盯着店铺是否开门、骑手是否在线,平台全自动调度——这就是
JobScheduler的哲学。
附:调度中心核心源码一览(给好奇的极客):
java
Copy
// 1. 订单持久化(JobSchedulerService.java)
void schedule(JobInfo job) {
JobStatus jobStatus = JobStatus.createFromJobInfo(job);
mJobStore.add(jobStatus); // 存进仓库
startTrackingJob(jobStatus); // 交给侦察兵们
}
// 2. 侦察兵通知条件满足(JobSchedulerService.java)
public void onControllerStateChanged() {
for (JobStatus job : mJobStore.getJobs()) {
if (isReadyToBeExecuted(job)) { // 所有条件满足?
queueJob(job); // 扔进执行队列
}
}
}
// 3. 快递员执行(JobServiceContext.java)
void executeRunnableJob(JobStatus job) {
bindService(job.getService()); // 绑定客户服务
onServiceConnected(..., service) {
service.onStartJob(job.getParams()); // 调用客户干活
}
}
💡 小提醒:
-
在Android 8.0+,谷歌推荐用
WorkManager(底层可能用JobScheduler或AlarmManager),但核心思想不变 -
想深入源码?去
frameworks/base/services/core/java/com/android/server/job/探险吧!