一、城市管理法则:ADJ 的基本概念
在 Android 城市中,每个进程都是城市的 "居民",而 ADJ(Adjustment)则是居民的 "优先级标签"。系统作为 "城市管理员",会根据 ADJ 标签决定资源分配和进程清理顺序。
1.1 居民分类:ADJ 等级体系
| ADJ 等级 | 标签值 | 居民类型 | 待遇(存活优先级) |
|---|---|---|---|
| NATIVE_ADJ | -1000 | 原生系统进程 | 最高优先级,几乎不被杀 |
| SYSTEM_ADJ | -900 | system_server 进程 | 城市核心管理者,永不被杀 |
| FOREGROUND_APP_ADJ | 0 | 前台活跃应用 | 用户正在使用,优先分配资源 |
| VISIBLE_APP_ADJ | 100 | 可见但非前台应用 | 界面可见,较难被杀 |
| PERCEPTIBLE_APP_ADJ | 200 | 可感知进程(如后台音乐) | 用户可感知,中等优先级 |
| SERVICE_ADJ | 500 | 服务进程 | 后台服务,默认优先级 |
| CACHED_APP_ADJ | 900+ | 缓存进程 | 最低优先级,优先被清理 |
1.2 清理工 LMK:基于 ADJ 的资源回收
城市的 "清理工"LMK(Low Memory Killer)会根据 ADJ 标签清理进程:
-
当内存不足时,优先清理 ADJ 值高的进程(如缓存进程)
-
ADJ≤0 的核心进程几乎不会被清理
-
清理阈值随内存大小动态调整,64 位设备阈值更高
java
// LMK清理逻辑简化示意
void lmkCleanup() {
// 内存阈值表(单位MB)
int[] memoryThresholds = {73, 92, 110, 129, 221, 332};
int[] adjLevels = {0, 100, 200, 300, 900, 906};
int freeMemory = getFreeMemory();
for (int i = memoryThresholds.length - 1; i >= 0; i--) {
if (freeMemory < memoryThresholds[i]) {
killProcessesWithAdj(adjLevels[i]); // 杀ADJ≥指定值的进程
break;
}
}
}
二、居民待遇:各 ADJ 等级的 "生存法则"
2.1 顶级特权居民:ADJ≤0 的核心群体
SYSTEM_ADJ (-900):城市管理者 system_server
-
城市的核心管理者,负责调度所有居民
-
启动时通过
AMS.setSystemProcess()设置 ADJ -
拥有最高权限,永不被杀
java
// system_server启动时设置ADJ
public void setSystemProcess() {
ProcessRecord app = newProcessRecordLocked(...);
app.persistent = true;
app.maxAdj = ProcessList.SYSTEM_ADJ; // 设置为-900
updateOomAdjLocked();
}
FOREGROUND_APP_ADJ (0):前台活跃居民
-
用户正在交互的应用,如当前显示的 Activity
-
触发条件:Activity 处于 resumed 状态、服务正在执行生命周期回调
java
// 前台进程判断逻辑
boolean isForeground(ProcessRecord app) {
// 场景1:有resumed状态的Activity
if (app.topActivity.state == ActivityState.RESUMED) {
return true;
}
// 场景2:正在执行服务回调
if (app.executingServices.size() > 0) {
return true;
}
// 场景3:正在接收广播
if (isReceivingBroadcastLocked(app)) {
return true;
}
return false;
}
2.2 中级待遇居民:ADJ=100-500 的普通群体
VISIBLE_APP_ADJ (100):可见但非前台
-
Activity 可见但未处于前台(如被对话框部分遮挡)
-
Android P 引入更细粒度分级,根据 Task 层级调整 ADJ 值
java
// 可见进程ADJ计算
void calculateVisibleAdj(ProcessRecord app) {
int minLayer = 99; // VISIBLE_APP_LAYER_MAX
for (ActivityRecord activity : app.activities) {
if (activity.visible) {
// 根据Task层级调整ADJ
TaskRecord task = activity.getTask();
if (task != null) {
app.curAdj = ProcessList.VISIBLE_APP_ADJ + task.mLayerRank;
} else {
app.curAdj = ProcessList.VISIBLE_APP_ADJ;
}
break;
}
}
}
SERVICE_ADJ (500):后台服务进程
-
没有 UI 界面的服务进程,如后台数据同步
-
30 分钟内活跃过的服务会维持较低 ADJ
java
// 服务进程ADJ判断
void calculateServiceAdj(ProcessRecord app) {
for (ServiceRecord service : app.services) {
if (service.startRequested) { // 服务正在运行
long inactivityTime = System.currentTimeMillis() - service.lastActivity;
if (inactivityTime < MAX_SERVICE_INACTIVITY) { // 30分钟内活跃过
app.curAdj = ProcessList.SERVICE_ADJ;
break;
}
}
}
}
2.3 底层待遇居民:ADJ≥900 的缓存群体
CACHED_APP_ADJ (900+):缓存进程
-
长时间未使用的进程,分为两类:
- 有 Activity 的缓存进程(ADJ=900,902,904)
- 空进程(ADJ=901,903,905)
-
系统会限制缓存进程数量(默认 32 个),超过则清理
java
// 缓存进程清理逻辑
void cleanupCachedProcesses() {
int maxCached = mConstants.CUR_MAX_CACHED_PROCESSES; // 默认32
int numCached = getCachedProcessCount();
if (numCached > maxCached) {
// 按LRU顺序杀ADJ最高的缓存进程
killProcessesByLruOrder(ProcessList.CACHED_APP_MIN_ADJ);
}
}
三、城市管理策略:ADJ 与资源调度的关系
3.1 调度组:CPU 资源分配的 "优先级队列"
ADJ 决定进程的 CPU 调度组,类似居民的 "资源获取权限":
-
SCHED_GROUP_TOP_APP (2):前台进程,优先获取 CPU
-
SCHED_GROUP_DEFAULT (1):普通前台进程,中等资源
-
SCHED_GROUP_BACKGROUND (0):后台进程,最少资源
java
// 调度组设置逻辑
void setProcessSchedGroup(ProcessRecord app) {
if (app.curAdj == ProcessList.FOREGROUND_APP_ADJ) {
app.schedGroup = ProcessList.SCHED_GROUP_TOP_APP; // 最高调度组
} else if (app.curAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
app.schedGroup = ProcessList.SCHED_GROUP_DEFAULT; // 前台调度组
} else {
app.schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; // 后台调度组
}
}
3.2 内存因子:动态调整的 "城市压力指数"
系统根据缓存进程数量计算 "内存因子",决定清理强度:
-
ADJ_MEM_FACTOR_CRITICAL (3):内存极度紧张,优先清理
-
ADJ_MEM_FACTOR_NORMAL (0):内存充足,较少清理
java
// 内存因子计算
int calculateMemoryFactor() {
int numCached = getCachedProcessCount();
int numEmpty = getEmptyProcessCount();
int total = numCached + numEmpty;
if (total <= TRIM_CRITICAL_THRESHOLD) { // ≤3
return ADJ_MEM_FACTOR_CRITICAL;
} else if (total <= TRIM_LOW_THRESHOLD) { // ≤5
return ADJ_MEM_FACTOR_LOW;
} else if (numCached <= 5 && numEmpty <= 8) {
return ADJ_MEM_FACTOR_MODERATE;
} else {
return ADJ_MEM_FACTOR_NORMAL;
}
}
四、开发者指南:如何优化应用的 "城市待遇"
4.1 进程分离:UI 与服务隔离
xml
<!-- 配置独立服务进程 -->
<service
android:name=".BackgroundService"
android:process=":service" />
- 避免 UI 进程与服务进程混合,防止 UI 进入后台时服务被连带清理
- 独立服务进程 ADJ=500,比缓存进程 (900+) 更难被杀
4.2 前台服务:提升 ADJ 的 "通行证"
java
// 启动前台服务(如音乐播放)
public void startForegroundService() {
Intent serviceIntent = new Intent(this, MusicService.class);
startForegroundService(serviceIntent); // 提升ADJ到200
// 在服务中设置通知
Notification notification = new Notification.Builder(this)
.setContentTitle("音乐播放")
.setSmallIcon(R.drawable.ic_music)
.build();
startForeground(NOTIFICATION_ID, notification);
}
- 前台服务 ADJ=PERCEPTIBLE_APP_ADJ (200),仅次于前台进程
- 必须显示通知,否则会被系统惩罚
4.3 资源释放:遵守城市 "清理规则"
java
// 实现内存清理回调
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level >= TRIM_MEMORY_MODERATE) {
// 释放非必要资源
clearCaches();
} else if (level >= TRIM_MEMORY_COMPLETE) {
// 释放所有可重建资源
releaseHeavyResources();
}
}
- 系统内存紧张时会回调
onTrimMemory - 及时释放资源可降低进程被杀概率
五、城市管理总结:ADJ 算法的核心逻辑
-
ADJ 决定存活优先级:值越低越难被杀,核心进程 ADJ≤0
-
动态调整机制:根据内存状态和进程活跃度动态计算 ADJ
-
调度组与 ADJ 关联:高 ADJ 进程获得更多 CPU 资源
-
开发者优化方向:
-
合理使用前台服务提升 ADJ
-
分离 UI 与服务进程
-
及时释放资源响应系统回调
-
通过理解 ADJ 算法,开发者可以让应用在 Android 城市中获得更优的 "居住待遇",同时避免过度占用资源,维持城市的整体稳定。