Android oom_adj 详细解读

源码基于:Android R

0. 前言

在博文《oom_adj 内存水位算法剖析》一文中详细的分析了lmkd 中针对 oom_adj 内存水位的计算、使用方法,在博文《oom_adj 更新原理(1)》《oom_adj 更新原理(2)》中对Android 系统中 oom_adj 的更新原理进行了详细的剖析。通过这几篇博文我们对 oom_adj 有了更深地了解。

本文在之前博文的基础上,剖析代码细节,对每个 oom_adj 的值进行详细地解读。

1. adj 等级

首先如之前几篇博文,还是先把 oom_adj 的值列举出来,下文将对每个 oom_adj 的值进行单独地解读。 

frameworks/base/services/core/java/com/android/server/am/ProcessList.java

ADJ 等级取值说明
UNKNOWN_ADJ1001用于特定地方,一般指将要cache进程,不知道确切值
CACHED_APP_MAX_ADJ999不可见进程的最大值,进程可以无任何干扰的被杀
CACHED_APP_LMK_FIRST_ADJ950oom_adj 等级第一个允许被杀的level,不能等于CACHED_APP_MAX_ADJ
CACHED_APP_MIN_ADJ900不可见进程的最小值
SERVICE_B_ADJ800B List中的Service(较老的、使用可能性更小)
PREVIOUS_APP_ADJ700上一个App的进程(往往通过按返回键)
HOME_APP_ADJ600Home进程
SERVICE_ADJ500app service 进程,杀掉它一般不会有太大的影响
HEAVY_WEIGHT_APP_ADJ400后台的重量级进程,system/rootdir/init.rc文件中startup
BACKUP_APP_ADJ300备份进程,杀掉它不完全致命,但不好
PERCEPTIBLE_LOW_APP_ADJ250被用户或系统绑定的进程,比service 要重要,但是如果被杀掉,不会立即影响客户的感官
PERCEPTIBLE_APP_ADJ200可感知的进程,如后台music 播放
VISIBLE_APP_ADJ100可视进程
PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ50recently TOP 进程
FOREGROUND_APP_ADJ0前台进程
PERSISTENT_SERVICE_ADJ-700关联系统的进程或一个常驻进程
PERSISTENT_PROC_ADJ-800

系统常驻进程,例如telephony

绝对不想杀,但不完全致命

SYSTEM_ADJ-900系统进程
NATIVE_ADJ-1000native 进程,不归系统管理,所以没有oom_adj 适配

2. ADJ < 0

在 adj 的分级中 FOREGROUND_APP_ADJ 的值为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) 服务进程;

2.1 SYSTEM_ADJ(-900)

SYSTEM_ADJ 仅指 system_server 进程。在 SystemServer.startBootstrapServices() 过程中会调用 AMS.setSystemProcess(),而该函数中会将system_server 的maxAdj 设置为 SYSTEM_ADJ

frameworks/base/services/core/java/com/android/server/am/AMS.javapublic void setSystemProcess() {try {...synchronized (this) {ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,false,0,new HostingRecord("system"));app.setPersistent(true);app.pid = app.mPidForCompact = MY_PID;app.getWindowProcessController().setPid(MY_PID);app.maxAdj = ProcessList.SYSTEM_ADJ; //----这里设定system_server 的 maxAdjapp.makeActive(mSystemThread.getApplicationThread(), mProcessStats);addPidLocked(app);mProcessList.updateLruProcessLocked(app, false, null);updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);}}...}

在调用 updateOomAdjLocked() 函数的时候传入的是一个参数,从博文《oom_adj 更新原理(2)》中的 computeOomAdjLocked() 得知:

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javaif (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {...app.curAdj = app.maxAdj;...
}

最终的system_server 进程的 adj 被设置为 -900,从 dumpsys meminfo 中也能看到:

    153,028K: System153,028K: system (pid 1143 / adj -900)

2.2 PERSISTENT_PROC_ADJ(-800)

PERSISTENT_PROC_ADJ 的进程需要在 AndroidManifest.xml 中声明 android:persistent 属性为 true 的系统 (带有 FLAG_SYSTEM) 进程,又称 persistent 进程。对于 persistent 进程常规情况下是不会被 kill 的,一旦被kill 或者发生 crash,系统也会立即重启拉起该进程。

设定位置:

  • ProcessList.newProcessRecordLocked();
  • AMS.addAppLocked();

在新的application 启动过程中都会调用 newProcessRecordLocked(),甚至包括 system_server 进程,例如 上一节 提到的 setSystemProcess() 函数:

frameworks/base/services/core/java/com/android/server/am/AMS.javapublic void setSystemProcess() {try {...synchronized (this) {ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,false,0,new HostingRecord("system"));app.setPersistent(true);app.pid = app.mPidForCompact = MY_PID;app.getWindowProcessController().setPid(MY_PID);app.maxAdj = ProcessList.SYSTEM_ADJ; //----这里设定system_server 的 maxAdjapp.makeActive(mSystemThread.getApplicationThread(), mProcessStats);addPidLocked(app);mProcessList.updateLruProcessLocked(app, false, null);updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);}}...}

代码中第一句话就是通过 newProcessRecordLocked() 函数创建 ProcessRecord 对象。

 来看下该函数:

frameworks/base/services/core/java/com/android/server/am/ProcessList.javafinal ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,boolean isolated, int isolatedUid, HostingRecord hostingRecord) {String proc = customProcess != null ? customProcess : info.processName;final int userId = UserHandle.getUserId(info.uid);int uid = info.uid;if (isolated) {...}final ProcessRecord r = new ProcessRecord(mService, info, proc, uid);if (!mService.mBooted && !mService.mBooting&& userId == UserHandle.USER_SYSTEM&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {// The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;r.setPersistent(true);r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;}if (isolated && isolatedUid != 0) {// Special case for startIsolatedProcess (internal only) - assume the process// is required by the system server to prevent it being killed.r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;}addProcessNameLocked(r);return r;}

通过函数得知,只有在 system_server 启动时会被置为 PERSISTENT_PROC_ADJ,但在 setSystemProcess() 最后会将system_server 的 maxAdj 修改为 SYSTEM_ADJ。

如果该进程是 isolated,maxAdj 会被设为 PERSISTENT_SERVICE_ADJ,详细可以查看第 2.3 节。

来看下另一个函数 addAppLocked():

frameworks/base/services/core/java/com/android/server/am/AMS.javafinal ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,boolean disableHiddenApiChecks, boolean disableTestApiChecks,boolean mountExtStorageFull, String abiOverride, int zygotePolicyFlags) {ProcessRecord app;if (!isolated) {app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,info.uid, true);} else {app = null;}if (app == null) {app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0,new HostingRecord("added application",customProcess != null ? customProcess : info.processName));mProcessList.updateLruProcessLocked(app, false, null);updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);}...if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {app.setPersistent(true);app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;}if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {mPersistentStartingProcesses.add(app);mProcessList.startProcessLocked(app, new HostingRecord("added application",customProcess != null ? customProcess : app.processName),zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks,mountExtStorageFull, abiOverride);}return app;}

当 application 的 flag 置 FLAG_SYSTEM | FLAG_PERSISTENT 时,maxAdj 的值才被设为 PERSISENT_PROC_ADJ。

在SystemServer 启动 startOtherServices() 中会调用 AMS.systemReady() 进行boot 的 完成阶段,此处会调用 startPersistentApps():

frameworks/base/services/core/java/com/android/server/am/AMS.javavoid startPersistentApps(int matchFlags) {if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;synchronized (this) {try {final List<ApplicationInfo> apps = AppGlobals.getPackageManager().getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();for (ApplicationInfo app : apps) {if (!"android".equals(app.packageName)) {addAppLocked(app, null, false, null /* ABI override */,ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);}}} catch (RemoteException ex) {}}}

这里加载系统的 persistent 进程,调用 addAppLocked() 函数。

2.3 PERSISTENT_SERVICE_ADJ(-700)

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

我们在上一文 newProcessRecordLocked() 中已经提到过了,当进程为 isolated 时,maxAdj 会被设置为 PERSISTENT_SERVICE_ADJ,而newProcessRecordLocked() 触发的流程如下:

AMS.startIsolatedProcess() -> ProcessList.startProcessLocked() ->newProcessRecordLocked()

另外一种就是在 updateOomAdjLocked() 中会调用 computeOomAdjLocked():

  frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javaprivate final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,boolean computeClients) {...int capabilityFromFGS = 0; // capability from foreground service.for (int is = app.numberOfRunningServices() - 1;is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND|| procState > PROCESS_STATE_TOP);is--) {ServiceRecord s = app.getRunningServiceAt(is);...ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = s.getConnections();for (int conni = serviceConnections.size() - 1;conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND|| procState > PROCESS_STATE_TOP);conni--) {ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni);for (int i = 0;i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND|| procState > PROCESS_STATE_TOP);i++) {...int clientAdj = client.getCurRawAdj();int clientProcState = client.getCurRawProcState();if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {...if (adj > clientAdj) {if (app.hasShownUi && !app.getCachedIsHomeProcess()&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {adjType = "cch-bound-ui-services";}} else {int newAdj;if ((cr.flags&(Context.BIND_ABOVE_CLIENT|Context.BIND_IMPORTANT)) != 0) {if (clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {newAdj = clientAdj;} else {newAdj = ProcessList.PERSISTENT_SERVICE_ADJ;...}}

当前进程绑定有client,且当前进程 adj 优先级还没有 client 优先级高时,确定是否设置了 BIND_ABOVE_CLIENT 或 BIND_IMPORTANT 的flags

3. FOREGROUND_APP_ADJ(0)

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

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

 详细代码可以查看博文《oom_adj 更新原理(2)》中的 computeOomAdjLocked() :

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javaprivate final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,boolean computeClients) {...boolean foregroundActivities = false;if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) {// The last app on the list is the foreground app.adj = ProcessList.FOREGROUND_APP_ADJ;...} else if (app.runningRemoteAnimation) {adj = ProcessList.VISIBLE_APP_ADJ;...} else if (app.getActiveInstrumentation() != null) {adj = ProcessList.FOREGROUND_APP_ADJ;...} else if (app.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) {adj = ProcessList.FOREGROUND_APP_ADJ;...} else if (app.executingServices.size() > 0) {adj = ProcessList.FOREGROUND_APP_ADJ;...} else if (app == topApp) {adj = ProcessList.FOREGROUND_APP_ADJ;...} else {procState = PROCESS_STATE_CACHED_EMPTY;...}

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

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javaprivate final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,boolean computeClients) {...final ActivityServiceConnectionsHolder a = cr.activity;if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ&& a.isActivityVisible()) {adj = ProcessList.FOREGROUND_APP_ADJ;app.setCurRawAdj(adj);

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

  • 当Provider的客户端进程ADJ<=FOREGROUND_APP_ADJ时,则Provider进程ADJ等于FOREGROUND_APP_ADJ
  • 当Provider有外部(非框架)进程依赖,也就是调用了getContentProviderExternal()方法,则ADJ至少等于FOREGROUND_APP_ADJ

4. VISIBLE_APP_ADJ(100)

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

从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() 方法。

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

5. PERCEPTIBLE_APP_ADJ(200)

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

在 computeOomAdjLocked() 函数中对于可感知进程也有专门的计算:

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javaprivate final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,boolean computeClients) {.../********* 场景 1 **********/// Examine all activities if not already foreground.if (!foregroundActivities && app.getCachedHasActivities()) {app.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback,adj, foregroundActivities, procState, schedGroup, appUid, logUid,PROCESS_STATE_CUR_TOP);adj = app.mCachedAdj;...}.../********* 场景 2 **********/if (adj > ProcessList.PERCEPTIBLE_APP_ADJ|| procState > PROCESS_STATE_FOREGROUND_SERVICE) {if (app.hasForegroundServices()) {// The user is aware of this app, so make it visible.adj = ProcessList.PERCEPTIBLE_APP_ADJ;procState = PROCESS_STATE_FOREGROUND_SERVICE;app.adjType = "fg-service";app.setCached(false);schedGroup = ProcessList.SCHED_GROUP_DEFAULT;} else if (app.hasOverlayUi()) {// The process is display an overlay UI.adj = ProcessList.PERCEPTIBLE_APP_ADJ;procState = PROCESS_STATE_IMPORTANT_FOREGROUND;app.setCached(false);app.adjType = "has-overlay-ui";schedGroup = ProcessList.SCHED_GROUP_DEFAULT;}}.../********* 场景 3 **********/if (adj > ProcessList.PERCEPTIBLE_APP_ADJ|| procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {if (app.forcingToImportant != null) {adj = ProcessList.PERCEPTIBLE_APP_ADJ;procState = PROCESS_STATE_TRANSIENT_BACKGROUND;app.setCached(false);app.adjType = "force-imp";app.adjSource = app.forcingToImportant;schedGroup = ProcessList.SCHED_GROUP_DEFAULT;}}

场景1:如果不是前台进程,且存在cache activities,则需要继续确定adj

调用 computeOomAdjFromActivitiesIfNecessary(),这里是在计算 oom_adj 时,传入一个 callback,通过 window 来确认activity 处于什么状态 visible 还是PAUSING、STOPPING 或其他状态,并根据状态调用 callback 对应的回调函数。

  • visible:adj 如果大于 VISIBLE_APP_ADJ,则会被拉回 VISIBLE_APP_ADJ;
  • PAUSING:adj 如果大于 PERCEPTIBLE_APP_ADJ,则会被拉回 PERCEPTIBLE_APP_ADJ;
  • STOPPING:adj 如果大于 PERCEPTIBLE_APP_ADJ,则会被拉回 PERCEPTIBLE_APP_ADJ;

场景2:进程拥有前台services 或进程用有 overlay UI

前提调价是进程重要性已经低于可感知级别,或者进程状态级别高于带前台service

拥有前台services,是执行 startForegroundService() 函数了;

hasOverlayUi() 为true,表示非activity 的UI 位于屏幕最顶层,例如显示类型  TYPE_APPLICATION_OVERLAY 的窗口。

场景3:进程forcingToImportant 非空

该值非空,表示执行了 setProcessImportant() 函数,例如 Toast 弹出过程;

6. BACKUP_APP_ADJ(300)

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javaprivate final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,boolean computeClients) {...final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId);if (backupTarget != null && app == backupTarget.app) {// If possible we want to avoid killing apps while they're being backed upif (adj > ProcessList.BACKUP_APP_ADJ) {if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);adj = ProcessList.BACKUP_APP_ADJ;if (procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {procState = PROCESS_STATE_TRANSIENT_BACKGROUND;}app.adjType = "backup";app.setCached(false);}if (procState > ActivityManager.PROCESS_STATE_BACKUP) {procState = ActivityManager.PROCESS_STATE_BACKUP;app.adjType = "backup";}}

前提条件是 backupTarget 不为空:

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

7. HEAVY_WEIGHT_APP_ADJ(400)

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javaprivate final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,boolean computeClients) {...//重量级后台进程,把adj和proc_state都拉回到 heavy weight水平if (app.getCachedIsHeavyWeight()) {if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {// We don't want to kill the current heavy-weight process.adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;app.setCached(false);app.adjType = "heavy";}if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;app.adjType = "heavy";}}
  • realStartActivityLocked()过程,当应用的privateFlags标识PRIVATE_FLAG_CANT_SAVE_STATE,设置mHeavyWeightProcess值;
  • finishHeavyWeightApp(), 置空mHeavyWeightProcess值;

8. HOME_APP_ADJ(600)

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javaprivate final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,boolean computeClients) {...//HOME 进程,将其拉回HOME_APP_ADJif (app.getCachedIsHomeProcess()) {if (adj > ProcessList.HOME_APP_ADJ) {// This process is hosting what we currently consider to be the// home app, so we don't want to let it go into the background.adj = ProcessList.HOME_APP_ADJ;schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;app.setCached(false);app.adjType = "home";}if (procState > ActivityManager.PROCESS_STATE_HOME) {procState = ActivityManager.PROCESS_STATE_HOME;app.adjType = "home";}}

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

9. PREVIOUS_APP_ADJ(700)

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.javaprivate final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,boolean computeClients) {.../********* 场景 1 **********/if (app.getCachedIsPreviousProcess() && app.getCachedHasActivities()) {if (adj > ProcessList.PREVIOUS_APP_ADJ) {// This was the previous process that showed UI to the user.// We want to try to keep it around more aggressively, to give// a good experience around switching between two apps.adj = ProcessList.PREVIOUS_APP_ADJ;schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;app.setCached(false);app.adjType = "previous";}if (procState > PROCESS_STATE_LAST_ACTIVITY) {procState = PROCESS_STATE_LAST_ACTIVITY;app.adjType = "previous";}}.../********* 场景 2 **********/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.setCached(false);app.adjType = "recent-provider";}if (procState > PROCESS_STATE_LAST_ACTIVITY) {procState = PROCESS_STATE_LAST_ACTIVITY;app.adjType = "recent-provider";}}

在 computeOomAdjLocked() 函数用有两种场景。

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/2667.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Centos 7 安装 Oracle 11G

Oracle 11G 安装教程 准备环境 p13390677_112040_Linux-x86-64_1of7.zipp13390677_112040_Linux-x86-64_2of7.zipCentos 7- rhel7-英文版的系统–不想换语言的执行(LANGen_US)– 传输 文件到服务器上 创建用户和组 [rootlocalhost ~]# groupadd oracle [rootlocalhost ~]…

Windows11 C盘瘦身

1.符号链接 将大文件夹移动到其他盘&#xff0c;创建成符号链接 2.修改Android Studio路径设置 1.SDK路径 2.Gradle路径 3.模拟器路径 设置环境变量 ANDROID_SDK_HOME

基于单片机的盲人导航智能拐杖老人防丢防摔倒发短息定位

功能介绍 以STM32单片机作为主控系统&#xff1b; OLED液晶当前实时距离&#xff0c;安全距离&#xff0c;当前经纬度信息&#xff1b;超声波检测小于设置的安全距离&#xff0c;蜂鸣器报警提示&#xff1a;低于安全距离&#xff01;超声波检测当前障碍物距离&#xff0c;GPS进…

python发送邮件yagmail库

yagmail库发送邮件简洁&#xff0c;代码量少 import yagmaildef send_yagmail(sender, send_password, addressee, hostsmtp.qq.com, port465):yag yagmail.SMTP(sender, send_password, host, port)img_url https://img2.baidu.com/it/u483398814,2966849709&fm253&…

基于单片机的智能空调系统的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;LCD1602液晶显示当前水温&#xff0c;定时提醒&#xff0c;水量变化DS18B20检测当前水体温度&#xff1b;水位传感器检测当前水位&#xff1b;继电器驱动加热片进行水温加热&#xff1b;定时提醒喝水&#xff0c;蜂鸣器报警&#x…

LeetCode面试题02.07.链表相交

面试题02.07.链表相交 两种解题思路 面试题02.07.链表相交一、双指针二、哈希集合 一、双指针 这道题简单来说&#xff0c;就是求两个链表交点节点的指针 这里注意&#xff1a;交点不是数值相等&#xff0c;而是指针相等 为了方便举例&#xff0c;假设节点元素数值相等&…

用Python采用Modbus-Tcp的方式读取485电子水尺数据

README.TXT 2023/6/15 V1.0 实现了单个点位数据通信、数据解析、数据存储 2023/6/17 V2.0 实现了多个点位数据通信、数据解析、数据存储 2023/6/19 V2.1 完善log存储&#xff0c;仅保留近3天的log记录&#xff0c;避免不必要的存储&#xff1b;限制log大小&#xff0c;2MB。架…

数字原生时代,奥哲如何让企业都成为“原住民”?

22年前&#xff0c;美国教育学家马克‧普伦斯基&#xff08;Marc Prensky&#xff09;出版了《数字原生与数字移民》&#xff08;Digital Natives, Digital Immigrants&#xff09;一书&#xff0c;首次提出了“数字原住民”和“数字移民”两大概念&#xff0c;用来定义跨时代的…

【数据结构】_1.集合与复杂度

目录 1. 集合框架 2. 时间复杂度 2.1 时间复杂度和空间复杂度 2.2 时间复杂度的概念 2.3 大O的渐进表示法 2.3.1 精确的时间复杂度表达式 2.3.2 大O渐进表示法的三条规则 2.3.3 时间复杂度的最好、平均与最坏情况 2.4 时间复杂度计算示例 3.空间复杂度 1. 集合框架 …

字节跳动后端面试,笔试部分

var code "7022f444-ded0-477c-9afe-26812ca8e7cb" 背景 笔者在刷B站的时候&#xff0c;看到了一个关于面试的实录&#xff0c;前半段是八股文&#xff0c;后半段是笔试部分&#xff0c;感觉笔试部分的题目还是挺有意思的&#xff0c;特此记录一下。 笔试部分 问…

【多线程例题】顺序打印abc线程

顺序打印-进阶版 方法一&#xff1a;三个线程竞争同一个锁&#xff0c;通过count判断是否打印 方法二&#xff1a;三个线程同时start&#xff0c;分别上锁&#xff0c;从a开始&#xff0c;打印后唤醒b 三个线程分别打印A&#xff0c;B&#xff0c;C 方法一&#xff1a;通过co…

JavaFX中MVC例子理解

JavaFX可以让你使用GUI组件创建桌面应用程序。一个GUI应用程序执行三个任务&#xff1a;接受用户的输入&#xff0c;处理输入&#xff0c;并显示输出。而一个GUI应用程序包含两个 类型的代码&#xff1a; 领域代码。处理特定领域的数据和遵循业务规范。交互代码。处理用户输入…

【Linux】多线程(上)

本文详细介绍了多线程的常见概念 生产者消费者模型将在多线程&#xff08;下&#xff09;继续讲解 欢迎大家指正 提起讨论进步啊 目录 多线程的理解 线程的优点 线程的缺点&#xff1a; 线程的用途 线程VS进程 用户级线程库 POSIX线程库 线程创建&#xff1a; 线程…

springboot整合jwt

JWT介绍 JWT是JSON Web Token的缩写&#xff0c;即JSON Web令牌&#xff0c;是一种自包含令牌。 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。 JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息&#xff0c;以便于从资源服务器获…

基于.net6的WPF程序使用SignalR进行通信

之前写的SignalR通信&#xff0c;是基于.net6api&#xff0c;BS和CS进行通信的。 .net6API使用SignalRvue3聊天WPF聊天_signalr wpf_故里2130的博客-CSDN博客 今天写一篇关于CS客户端的SignalR通信&#xff0c;后台服务使用.net6api 。其实和之前写的差不多&#xff0c;主要在…

Ubuntu22.04密码忘记怎么办 Ubuntu重置root密码方法

在Ubuntu 22.04 或其他更高版本上不小心忘记root或其他账户的密码怎么办&#xff1f; 首先uname -r查看当前系统正在使用的内核版本&#xff0c;记下来 前提&#xff1a;是你的本地电脑&#xff0c;有物理访问权限。其他如远程登录的不适用这套改密方法。 通过以下步骤&#…

写字楼/办公楼能源管理系统的具体应用 安科瑞 许敏

0 引言 随着社会的进步&#xff0c;我国经济的快速发展&#xff0c;企业的办公环境和方式发生了巨大的变化&#xff0c;专业的写字楼在各大城市遍布林立。写字楼的出现使得各地企业办公集中化、高效化&#xff0c;然而写字楼物业管理的同步发展对于企业服务来说更是一个很大的…

SciencePub学术 | 区块链类重点SCIEEI征稿中

SciencePub学术 刊源推荐: 区块链类重点SCIE&EI征稿中&#xff01;信息如下&#xff0c;录满为止&#xff1a; 一、期刊概况&#xff1a; SCI-01 【期刊简介】IF&#xff1a;4.0-4.5&#xff0c;JCR2区&#xff0c;中科院3区&#xff1b; 【检索情况】SCIE&EI双检&…

Mysql+ETLCloud CDC+StarRocks实时数仓同步实战

一、业务需求及其痛点 大型企业需要对各种业务系统中的销售及营销数据进行实时同步分析&#xff0c;例如库存信息、对帐信号、会员信息、广告投放信息&#xff0c;生产进度信息等等&#xff0c;这些统计分析信息可以实时同步到StarRocks中进行分析和统计&#xff0c;StarRocks…

vue注意点:$attrs、$slots!插槽

$attrs 当父组件给子组件传值&#xff0c;子组件并没有接收数据时&#xff0c;此时数据在$attrs中可以拿到&#xff0c;并且如果子组件不需要使用数据&#xff0c;而孙组件需要&#xff0c;则可以直接v-bind"$attrs"传给孙。 <-- 父组件 --> <div><…