Android 原生开发:系统解析之进程优先级

本文基于原生 Android 9.0 源码来解读进程优先级原理.

一、概述

1.1 进程

Android 框架对进程创建与管理进行了封装,对于 APP开发者只需知道 Android 四大组件的使用。当 Activity, Service, ContentProvider, BroadcastReceiver 任一组件启动时,当其所承载的进程存在则直接使用,不存在则由框架代码自动调用 startProcessLocked 创建进程。一个 APP 可以拥有多个进程,多个 APP 也可以运行在同一个进程,通过配置 Android:process 属性来决定。所以说对 APP 来说进程几乎是透明的,但了解进程对于深刻理解 Android 系统是至关关键的。

1.2 优先级

Android 系统的设计理念正是希望应用进程能尽量长时间地存活,以提升用户体验。应用首次打开比较慢,这个过程有进程创建以及 Application 等信息的初始化,所以应用在启动之后,即便退到后台并非立刻杀死,而是存活一段时间,这样下次再使用则会非常快。对于APP同样希望自身尽可能存活更长的时间,甚至探索各种保活黑科技。物极必反,系统处于低内存的状态下,手机性能会有所下降;系统继续放任所有进程一直存活,系统内存很快就会枯竭而亡,那么需要合理地进程回收机制。

到底该回收哪个进程呢?系统根据进程的组件状态来决定每个进程的优先级值 ADJ,系统根据一定策略先杀优先级最低的进程,然后逐步杀优先级更低的进程,依此类推,以回收预期的可用系统资源,从而保证系统正常运转。

谈到优先级,可能有些人会想到 Linux 进程本身有 nice 值,这个能决定 CPU 资源调度的优先级;而本文介绍 Android 系统中的 ADJ,主要决定在什么场景下什么类型的进程可能会被杀,影响的是进程存活时间。ADJ 与 nice 值两者定位不同,不过也有一定的联系,优先级很高的进程,往往也是用户不希望被杀的进程,是具有有一定正相关性。

1.3 ADJ级别

ADJ级别 取值 含义
NATIVE_ADJ -1000 native进程
SYSTEM_ADJ -900 仅指system_server进程
PERSISTENT_PROC_ADJ -800 系统persistent进程
PERSISTENT_SERVICE_ADJ -700 关联着系统或persistent进程
FOREGROUND_APP_ADJ 0 前台进程
VISIBLE_APP_ADJ 100 可见进程
PERCEPTIBLE_APP_ADJ 200 可感知进程,比如后台音乐播放
BACKUP_APP_ADJ 300 备份进程
HEAVY_WEIGHT_APP_ADJ 400 重量级进程
SERVICE_ADJ 500 服务进程
HOME_APP_ADJ 600 Home进程
PREVIOUS_APP_ADJ 700 上一个进程
SERVICE_B_ADJ 800 B List中的Service
CACHED_APP_MIN_ADJ 900 不可见进程的adj最小值
CACHED_APP_MAX_ADJ 906 不可见进程的adj最大值

从 Android 7.0 开始,ADJ 采用100、200、300;在这之前的版本 ADJ 采用数字 1、2、3,这样的调整可以更进一步地细化进程的优先级,比如在 VISIBLE_APP_ADJ(100)PERCEPTIBLE_APP_ADJ(200)之间,可以有 ADJ=101、102 级别的进程。

  • 省去 lmk 对 oom_score_adj 的计算过程,Android 7.0 之前的版本,oom_score_adj= oom_adj * 1000/17; 而Android 7.0开始,oom_score_adj= oom_adj,不用再经过一次转换。

1.4 LMK

为了防止剩余内存过低,Android 在内核空间有 lowmemorykiller (简称 LMK ), LMK 是通过注册 shrinker 来触发低内存回收的,这个机制并不太优雅,可能会拖慢 Shrinkers 内存扫描速度,已从内核 4.12 中移除,后续会采用用户空间的 LMKD + memory cgroups 机制,这里先不展开 LMK 讲解。

进程刚启动时 ADJ 等于 INVALID_ADJ,当执行完attachApplication(),该该进程的 curAdj 和 setAdj 不相等,则会触发执行 setOomAdj() 将该进程的节点/proc/pid/oom_score_adj写入oomadj值。下图参数为 Android 原生阈值,当系统剩余空闲内存低于某阈值(比如147MB),则从 ADJ 大于或等于相应阈值(比如 900)的进程中,选择 ADJ 值最大的进程,如果存在多个 ADJ 相同的进程,则选择内存最大的进程。 如下是 64 位机器, LMK 默认阈值图:

lmk_adj

在 updateOomLevels() 过程,会根据手机屏幕尺寸或内存大小来调整 scale,默认大多数手机内存都大于 700MB,则 scale 等于1。对于 64 位手机,阈值会更大些,具体如下。

private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
    ...
    for (int i=0; i<mOomAdj.length; i++) {
        int low = mOomMinFreeLow[i];
        int high = mOomMinFreeHigh[i];
        if (is64bit) {
            if (i == 4) high = (high*3)/2;
            else if (i == 5) high = (high*7)/4;
        }
        mOomMinFree[i] = (int)(low + ((high-low)*scale));
    }
}

二、解读ADJ

接下来,解读每个 ADJ 值都对应着怎样条件的进程,包括正在运行的组件以及这些组件的状态几何。这里重点介绍上图标红的 ADJ 级别所对应的进程。

Androi d系统中计算各进程 ADJ 算法的核心方法:

  • updateOomAdjLocked:更新 adj,当目标进程为空或者被杀则返回 false;否则返回 true;
  • computeOomAdjLocked:计算 adj,返回计算后 RawAdj 值;
  • applyOomAdjLocked:应用 adj,当需要杀掉目标进程则返回 false;否则返回 true。

当Android四大组件状态改变时会 updateOomAdjLocked() 来同步更新相应进程的 ADJ 优先级。这里需要说明一下,当同一个进程有多个决定其优先级的组件状态时,取优先级最高的 ADJ 作为最终的 ADJ。另外,进程会通过设置 maxAdj 来限定 ADJ 的上限。

关于分析进程ADJ相关信息,常用命令如下:

  • dumpsys meminfo,
  • dumpsys activity o
  • dumpsys activity p

2.0 ADJ<0的进程

  • NATIVE_ADJ(-1000):是由 init 进程 fork 出来的 Native 进程,并不受 system 管控;
  • SYSTEM_ADJ(-900):是指 system_server 进程;
  • PERSISTENT_PROC_ADJ(-800): 是指在 AndroidManifest.xml 中申明 android:persistent=”true”的系统(即带有 FLAG_SYSTEM 标记)进程,persistent 进程一般情况并不会被杀,即便被杀或者发生 Crash 系统会立即重新拉起该进程。
  • PERSISTENT_SERVICE_ADJ(-700):是由 startIsolatedProcess() 方式启动的进程,或者是由system_server 或者 persistent 进程所绑定(并且带有 BIND_ABOVE_CLIENT 或者 BIND_IMPORTANT )的服务进程

再来说一下其他优先级:

  • BACKUP_APP_ADJ(300):执行 bindBackupAgent() 过程的进程
  • HEAVY_WEIGHT_APP_ADJ(400): realStartActivityLocked() 过程,当应用的 privateFlags 标识 PRIVATE_FLAG_CANT_SAVE_STATE 的进程;
  • HOME_APP_ADJ(600):当类型为 ACTIVITY_TYPE_HOME 的应用,比如桌面 APP
  • PREVIOUS_APP_ADJ(700):用户上一个使用的APP进程

SYSTEM_ADJ(-900)

SYSTEM_ADJ: 仅指 system_server 进程。在执行 SystemServer 的 startBootstrapServices() 过程会调用 AMS.setSystemProcess(),将 system_server 进程的 maxAdj 设置成 SYSTEM_ADJ,源码如下:

public void setSystemProcess() {
    ...
    ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
            "android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
    mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
    synchronized (this) {
        ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
        app.persistent = true;
        app.pid = MY_PID;
        app.maxAdj = ProcessList.SYSTEM_ADJ;
        app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
        synchronized (mPidsSelfLocked) {
            mPidsSelfLocked.put(app.pid, app);
        }
        updateLruProcessLocked(app, false, null);
        updateOomAdjLocked();
    }
    ...
}

PERSISTENT_PROC_ADJ(-800)

PERSISTENT_PROC_ADJ:在 AndroidManifest.xml 中申明 android:persistent=”true” 的系统(即带有FLAG_SYSTEM 标记)进程,称之为 persistent 进程。对于 persistent 进程常规情况都不会被杀,一旦被杀或者发生 Crash,进程会立即重启。

AMS.addAppLocked()或 AMS.newProcessRecordLocked() 过程会赋值:

场景1: newProcessRecordLocked

final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess, boolean isolated, int isolatedUid) {
  String proc = customProcess != null ? customProcess : info.processName;
  final int userId = UserHandle.getUserId(info.uid);
  int uid = info.uid;
  ...
  final ProcessRecord r = new ProcessRecord(stats, info, proc, uid);
  if (!mBooted && !mBooting
          && userId == UserHandle.USER_SYSTEM
          && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
      r.persistent = true;
      r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
  }
  if (isolated && isolatedUid != 0) {
      r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
  }
  return r;
}

在每一次进程启动的时候都会判断该进程是否persistent进程,如果是则会设置maxAdj=PERSISTENT_PROC_ADJ。 system_server 进程应该也是 persistent 进程?

场景2:addAppLocked

final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, String abiOverride) {
    ProcessRecord app;
    if (!isolated) {
        app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
                info.uid, true);
    } else {
        app = null;
    }

    if (app == null) {
        app = newProcessRecordLocked(info, customProcess, isolated, 0);
        updateLruProcessLocked(app, false, null);
        updateOomAdjLocked();
    }
    ...

    if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
        app.persistent = true;
        app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
    }
    if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
        mPersistentStartingProcesses.add(app);
        startProcessLocked(app, "added application",
                customProcess != null ? customProcess : app.processName, abiOverride);
    }
    return app;
}

开机过程会先启动persistent进程,并赋予 maxAdj 为 PERSISTENT_PROC_ADJ,调用链:

startOtherServices()
  AMS.systemReady
    AMS.startPersistentApps
      AMS.addAppLocked

PERSISTENT_SERVICE_ADJ(-700)

PERSISTENT_SERVICE_ADJ: startIsolatedProcess()方式启动的进程,或者是由 system_server 或者 persistent 进程所绑定的服务进程。

场景1:newProcessRecordLocked

final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess, boolean isolated, int isolatedUid) {
  String proc = customProcess != null ? customProcess : info.processName;
  final int userId = UserHandle.getUserId(info.uid);
  int uid = info.uid;
  ...
  final ProcessRecord r = new ProcessRecord(stats, info, proc, uid);
  if (!mBooted && !mBooting
          && userId == UserHandle.USER_SYSTEM
          && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
      r.persistent = true;
      r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
  }
  if (isolated && isolatedUid != 0) { //startIsolatedProcess
      r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
  }
  return r;
}

调用链:

startOtherServices
  WebViewUpdateService.prepareWebViewInSystemServer
    WebViewUpdateServiceImpl.prepareWebViewInSystemServer
      WebViewUpdater.prepareWebViewInSystemServer
        WebViewUpdater.onWebViewProviderChanged
          SystemImpl.onWebViewProviderChanged
            WebViewFactory.onWebViewProviderChanged
              WebViewLibraryLoader.prepareNativeLibraries
                WebViewLibraryLoader.createRelros
                  WebViewLibraryLoader.createRelroFile
                    AMS.startIsolatedProcess

BACKUP_APP_ADJ(300)

if (mBackupTarget != null && app == mBackupTarget.app) {
    if (adj > ProcessList.BACKUP_APP_ADJ) {
        adj = ProcessList.BACKUP_APP_ADJ;
        if (procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
            procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
        }
        app.adjType = "backup";
        app.cached = false;
    }
    if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
        procState = ActivityManager.PROCESS_STATE_BACKUP;
        app.adjType = "backup";
    }
}

  • 执行 bindBackupAgent() 过程,设置 mBackupTarget 值;
  • 执行 clearPendingBackup() 或 unbindBackupAgent() 过程,置空 mBackupTarget 值;

HEAVY_WEIGHT_APP_ADJ(400)

  • realStartActivityLocked() 过程,当应用的 privateFlags 标识 PRIVATE_FLAG_CANT_SAVE_STATE,设置 mHeavyWeightProcess 值;
  • finishHeavyWeightApp(), 置空 mHeavyWeightProcess 值;

HOME_APP_ADJ(600)

当类型为 ACTIVITY_TYPE_HOME 的应用启动后会设置 mHomeProcess,比如桌面 APP。

PREVIOUS_APP_ADJ(700)

场景1:用户上一个使用的包含 UI 的进程,为了给用户在两个 APP 之间更好的切换体验,将上一个进程 ADJ 设置到 PREVIOUS_APP_ADJ 的档次。 当 activityStoppedLocked() 过程会更新上一个应用。

if (app == mPreviousProcess && app.activities.size() > 0) {
    if (adj > ProcessList.PREVIOUS_APP_ADJ) {
        adj = ProcessList.PREVIOUS_APP_ADJ;
        schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
        app.cached = false;
        app.adjType = "previous";
    }
    if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
        procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
        app.adjType = "previous";
    }
}

场景2: 当 provider 进程,上一次使用时间不超过 20S 的情况下,优先级不低于 PREVIOUS_APP_ADJ。provider 进程这个是 Android 7.0 以后新增的逻辑 ,这样做的好处是在内存比较低的情况下避免拥有 provider 的进程出现颠簸,也就是启动后杀,然后又被拉。

if (app.lastProviderTime > 0 &&
        (app.lastProviderTime+mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
    if (adj > ProcessList.PREVIOUS_APP_ADJ) {
        adj = ProcessList.PREVIOUS_APP_ADJ;
        schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
        app.cached = false;
        app.adjType = "recent-provider";
    }
    if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
        procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
        app.adjType = "recent-provider";
    }
}

2.1 FOREGROUND_APP_ADJ(0)

场景1:满足以下任一条件的进程都属于 FOREGROUND_APP_ADJ(0) 优先级:

  • 正处于resumed状态的Activity
  • 正执行一个生命周期回调的Service(比如执行 onCreate , onStartCommand, onDestroy等)
  • 正执行 onReceive() 的 BroadcastReceiver
  • 通过 startInstrumentation() 启动的进程

源码如下:

if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP) {
    adj = ProcessList.FOREGROUND_APP_ADJ;
    schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
    app.adjType = "top-activity";
    foregroundActivities = true;
    procState = PROCESS_STATE_CUR_TOP;
} else if (app.instr != null) {
    adj = ProcessList.FOREGROUND_APP_ADJ;
    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
    app.adjType = "instrumentation";
    procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
} else if (isReceivingBroadcastLocked(app, mTmpBroadcastQueue)) {
    adj = ProcessList.FOREGROUND_APP_ADJ;
    schedGroup = (mTmpBroadcastQueue.contains(mFgBroadcastQueue))
            ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
    app.adjType = "broadcast";
    procState = ActivityManager.PROCESS_STATE_RECEIVER;
} else if (app.executingServices.size() > 0) {
    adj = ProcessList.FOREGROUND_APP_ADJ;
    schedGroup = app.execServicesFg ?
            ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
    app.adjType = "exec-service";
    procState = ActivityManager.PROCESS_STATE_SERVICE;
} else if (app == TOP_APP) {
    adj = ProcessList.FOREGROUND_APP_ADJ;
    schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
    app.adjType = "top-sleeping";
    foregroundActivities = true;
    procState = PROCESS_STATE_CUR_TOP;
} else {
    schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
    adj = cachedAdj;
    procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
    app.cached = true;
    app.empty = true;
    app.adjType = "cch-empty";
}

场景2: 当客户端进程 activity 里面调用 bindService() 方法时 flags 带有 BIND_ADJUST_WITH_ACTIVITY 参数,并且该 activity 处于可见状态,则当前服务进程也属于前台进程,源码如下:

for (int is = app.services.size()-1; is >= 0; is--) {
    ServiceRecord s = app.services.valueAt(is);
    for (int conni = s.connections.size()-1; conni >= 0; conni--) {
        ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
        for (int i = 0; i < clist.size(); i++) {
            ConnectionRecord cr = clist.get(i);
            if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
              ...
            }

            final ActivityRecord a = cr.activity;
            if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
                if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ &&
                    (a.visible || a.state == ActivityState.RESUMED ||
                     a.state == ActivityState.PAUSING)) {
                    adj = ProcessList.FOREGROUND_APP_ADJ;
                    if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
                        if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
                            schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
                        } else {
                            schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                        }
                    }
                    app.cached = false;
                    app.adjType = "service";
                    app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                            .REASON_SERVICE_IN_USE;
                    app.adjSource = a;
                    app.adjSourceProcState = procState;
                    app.adjTarget = s.name;
                }
            }
        }
    }
}

provider客户端

场景3: 对于 provider 进程,还有以下两个条件能成为前台进程:

  • 当 Provider 的客户端进程 ADJ<=FOREGROUND_APP_ADJ 时,则 Provider 进程 ADJ 等于 FOREGROUND_APP_ADJ
  • 当Provider有外部(非框架)进程依赖,也就是调用了 getContentProviderExternal() 方法,则 ADJ 至少等于 FOREGROUND_APP_ADJ
for (int provi = app.pubProviders.size()-1; provi >= 0; provi--) {
    ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
    //根据client来调整provider进程的adj和procState
    for (int i = cpr.connections.size()-1; i >= 0; i--) {
        ContentProviderConnection conn = cpr.connections.get(i);
        ProcessRecord client = conn.client;
        int clientAdj = computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
        if (adj > clientAdj) {
            if (app.hasShownUi && app != mHomeProcess
                    && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                ...
            } else {
                adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
                        ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
                adjType = "provider";
            }
            app.cached &= client.cached;
        }
        ...
    }
    //根据provider外部依赖情况来调整adj和schedGroup
    if (cpr.hasExternalProcessHandles()) {
         if (adj > ProcessList.FOREGROUND_APP_ADJ) {
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
             app.cached = false;
             app.adjType = "ext-provider";
             app.adjTarget = cpr.name;
         }
     }
}

2.2 VISIBLE_APP_ADJ(100)

可见进程:当 ActivityRecord 的 visible=true,也就是 Activity 可见的进程。

if (!foregroundActivities && activitiesSize > 0) {
    int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;
    for (int j = 0; j < activitiesSize; j++) {
        final ActivityRecord r = app.activities.get(j);
        if (r.visible) {
            if (adj > ProcessList.VISIBLE_APP_ADJ) {
                adj = ProcessList.VISIBLE_APP_ADJ;
                app.adjType = "vis-activity";
            }
            if (procState > PROCESS_STATE_CUR_TOP) {
                procState = PROCESS_STATE_CUR_TOP;
                app.adjType = "vis-activity";
            }
            schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
            app.cached = false;
            app.empty = false;
            foregroundActivities = true;
            final TaskRecord task = r.getTask();
            if (task != null && minLayer > 0) {
                final int layer = task.mLayerRank;
                if (layer >= 0 && minLayer > layer) {
                    minLayer = layer;
                }
            }
            break;
        }
        ...
    }
    if (adj == ProcessList.VISIBLE_APP_ADJ) {
        adj += minLayer;
    }
}

从 Android P 开始,进一步细化 ADJ 级别,增加了 VISIBLE_APP_LAYER_MAX(99) ,是指VISIBLE_APP_ADJ(100)跟PERCEPTIBLE_APP_ADJ(200) 之间有 99 个槽,则可见级别 ADJ 的取值范围为 [100,199]。 算法会根据其所在 task 的 mLayerRank 来调整其 ADJ,100 加上 mLayerRank 就等于目标 ADJ,layer 越大,则 ADJ 越小。

关于 TaskRecord 的 mLayerRank 的计算方式是在 updateOomAdjLocked() 过程调用 ASS 的 rankTaskLayersIfNeeded() 方法,如下:

[-> ActivityStackSupervisor.java]
void rankTaskLayersIfNeeded() {
    if (!mTaskLayersChanged) {
        return;
    }
    mTaskLayersChanged = false;
    for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
        final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
        int baseLayer = 0;
        for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
            final ActivityStack stack = display.getChildAt(stackNdx);
            baseLayer += stack.rankTaskLayers(baseLayer);
        }
    }
}

[-> ActivityStack.java]
final int rankTaskLayers(int baseLayer) {
    int layer = 0;
    for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
        final TaskRecord task = mTaskHistory.get(taskNdx);
        ActivityRecord r = task.topRunningActivityLocked();
        if (r == null || r.finishing || !r.visible) {
            task.mLayerRank = -1;
        } else {
            task.mLayerRank = baseLayer + layer++;
        }
    }
    return layer;
}

当 TaskRecord 顶部的 ActivityRecord 为空或者结束或者不可见时,则设置该 TaskRecord 的 mLayerRank 等于 -1; 每个 ActivityDisplay 的 baseLayer 都是从 0 开始,从最上面的 TaskRecord 开始,第一个 ADJ=100,从上至下依次加 1,直到 199 为上限。

visible_adj_layer

service客户端

ServiceRecord 的成员变量 startRequested=true,是指被显式调用了 startService() 方法。当 service 被 stop 或 kill 会将其置为 false。

一般情况下,即便客户端进程处于前台进程 (ADJ=0) 级别,服务进程只会提升到可见 (ADJ=1) 级别。以下 flags 是由调用 bindService() 过程所传递的 flags 来决定的。

flag 含义
BIND_WAIVE_PRIORITY 是指客户端进程的优先级不会影响目标服务进程的优先级。比如当调用bindService又不希望提升目标服务进程的优先级的情况下,可以使用该flags
BIND_ADJUST_WITH_ACTIVITY 是指当从Activity绑定到该进程时,允许目标服务进程根据该activity的可见性来提升优先级
BIND_ABOVE_CLIENT 当客户端进程绑定到一个服务进程时,则服务进程比客户端进程更重要
BIND_IMPORTANT 标记该服务对于客户端进程很重要,当客户端进程处于前台进程(ADJ=0)级别时,会把服务进程也提升到前台进程级别
BIND_NOT_VISIBLE 当客户端进程处于可见(ADJ=1)级别,也不允许被绑定的服务进程提升到可见级别,该类服务进程的优先级上限为可感知(ADJ=2)级别
BIND_NOT_FOREGROUND 不允许被绑定的服务进程提升到前台调度优先级,但是内存优先级可以提升到前台级别。比如不希望服务进程占用

作为工程师很多时候可能还是想看看源码 。但是关于 ADJ 计算这一块源码场景 computeOomAdjLocked() , 为了更清晰地说明客户端进程如何影响服务进程,在保证不失去原意的情况下重写了这块部分逻辑:

这个过程主要根据 service 本身、client 端情况以及 activity 状态分别来调整 adj 和 schedGroup

for (int is = app.services.size()-1; is >= 0; is--) {
    ServiceRecord s = app.services.valueAt(is);
    if (s.startRequested) {
        ... // 根据service本身调整adj和adjType
    }
    for (int conni = s.connections.size()-1; conni >= 0; conni--) {
        ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
        for (int i = 0; i < clist.size(); i++) {
            ConnectionRecord cr = clist.get(i);
            //根据client端来调整adj
            if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
                if (adj > clientAdj) {
                    if (app.hasShownUi && app != mHomeProcess
                            && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                        ...
                    } else {
                        int newAdj = clientAdj;
                        if ((cr.flags&(Context.BIND_ABOVE_CLIENT
                                |Context.BIND_IMPORTANT)) != 0) {
                            if(clientAdj < ProcessList.PERSISTENT_SERVICE_ADJ) {
                                newAdj = PERSISTENT_SERVICE_ADJ;
                            }
                        } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0) {
                            if(clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ) {
                                newAdj = PERCEPTIBLE_APP_ADJ;
                            }
                        } else {
                            if (clientAdj < ProcessList.VISIBLE_APP_ADJ) {
                                newAdj = VISIBLE_APP_ADJ;
                            }
                        }

                        if (adj > newAdj) {
                            adj = newAdj;
                            adjType = "service";
                        }
                    }
                }
            }
            final ActivityRecord a = cr.activity;
            // 根据client的activity来调整adj和schedGroup
            if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
                ...
            }
        }
    }
}

上段代码说明服务端进程优先级(adj)不会低于客户端进程优先级(newAdj),而newAdj的上限受限于flags,具体服务端进程受客户端进程影响的ADJ上限如下:

  • BIND_ABOVE_CLIENTBIND_IMPORTANT的情况下,ADJ 上限为 PERSISTENT_SERVICE_ADJ
  • BIND_NOT_VISIBLE 的情况下, ADJ 上限为PERCEPTIBLE_APP_ADJ
  • 否则,一般情况下,ADJ 上限为 VISIBLE_APP_ADJ

由此,可见当 bindService 过程带有 BIND_ABOVE_CLIENT 或者 BIND_IMPORTANT flags 的同时,客户端进程 ADJ 小于或等于 PERSISTENT_SERVICE_ADJ 的情况下,该进程则为 PERSISTENT_SERVICE_ADJ。另外,即便是启动过 Activity 的进程,当客户端进程 ADJ<=200 时,还是可以提升该服务进程的优先级。

2.3 PERCEPTIBLE_APP_ADJ(200)

可感知进程:当该进程存在不可见的 Activity,但 Activity 正处于 PAUSING、PAUSED、STOPPING 状态,则为 PERCEPTIBLE_APP_ADJ

if (!foregroundActivities && activitiesSize > 0) {
    int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;
    for (int j = 0; j < activitiesSize; j++) {
        final ActivityRecord r = app.activities.get(j);
        if (r.visible) {
            ...
        } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                app.adjType = "pause-activity";
            }
            if (procState > PROCESS_STATE_CUR_TOP) {
                procState = PROCESS_STATE_CUR_TOP;
                app.adjType = "pause-activity";
            }
            schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
            app.cached = false;
            app.empty = false;
            foregroundActivities = true;
        } else if (r.state == ActivityState.STOPPING) {
            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                app.adjType = "stop-activity";
            }

            if (!r.finishing) {
                if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
                    procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
                    app.adjType = "stop-activity";
                }
            }
            app.cached = false;
            app.empty = false;
            foregroundActivities = true;
        }
    }
}

满足以下任一条件的进程也属于可感知进程:

  • foregroundServices 非空:前台服务进程,执行 startForegroundService() 方法
  • app.forcingToImportant 非空:执行setProcessImportant()方法,比如 Toast 弹出过程。
  • hasOverlayUi 非空:非 activity 的 UI 位于屏幕最顶层,比如显示类型 TYPE_APPLICATION_OVERLAY 的窗口
还有 38% 的精彩内容
©著作权归作者所有,转载或内容合作请联系作者
支付 ¥9.99 继续阅读
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容