基于 Android 7.1.1 源码分析:
前言
我们想象这样的场景,如果有一个应用,它 schedule 了一些 job, 这些 job 可能正在运行,可能在 pending!这个时候,用户卸载了这个应用,那这个应用对应的 job 该何去何从?这里就要涉及到 package change 对 job 的影响了!
同时,进程优先级的变化,其实也会影响进程内的 job 的优先级!
我们回到 JSS 的启动和初始化:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public void onBootPhase(int phase) {
if (PHASE_SYSTEM_SERVICES_READY == phase) {
mConstants.start(getContext().getContentResolver());
// 注册广播,监听应用程序包的操作!
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
filter.addDataScheme("package");
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
// 注册广播,监听设备用户的操作!
final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
try {
// 注册 uid 监控者
ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
| ActivityManager.UID_OBSERVER_IDLE);
} catch (RemoteException e) {
// ignored; both services live in system_server
}
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
... ... ... ...
}
}
可以看到,这段代码,注册了两个广播监听者,下面我们一一来看!
这里有个小细节,如果想监听程序的安装和删除的广播,需要静态或者动态配置以下属性:1
2<data android:scheme="package">
</data>
或者:1
filter.addDataScheme("package");
1 Package Change
我们回到 JobSchedulerService 服务中去,在 JSS 中,有一个广播接收者,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112/**
* Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
* still clean up. On reinstall the package will have a new uid.
*/
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (DEBUG) {
Slog.d(TAG, "Receieved: " + action);
}
if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { // 应用程序包改变的广播!
// Purge the app's jobs if the whole package was just disabled. When this is
// the case the component name will be a bare package name.
final String pkgName = getPackageName(intent);
final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (pkgName != null && pkgUid != -1) {
// 获得改变的应用程序组件。
final String[] changedComponents = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
if (changedComponents != null) {
for (String component : changedComponents) {
if (component.equals(pkgName)) {
if (DEBUG) {
Slog.d(TAG, "Package state change: " + pkgName);
}
try {
final int userId = UserHandle.getUserId(pkgUid);
IPackageManager pm = AppGlobals.getPackageManager();
// 获得这个 package 的状态!
final int state = pm.getApplicationEnabledSetting(pkgName, userId);
if (state == COMPONENT_ENABLED_STATE_DISABLED
|| state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
if (DEBUG) {
Slog.d(TAG, "Removing jobs for package " + pkgName
+ " in user " + userId);
}
// 第二个参数表示,强制取消!
cancelJobsForUid(pkgUid, true);
}
} catch (RemoteException|IllegalArgumentException e) {
... ... ... ... ...
}
break;
}
}
}
} else {
Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
}
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { // 应用程序被移除的广播!
// If this is an outright uninstall rather than the first half of an
// app update sequence, cancel the jobs associated with the app.
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (DEBUG) {
Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
}
cancelJobsForUid(uidRemoved, true);
}
} else if (Intent.ACTION_USER_REMOVED.equals(action)) { // 设备用户被移除的广播!
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
if (DEBUG) {
Slog.d(TAG, "Removing jobs for user: " + userId);
}
cancelJobsForUser(userId);
} else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { // package 被启用的广播!
// Has this package scheduled any jobs, such that we will take action
// if it were to be force-stopped?
final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
final String pkgName = intent.getData().getSchemeSpecificPart();
if (pkgUid != -1) {
List<JobStatus> jobsForUid;
synchronized (mLock) {
jobsForUid = mJobs.getJobsByUid(pkgUid);
}
for (int i = jobsForUid.size() - 1; i >= 0; i--) {
if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
if (DEBUG) {
Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
+ pkgUid + " has jobs");
}
setResultCode(Activity.RESULT_OK);
break;
}
}
}
} else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) { // package 被重启 的广播!
// possible force-stop
final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
final String pkgName = intent.getData().getSchemeSpecificPart();
if (pkgUid != -1) {
if (DEBUG) {
Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
}
cancelJobsForPackageAndUid(pkgName, pkgUid);
}
}
}
};
我们来总结一下:
监听的广播有下面的这些:
package 相关
- Intent.ACTION_PACKAGE_CHANGED:应用程序包发生改变的广播!
- Intent.ACTION_PACKAGE_REMOVED:删除应用程序包的广播!
- Intent.ACTION_QUERY_PACKAGE_RESTART:
- Intent.ACTION_PACKAGE_RESTARTED:
user 相关
- Intent.ACTION_USER_REMOVED:设备用户被移除的广播!
但不管怎样,最后都会到了 cancel 相关的代码了!
2 Uid Change
接着来,是监听进程 uid 的变化,依赖于 JSS 内部的一个观察者:mUidObserver:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18final private IUidObserver mUidObserver = new IUidObserver.Stub() {
// 传入的参数是 uid 和 uid 所属进程的状态 !
public void onUidStateChanged(int uid, int procState) throws RemoteException { // 进程状态变化!
updateUidState(uid, procState);
}
public void onUidGone(int uid) throws RemoteException { // 进程被杀了!
updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
}
public void onUidActive(int uid) throws RemoteException { // 进程活跃!
}
public void onUidIdle(int uid) throws RemoteException { // 进程空闲!
cancelJobsForUid(uid, false);
}
};
这里面调用了一个 方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18void updateUidState(int uid, int procState) {
synchronized (mLock) {
if (procState == ActivityManager.PROCESS_STATE_TOP) {
// Only use this if we are exactly the top app. All others can live
// with just the foreground priority. This means that persistent processes
// can never be the top app priority... that is fine.
mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
} else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
} else {
mUidPriorityOverride.delete(uid);
}
}
}
可以看到,这里当应用的进程的状态发生变化时,需要对其进程的状态和uid进行记录:1
2
3
4/**
* Which uids are currently in the foreground.
*/
final SparseIntArray mUidPriorityOverride = new SparseIntArray();
这个稀疏数组用来保存进程的 uid 和进程的状态的,当某个应用的进程变为前台进程,每次变化就会被保存到这个集合中!
还记得在 schedule 篇中,我们讲到,对于同一个 uid 和 jobId 的 job,如果优先级不同,会发生优先级高的替换优先级低的 job 的情况,这里会用到如下的方法:
1 | private int adjustJobPriority(int curPriority, JobStatus job) { |
这个方式是为了给 job 计算合适的优先级!
这里就不细说哦,可以去看 schedule 篇!