上一篇文章:JobScheduler源码分析(三) Job从创建到执行
在前两篇文章中,对JobSchedulerService的启动和Job的调度过程大致做了个梳理,通过前几篇的分析我们知道,要使得客户端Job被JSS调度执行,必须满足该Job在创建时所设置的约束,而这些约束何时满足,这将由StateController进行控制,本篇中将对所有StateController类的控制流程进行分析。
通过前面文章的分析得,共有八个StateController的子类,各自单独地记录每个Job的状态,并在Job准备好运行时通知JSS进行调度执行,或者在约束条件不满足时通知JSS停止Job的执行。所有的StateController类有如下共性:
- 1.在JobSchedulerService的构造方法中进行实例化;
- 2.每向JSS中加入一个Job,都会调用
StateController.maybeStartTrackingJobLocked()方法开始记录跟踪此Job; - 3.当Job调度执行完毕后、或当Job被取消时,都会调用
StateController.maybeStopTrackingJobLocked()方法停止此Job的记录;
除了以上共同特性外,各个状态控制器中对约束的设置,大部分是通过广播实现的,状态控制器内部会注册一个广播,当广播接收器收到广播后,将根据得到的状态,通过setXXXConstraintSatisfied()方法,对satisfiedConstraints进行按位运算,比如在DeviceIdleJobsController中会调用setDeviceNotDozingConstraintSatisfied(true/false)来设置是否Doze的约束条件满足。第三篇文章分析时说过:JobStatus中有一个全局变量satisfiedConstraints,这个表示该Job当前满足的约束,如果当前状态控制器所检测到的系统状态满足某个约束,则按位或,否则清零即可。最终在判断Job约束条件是否满足时,将拿satisfiedConstraints和requiredConstraints进行比较,requiredConstraints变量上记录的是创建Job时在JobInfo设置的所有约束条件,也是通过按位或运算进行标记的:
public boolean isConstraintsSatisfied() {
//该Job所有的约束条件
final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
//该Job现在所满足的约束条件
int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
//相等,说明当前所满足约束已是所需所有约束
return (sat & req) == req;
}
下面我们就具体来看看,各个状态控制器如何获取并将结果设置给satisfiedConstraints的,这部分逻辑比较清晰易懂,就不再啰哩啰嗦了,点到为止即可。
1. DeviceIdleJobsController
DeviceIdleJobsController用来控制Job对Doze的依赖条件,或者也可以说Doze对Job的限制,当设备进入Doze模式的IDLE状态时,将会限制除了Doze白名单外的所有应用的Job调度,当Doze退出IDLE状态进入维护状态后,将会对所有应用的Job解除限制。而DeviceIdleJobsController中则是通过广播的形式来感知Doze模式的状态变化,在其构造方法中可以看到广播的注册和监听:
public DeviceIdleJobsController(JobSchedulerService service) {
super(service);
// ......
final IntentFilter filter = new IntentFilter();
// Deep Doze状态发生改变后发送
filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
// Light Doze状态发生改变后发送
filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
// 白名单列表发生变化时发送
filter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
// i临时白名单列表发生变化时发送
filter.addAction(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
mContext.registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
}
来看看关于广播接收器中的逻辑,其中只看当Doze的状态发生变化的部分:
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
// Deep Doze和Light Doze进入/退出IDLE后发送
case PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED:
case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
//根据Doze是否进入IDLE状态更新状态
updateIdleMode(mPowerManager != null && (mPowerManager.isDeviceIdleMode()
|| mPowerManager.isLightDeviceIdleMode()));
break;
}
}
};
以上逻辑中可以看到,不管是Deep Doze还是Light Doze,当进入或者退出IDLE后,执行的是同样的流程,这一方面也说明两种Doze对Job都是有延迟的。然后通过PowerManager获取到当前Doze的状态,并调用updateIdleMode()更新,updateIdleMode()方法中的处理主要如下:
- 1.Doze退出/进入时,都会进行Job的遍历,并最终会调用
updateTaskStateLocked()方法更新Job的Doze约束; - 2.如果退出Doze后,立即会处理处于前台的进程的Job,其余进程通过Handler延时3s处理;
- 3.一旦Doze状态发生改变,将通过
onDeviceIdleStateChanged()回调进入JSS中。
来看看updateTaskStateLocked():
private boolean updateTaskStateLocked(JobStatus task) {
//是否豁免Doze模式的限制:设置了标记且(对应app处于前台或app处于临时白名单列表中)
final boolean allowInIdle = ((task.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0)
&& (mForegroundUids.get(task.getSourceUid()) || isTempWhitelistedLocked(task));
//Job所属应用是否处于Doze白名单中
final boolean whitelisted = isWhitelistedLocked(task);
//Job是否允许调度:Doze模式不处于IDLE状态或应用处于白名单中或应用处于前台且带有FLAG_IMPORTANT_WHILE_FOREGROUND
final boolean enableTask = !mDeviceIdleMode || whitelisted || allowInIdle;
//设置Doze模式约束条件
return task.setDeviceNotDozingConstraintSatisfied(enableTask, whitelisted);
}
在以上方法中,判断是否这个Job可以在Doze下调度呢?并将结果通过setDeviceNotDozingConstraintSatisfied()设置给JobStatus:
boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
dozeWhitelisted = whitelisted;
//该方法用来设置或这取消约束条件
return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
}
最终,通过setDeviceNotDozingConstraintSatisfied()方法将CONSTRAINT_DEVICE_NOT_DOZING标记设置给satisfiedConstraints。
以上流程说明,当Doze模式不处于IDLE状态,或者Job所属应用处于白名单,或者Job所属应用被豁免,那么将设置为satisfiedConstraints设置CONSTRAINT_DEVICE_NOT_DOZING标记,表示此时已满足Doze的约束要求了,将不会被Doze模式所限制。在Job调度过程中,会看到关于他的判断:
//JobStatus.java:
public boolean isReady() {
//Doze模式是否处于非IDLE状态:不处于IDLE状态&&没有设置FLAG_WILL_BE_FOREGROUND标记
final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0
|| (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
return (isConstraintsSatisfied() || deadlineSatisfied) && notDozing && notRestrictedInBg;
}
在JSS的回调方法onDeviceIdleStateChanged()中,将会根据Doze状态的变化决定重新检查Job队列还是取消当前正在运行的Job。
2. IdleController
IdleController用来控制设备闲置状态对Job的约束,仅仅对设置过setRequiresDeviceIdle(true)的Job有效。
注意,这个闲置状态和Doze模式中的IDLE状态不是同一个概念,在IdleController中,会注册三类广播:亮灭屏广播、开始/结束屏保广播和无线充电广播,同时在配置文件config.xml中配置了一个阀值,规定当进入收到灭屏等广播后,到达这个阀值时,认定设备处于空闲状态。下面来看具体实现逻辑。
首先来看其构造方法:
public IdleController(JobSchedulerService service) {
super(service);
//初始化
initIdleStateTracking();
}
private void initIdleStateTracking() {
//处于闲置状态的阀值
mInactivityIdleThreshold = mContext.getResources().getInteger(
com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
//设置Alarm的窗口时间
mIdleWindowSlop = mContext.getResources().getInteger(
com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
//BroadcastReceiver子类
mIdleTracker = new IdlenessTracker();
mIdleTracker.startTracking();
}
在以上构造方法中,主要做了以下工作:
- 1.读取定义进入闲置状态的阀值,其定义如下:
<!-- Inactivity threshold (in milliseconds) used in JobScheduler. JobScheduler will consider
the device to be "idle" after being inactive for this long. -->
<integer name="config_jobSchedulerInactivityIdleThreshold">4260000</integer>
<!-- The alarm window (in milliseconds) that JobScheduler uses to enter the idle state -->
<integer name="config_jobSchedulerIdleWindowSlop">300000</integer>
- 2.初始化广播接收器并进行注册,这是在
startTracking()中完成的:
public void startTracking() {
IntentFilter filter = new IntentFilter();
// 屏幕状态广播
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
// 屏保状态广播
filter.addAction(Intent.ACTION_DREAMING_STARTED);
filter.addAction(Intent.ACTION_DREAMING_STOPPED);
// AMS发送的广播
filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
// 无线充电状态
filter.addAction(Intent.ACTION_DOCK_IDLE);
filter.addAction(Intent.ACTION_DOCK_ACTIVE);
mContext.registerReceiver(this, filter);
}
在广播接收器中的逻辑,将根据是否是限制状态来设置相关约束,并回调onControllerStateChanged()进入JSS中,重新check Job队列。:
void reportNewIdleState(boolean isIdle) {
synchronized (mLock) {
for (int i = mTrackedTasks.size()-1; i >= 0; i--) {
mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle);
}
}
mStateChangedListener.onControllerStateChanged();
}
在setIdleConstraintSatisfied()方法中:
boolean setIdleConstraintSatisfied(boolean state) {
//设置CONSTRAINT_IDLE标记
return setConstraintSatisfied(CONSTRAINT_IDLE, state);
}
在以上方法中,将根据当前是否处于闲置状态,对JobStatus中的全局变量satisfiedConstraints设置CONSTRAINT_IDLE标记。
3. TimeController
TimeController是用来控制截止时间和延迟时间对Job的约束,仅对设置了setOverrideDeadline()和setMinimumLatency()的Job有效。
在TimeController中,会根据以上两个方法设置的延迟时间和截至时间,分别设置一个Alarm,当到达警报时间后,在AlarmListener.onAlarm()方法中去更新,并在evaluateDeadlineConstraint()中更新相关标记:
private boolean evaluateDeadlineConstraint(JobStatus job, long nowElapsedMillis) {
final long jobDeadline = job.getLatestRunTimeElapsed();
//到达截至时间
if (jobDeadline <= nowElapsedMillis) {
//到达延迟调度时间
if (job.hasTimingDelayConstraint()) {
job.setTimingDelayConstraintSatisfied(true);
}
job.setDeadlineConstraintSatisfied(true);
return true;
}
return false;
}
最终,会在setTimingDelayConstraintSatisfied()中设置CONSTRAINT_TIMING_DELAY标记给satisfiedConstraints:
boolean setTimingDelayConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state);
}
会在setDeadlineConstraintSatisfied()中设置CONSTRAINT_DEADLINE标记给satisfiedConstraints:
boolean setDeadlineConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_DEADLINE, state);
}
4. BatteryController
BatteryController用来控制充电状态和低电量模式对Job的约束,仅仅对设置过setRequiresBatteryNotLow(true)或setRequiresCharging(true)的Job有效。
充电状态和低电量模式这两个状态也是通过广播进行检测的,在其构造方法中,调用startTracking()方法注册广播:
public void startTracking() {
IntentFilter filter = new IntentFilter();
// 低电量时发送
filter.addAction(Intent.ACTION_BATTERY_LOW);
// 退出低电量时发送
filter.addAction(Intent.ACTION_BATTERY_OKAY);
// 充电/放电时发送
filter.addAction(BatteryManager.ACTION_CHARGING);
filter.addAction(BatteryManager.ACTION_DISCHARGING);
mContext.registerReceiver(this, filter);
BatteryManagerInternal batteryManagerInternal =
LocalServices.getService(BatteryManagerInternal.class);
// 是否处于低电量模式
mBatteryHealthy = !batteryManagerInternal.getBatteryLevelLow();
// 是否在进行充电
mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
}
在其广播接收器中,调用maybeReportNewChargingStateLocked()方法:
private void maybeReportNewChargingStateLocked() {
// 是否 充电且不处于低电量模式
final boolean stablePower = mChargeTracker.isOnStablePower();
// 是否 不处于低电量模式
final boolean batteryNotLow = mChargeTracker.isBatteryNotLow();
boolean reportChange = false;
// 遍历跟踪的Job列表
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
// 设置充电约束条件
boolean previous = ts.setChargingConstraintSatisfied(stablePower);
if (previous != stablePower) {
reportChange = true;
}
// 设置低电量约束条件
previous = ts.setBatteryNotLowConstraintSatisfied(batteryNotLow);
if (previous != batteryNotLow) {
reportChange = true;
}
}
if (stablePower || batteryNotLow) {
// 电源状态稳定后,立即调度执行Job
mStateChangedListener.onRunJobNow(null);
} else if (reportChange) {
// 状态发生变化,通知JSS
mStateChangedListener.onControllerStateChanged();
}
}
在以上方法中,将根据当前电池状态,分别调用JobStatus的setChargingConstraintSatisfied()和setBatteryNotLowConstraintSatisfied()来设置充电约束和低电量约束:
boolean setChargingConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
}
boolean setBatteryNotLowConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state);
}
最终,将标记值CONSTRAINT_CHARGING和CONSTRAINT_BATTERY_NOT_LOW设置给satisfiedConstraints。
5. BackgroundJobsController后台限制
BackgroundJobsController用来控制Job后台的运行。由于AppStandby机制,当应用处于后台时,会进行一些功能的限制,以达到优化功耗的目的,BackgroundJobsController中正是对"当应用处于后台时限制它的Job"实现的地方。
和前几个控制器不同的是,BackgroundJobsController中监测某应用能否在后台运行Job,不是通过广播实现的,而是通过AppStateTracker和其内部接口AppStateTracker.Listener实现,而AppStateTracker是和AppStandby相关联的用来记录应用状态的类,当AppStandby中状态发生变化时,AppStateTracker.Listener的几个方法将被回调,从而更新各个应用的Job的后台约束:
//frameworks/base/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
private final Listener mForceAppStandbyListener = new Listener() {
@Override
public void updateAllJobs() {
synchronized (mLock) {
updateAllJobRestrictionsLocked();
}
}
@Override
public void updateJobsForUid(int uid, boolean isActive) {
synchronized (mLock) {
updateJobRestrictionsForUidLocked(uid, isActive);
}
}
@Override
public void updateJobsForUidPackage(int uid, String packageName, boolean isActive) {
synchronized (mLock) {
updateJobRestrictionsForUidLocked(uid, isActive);
}
}
};
}
而这几个方法中,最终都会调到updateSingleJobRestrictionLocked()中,来看看这个方法:
boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, int activeState) {
final int uid = jobStatus.getSourceUid();
final String packageName = jobStatus.getSourcePackageName();
// AppStandby是否对该Uid的Job有限制
final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName,
(jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION)
!= 0);
final boolean isActive;
if (activeState == UNKNOWN) {
isActive = mAppStateTracker.isUidActive(uid);
} else {
isActive = (activeState == KNOWN_ACTIVE);
}
//设置后台约束
boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
//设置Uid是否处于活动状态
didChange |= jobStatus.setUidActive(isActive);
return didChange;
}
最终,将会在setBackgroundNotRestrictedConstraintSatisfied()设置标记CONSTRAINT_BACKGROUND_NOT_RESTRICTED给satisfiedConstraints:
boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state);
}
6. ConnectivityController
ConnectivityController用来控制网络对Job的约束,仅仅对设置过setRequiredNetwork()或setRequiredNetworkType()的Job有效。
ConnectivityController中通过接口回调的方式获取新的网络状态:
public ConnectivityController(JobSchedulerService service) {
super(service);
// ConnectivityService提供数据连接管理服务
// NetworkPolicyManagerService提供网络策略管理服务
mConnManager = mContext.getSystemService(ConnectivityManager.class);
mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
// 注册回调接口
final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
mConnManager.registerNetworkCallback(request, mNetworkCallback);
mNetPolicyManager.registerListener(mNetPolicyListener);
}
如此一来,当网络状态发生变化后,将回调方法从而通知ConnectivityController,关于何时触发毁掉,我不清楚,这里直接来看它回调后的内容,最终会进入updateConstraintsSatisfied()中:
private boolean updateConstraintsSatisfied(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities) {
// ......
final boolean changed = jobStatus
.setConnectivityConstraintSatisfied(connected && satisfied);
jobStatus.network = network;
return changed;
}
关于这部分内容,我也是不知道啊,留着以后补充吧。总之,最后通过setConnectivityConstraintSatisfied()设置标记CONSTRAINT_CONNECTIVITY给satisfiedConstraints:
boolean setConnectivityConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
}
7. StorageController
StorageController用来控制存储空间对Job的约束,仅仅对设置过setRequiresStorageNotLow(true)的Job有效。其内部也是通过广播的形式获取设备是否处于低存储状态:
public void startTracking() {
IntentFilter filter = new IntentFilter();
// 存储空间不足时发送
filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
// 存储空间正常时发送
filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
mContext.registerReceiver(this, filter);
}
在广播接收器onReceive()方法中,将通过具体广播,得到表示当前是否处于低存储的bool值mStorageLow,然后调用maybeReportNewStorageState()方法来更新Job的存储约束:
private void maybeReportNewStorageState() {
// 是否处于非低存储空间状态
final boolean storageNotLow = mStorageTracker.isStorageNotLow();
boolean reportChange = false;
synchronized (mLock) {
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
// 更新Job约束
boolean previous = ts.setStorageNotLowConstraintSatisfied(storageNotLow);
if (previous != storageNotLow) {
reportChange = true;
}
}
}
if (reportChange) {
// 存储空间发生变化
mStateChangedListener.onControllerStateChanged();
}
if (storageNotLow) {
// 当又低存储恢复正常存储状态后,立即运行Job
mStateChangedListener.onRunJobNow(null);
}
}
最终,在setStorageNotLowConstraintSatisfied()中设置标记CONSTRAINT_STORAGE_NOT_LOW给satisfiedConstraints:
boolean setStorageNotLowConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state);
}
8. ContentObserverController
ContentObserverController用来监测content URIS对Job的约束,仅仅对设置过addTriggerContentUri(Uri)的Job有效,当该URI发生变化后,将运行Job。这个暂时还不了解,就不分析了。