[LMKD] [Android] 进程OomAdj调整分析:OomAdjuster分析(3)

一.简要说明

| 🍎 1. OomAdjuster核心作用就是调整进程---服务端或客户端的oomAdj值和进程状态,分别处理了各种场景:灭屏,activity不可见,activity回到后台,正在播放动画,近期使用的Service,前台和后台service,内容提供者,正在接收广播的进程... 2. 其主要入口就是`mOomAdjuster.updateOomAdjLocked(app, oomAdjReason)`,由ASM进行调用(唯一调用者) 3.代码位于**frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java** 4. 核心方法:`updateOomAdjLSP`,`updateOomAdjInnerLSP`,`computeOomAdjLSP`,`applyOomAdjLSP`

二.源码分析

java 复制代码
  	/**
     * Update OomAdj for specific process and its reachable processes (with direction/indirect
     * bindings from this process); Note its clients' proc state won't be re-evaluated if this proc
     * is hosting any service/content provider.
     *
     * @param app The process to update, or null to update all processes
     * @param oomAdjReason
     */
    @GuardedBy("mService")
    boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
        synchronized (mProcLock) {
            return updateOomAdjLSP(app, oomAdjReason);
        }
    }

传递2个参数,第一个是进程实体,第二个是调整的意思,整个方法有一把锁mService,而方法内也持有一把锁ActivityManagerGlobalLock mProcLock,继续看看updateOomAdjLSP

java 复制代码
  	@GuardedBy({"mService", "mProcLock"})//两把锁
    private boolean updateOomAdjLSP(ProcessRecord app, String oomAdjReason) {
        // 应用启动流程,在AMS中会调用到这里来,ProcessRecord刚创建出来,ActivityThread进程还没fork出来
        // 这里的app是ProcessRecord,不为空,所以不走这里
        if (app == null || !mConstants.OOMADJ_UPDATE_QUICK) {
            updateOomAdjLSP(oomAdjReason);
            return true;
        }

        if (checkAndEnqueueOomAdjTargetLocked(app)) {
            // Simply return true as there is an oomAdjUpdate ongoing
            return true;
        }

        try {
            // 走这
            **mOomAdjUpdateOngoing** = true;
            **return performUpdateOomAdjLSP(app, oomAdjReason);**
        } finally {
            // Kick off the handling of any pending targets enqueued during the above update
            mOomAdjUpdateOngoing = false;
            updateOomAdjPendingTargetsLocked(oomAdjReason);
        }
    }

继续看看performUpdateOomAdjLSP(两个参数)方法

java 复制代码
@GuardedBy({"mService", "mProcLock"})
    private boolean performUpdateOomAdjLSP(ProcessRecord app, String oomAdjReason) {// odj调整leilei
        // 获取顶部activity---通常正在运行
        final ProcessRecord topApp = mService.getTopApp();

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
        // oomAdj调整开始的标志,记录了开始的时间和mOomAdjStarted为true
        mService.mOomAdjProfiler.oomAdjStarted();
        // 调整的序列号++,该序列号会用于判断调整是否完成
        mAdjSeq++;

        // Firstly, try to see if the importance of itself gets changed
        // 获取要调整进程的进程状态,ProcessStateRecord保存了该进程状态
        final ProcessStateRecord state = app.mState;
        // 是否属于cache进程,属于cache进程代表进程随时被杀死或者已经杀死
        final boolean wasCached = state.isCached();
        // 先记录之前该app进程的优先级,getCurRawAdj代表获取的原始adj--原始adj代表没有被约束过的进程adj值--进程adj值是会被约束的
        final int oldAdj = state.getCurRawAdj();
        // 如果oldAdj >=900,说明属于Empty或cache进程
        final int cachedAdj = oldAdj >= ProcessList.CACHED_APP_MIN_ADJ
                ? oldAdj : ProcessList.UNKNOWN_ADJ;
        // 是否属于后台进程---调整的是进程,而非单个对象
        final boolean wasBackground = ActivityManager.isProcStateBackground(
                state.getSetProcState());
        // 记录进程能力,例如可访问网络,可访问地理位置等能力
        final int oldCap = state.getSetCapability();
        // 是否重复计算进程,也就是进程计算oom adj不止一次,需要计算好几次
        state.setContainsCycle(false);
        state.setProcStateChanged(false);
        state.resetCachedInfo();
        // Check if this process is in the pending list too, remove from pending list if so.
        // 如果进程属于挂起状态(待运行启动状态),则从该集合中移除
        mPendingProcessSet.remove(app);
        // 进入另一个performUpdateOomAdjLSP,主要目的是调用computeOomAdjLSP,计算进程adj优先级
        // 计算过后通过apply应用计算出来的adj值,也就是会调用 ProcessList.setOomAdj(),然后修改state.setCurAdj(),应用进程的adj
        boolean success = **performUpdateOomAdjLSP**(app, cachedAdj, topApp,
                SystemClock.uptimeMillis());
			 ...
}

继续看看performUpdateOomAdjLSP(四个参数)方法

java 复制代码
  	@GuardedBy({"mService", "mProcLock"})
    private boolean performUpdateOomAdjLSP(ProcessRecord app/*待调整的进程*/, int cachedAdj/*可能是[900,999]或者无效值1001*/,
            ProcessRecord topApp/*当前的topapp*/, long now/*oom adj调整的时间*/) {
        // 当进程还没有fork出来的时候,不做调整,结束
        if (app.getThread() == null) {
            return false;
        }
				...
        // 计算oom adj的核心方法---代码超过1k行
				// computeClients代表调整当前进程的时候,也同时调整客户端进程状态,因为Service是有客户端连接的,广播是有客户端发送,内容提供者亦如此
        **computeOomAdjLSP**(app, cachedAdj, topApp, false/*doingALl*/, now, false/*cycleReEval循环计算*/, true/*computeClients*/);
        ...
        // 应用由computeOomAdjLSP计算完成后的oom adj和进程状态,这里面会进行状态约束和oomAdj约束
        return **applyOomAdjLSP**(app, false, now, SystemClock.elapsedRealtime());
    }

这里主要分析**computeOomAdjLSP applyOomAdjLSP**两个非常重要的方法

  • computeOomAdjLSP

    这个方法非常复杂,具体如下分析

    java 复制代码
    // leilei关键代码
        @GuardedBy({"mService", "mProcLock"})
        private boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj/*默认值*/,
                ProcessRecord topApp, boolean doingAll/*false*/, long now, boolean cycleReEval/*false*/,
                boolean computeClients/*true*/) {
            final ProcessStateRecord state = app.mState;
            // 每次adj调整都会自增序列id(updateOomAdjInnerLSP/performUpdateOomAdjLSP会自增),如果序列id相等,代表这个进程已经参与过本次调整
            if (mAdjSeq == state.getAdjSeq()) {
                // 当前adj已经调整完成,则直接终止
                if (state.getAdjSeq() == state.getCompletedAdjSeq()) {
                    // This adjustment has already been computed successfully.
                    return false;
                } else {
                    // The process is being computed, so there is a cycle. We cannot
                    // rely on this process's state.
                    // 设置为循环调整adj
                    state.setContainsCycle(true);
                    // 这个集合就是要循环调整adj
                    mProcessesInCycle.add(app);
    
                    return false;
                }
            }
    
            // 因为new ProcessRecord的时候就会走到这里来,但是ActivityThread还没创建,此时会走这里
            if (app.getThread() == null) {
                // 设置当前的seq,如果下次调整发现state.getAdjSeq() == mAdjSeq
                // 代表此次调整完成,如果又发现getAdjSeq == getCompletedAdjSeq,则代表adj调整完成
                // 所以每次进程调整adj,都对应了一个mAdjSeq
                state.setAdjSeq(mAdjSeq);
                // 设置进程分组,属于SCHED_GROUP_BACKGROUND组
                state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
                // 设置进程状态为Empty(19,倒数第二优先级,倒数第一是NONEXISTENT)
                state.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
                // 设置当前进程adj优先级为999,它可以在没有任何干扰的情况下被杀死
                state.setCurAdj(ProcessList.CACHED_APP_MAX_ADJ);
                state.setCurRawAdj(ProcessList.CACHED_APP_MAX_ADJ);
                // 相当于将mAdjSeq赋值给setCompletedAdjSeq,代表调整完成
                state.setCompletedAdjSeq(state.getAdjSeq());
                // 设置进程能力为0,代表没有任何功能
                state.setCurCapability(PROCESS_CAPABILITY_NONE);
                return false;
            }
    
            // 设置默认值
            state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN);
            state.setAdjSource(null);
            state.setAdjTarget(null);
            state.setEmpty(false);
            state.setCached(false);
            // 重置allow start fgs state值为:PROCESS_STATE_NONEXISTENT
            state.resetAllowStartFgsState();
            // 非循环计算,设置进程可悲被冻结
            if (!cycleReEval) {
                // Don't reset this flag when doing cycles re-evaluation.
                app.mOptRecord.setShouldNotFreeze(false);
            }
    
            // 进程端的uid
            final int appUid = app.info.uid;
            final int logUid = mService.mCurOomAdjUid;
    
            // 记录当前的进程adj,通过下文会和当前的adj进行比较
            int prevAppAdj = state.getCurAdj();// 当前adj---被约束过的adj,值越小,保活率越高
            int prevProcState = state.getCurProcState();// 当前进程状态,值越小,保活率越高
            int prevCapability = state.getCurCapability();// 进程的能力,例如访问位置信息,相机等
            final ProcessServiceRecord psr = app.mServices;// 该进程的所有service集合,可能没有,与之对应的是Process{Provider/Receiver}Record
    
            // 一般系统服务的maxAdj才会小于等于FOREGROUND_APP_ADJ
            if (state.getMaxAdj() <= ProcessList.FOREGROUND_APP_ADJ) {
                // The max adjustment doesn't allow this app to be anything
                // below foreground, so it is not worth doing work for it.
                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app);
                }
                // 小于等于0的进程,都属于比较重要的进程
                // 所以type调整为fixed,为固定不可调整
                state.setAdjType("fixed");
                state.setAdjSeq(mAdjSeq);
                // 将进程之前设置最大adj(在processList#newProcessRecordLocked方法中
                // 会将系统进程设置setMaxAdj(ProcessList.PERSISTENT_PROC_ADJ))
                state.setCurRawAdj(state.getMaxAdj());
                // 默认是没有前景activity(默认值)
                state.setHasForegroundActivities(false);
                // 给的默认分组--分组分为SCHED_GROUP_BACKGROUND,SCHED_GROUP_TOP_APP,这两个比较常见
                state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
                // 此类进程应用拥有最大的能力,CAMERA,MICROPHONE,NETWORK,LOCATION
                state.setCurCapability(PROCESS_CAPABILITY_ALL);
                // 设置进程状态,系统进程都设置为进程持久化状态
                state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
                // System processes can do UI, and when they do we want to have
                // them trim their memory after the user leaves the UI.  To
                // facilitate this, here we need to determine whether or not it
                // is currently showing UI.
                // 系统进程默认没有ui界面,也算个初始值
                state.setSystemNoUi(true);
                // 如果调整的是当前应用是最顶端的activity
                if (app == topApp) {
                    // 这类进程是带有ui的
                    state.setSystemNoUi(false);
                    // 可以在/dev/cpuset/top-app查看该分组的信息,例如top app的pid等
                    // 这里设置分组为TOP_APP
                    state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
                    // 设置adj类型
                    state.setAdjType("pers-top-activity");
                } else if (state.hasTopUi()) {// 例如下拉状态栏,锁屏界面
                    // sched group/proc state adjustment is below
                    // 例如下拉状态栏,锁屏界面,也带有UI
                    state.setSystemNoUi(false);
                    state.setAdjType("pers-top-ui");
                } else if (state.getCachedHasVisibleActivities()) {// 如果有可见的activity,不论是否前台
                    // 设置为有UI
                    state.setSystemNoUi(false);
                }
                //只有带有界面的常驻进程才会进入这里面(如systemui会灭屏幕进入;phone进程灭屏由于isSystemNoUi是true所以不进入)
                if (!state.isSystemNoUi()) {// 带有界面的进程
                    // 如果是亮屏状态
                    if (mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE
                            || state.isRunningRemoteAnimation()) {
                        // screen on or animating, promote UI
                        // 如果是亮屏状态或者播放动画状态,提升进程状态为 常驻进程UI
                        state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
                        // 分组提升到TOP APP
                        state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
                    } else {
                        // screen off, restrict UI scheduling
                        // 如果是灭屏,进程状态切换到PROCESS_STATE_BOUND_FOREGROUND_SERVICE
                        state.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
                        // 分组切换到RESTRICTED,存在限制的进程分组
                        state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
                    }
                }
                // 设置原始的进程状态--还未被限制
                state.setCurRawProcState(state.getCurProcState());
                // 可以看到系统进程,默认都没有调整adj。而是直接指定当时的最大值adj
                state.setCurAdj(state.getMaxAdj());
                // 设置调整完成后的序列号
                state.setCompletedAdjSeq(state.getAdjSeq());
                // 代表是否允许启动前台服务
                state.bumpAllowStartFgsState(state.getCurProcState());
                // if curAdj is less than prevAppAdj, then this process was promoted
                // 只有调整后的优先级更小,或者进程状态比之前的更新,才算是有效调整
                return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState;
            }
    
            // 非系统进程会继续走到这里,默认设置没有ui
            state.setSystemNoUi(false);
    
            // 获取top进程的状态,TOP_SLEEPING或者TOP
            final int PROCESS_STATE_CUR_TOP = mService.mAtmInternal.getTopProcessState();
    
            // Determine the importance of the process, starting with most
            // important to least, and assign an appropriate OOM adjustment.
            int adj;//进程adj
            int schedGroup;//分组
            int procState;//进程状态
            int cachedAdjSeq;
            int capability = 0;//进程能力
    
            // 默认没有前景activity,默认activity不可见
            boolean foregroundActivities = false;
            boolean hasVisibleActivities = false;
            // 如果当前有top app,并且当前是唤醒状态(如亮屏),PROCESS_STATE_CUR_TOP才会是PROCESS_STATE_TOP
            if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) {
                // The last app on the list is the foreground app.
                // 也就是当前app处于前台,那么adj默认给到0:FOREGROUND_APP_ADJ
                // 可以在/proc/pid/oom_score_adj,查看当前进程的adj值
                adj = ProcessList.FOREGROUND_APP_ADJ;
                // 默认分组TOP APP
                schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
                // type也为top activity
                state.setAdjType("top-activity");
                // 前台app当然可见
                foregroundActivities = true;
                hasVisibleActivities = true;
                // 进程状态,top
                procState = PROCESS_STATE_CUR_TOP;
                // 将是否允许启动前台service的状态设置成PROCESS_STATE_TOP
                // 进程状态不低于PROCESS_STATE_BOUND_FOREGROUND_SERVICE即可启动前台服务
                // mAllowStartFgsState(也就是PROCESS_STATE_TOP赋予的) <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE
                // 2 <= 5条件成立,所以是可以启动前台service的
                state.bumpAllowStartFgsState(PROCESS_STATE_TOP);
                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app);
                }
            } else if (state.isRunningRemoteAnimation()) {// 如果正在播放动画
                // 如果处于远程动画播放状态,则会提高进程优先级为:可见adj
                adj = ProcessList.VISIBLE_APP_ADJ;
                // 设置top app分组
                schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
                state.setAdjType("running-remote-anim");
                // 进程状态也为top
                procState = PROCESS_STATE_CUR_TOP;
                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app);
                }
            } else if (app.getActiveInstrumentation() != null) {//进程中存在instrumentation在活跃
                // Don't want to kill running instrumentation.
                // 提高instrumentation优先级:0
                adj = ProcessList.FOREGROUND_APP_ADJ;
                // 分组到默认,而不是top app
                schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                state.setAdjType("instrumentation");
                // 进程状态设置为前台服务:4
                procState = PROCESS_STATE_FOREGROUND_SERVICE;
                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app);
                }
            } else if (state.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) {// 正在接收广播的进程
                // An app that is currently receiving a broadcast also
                // counts as being in the foreground for OOM killer purposes.
                // It's placed in a sched group based on the nature of the
                // broadcast as reflected by which queue it's active in.
                // 当前正在接收广播的应用程序也被视为处于OOM杀手目的的前台。
                // 它被放置在一个sched组中,该组基于广播的性质,如它在哪个队列中活动所反映的
                // 因为正在接收广播,所以也算是前台app,优先级提高到0
                adj = ProcessList.FOREGROUND_APP_ADJ;
                // 如果是前台广播,则放到默认分组,如果是后台广播则放到后台分组
                schedGroup = (mTmpBroadcastQueue.contains(mService.mFgBroadcastQueue))
                        ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
                state.setAdjType("broadcast");
                // 进程状态:正在接收广播
                procState = ActivityManager.PROCESS_STATE_RECEIVER;
                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app);
                }
            } else if (psr.numberOfExecutingServices() > 0) {// 正在运行的服务,保存到ArraySet<ServiceRecord> mExecutingServices = new ArraySet<>();
                // An app that is currently executing a service callback also
                // counts as being in the foreground.
                // 当前正在执行服务回调的应用程序也被视为处于前台
                // 由于当前app中存在服务正在运行,所以该app也调整为前台
                // 注意这不是调整的服务进程adj,而是调整的app进程
                adj = ProcessList.FOREGROUND_APP_ADJ;
                // 如果正在执行前台服务,则调整到默认分组,如果是后台服务则调整到后台分组
                schedGroup = psr.shouldExecServicesFg()
                        ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
                state.setAdjType("exec-service");
                // 进程状态:服务
                procState = PROCESS_STATE_SERVICE;
                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app);
                }
            } else if (app == topApp) {// 如果是top app,但是处于灭屏状态,因为亮屏且top app不会在此分支上执行
                // top app但是处于灭屏状态,进程adj也是属于前台类别
                // 经测试,确实是这样
                adj = ProcessList.FOREGROUND_APP_ADJ;
                // 分组分配到了后台
                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
                state.setAdjType("top-sleeping");
                // 拥有前台activity
                foregroundActivities = true;
                // 进程状态:PROCESS_STATE_CUR_TOP_SLEEP
                procState = PROCESS_STATE_CUR_TOP;
                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top (sleeping): " + app);
                }
            } else {// 其他情况,默认后台分组
                // As far as we know the process is empty.  We may change our mind later.
                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
                // At this point we don't actually know the adjustment.  Use the cached adj
                // value that the caller wants us to.
                // adj默认值,或者上次调整的值
                adj = cachedAdj;
                // 进程状态属于empty
                procState = PROCESS_STATE_CACHED_EMPTY;
                // 非循环计算,说明adj已经计算完成了,此时就设置为empty
                // 超过10个empty,empty就会被依次kill
                if (!state.containsCycle()) {
                    state.setCached(true);
                    state.setEmpty(true);
                    state.setAdjType("cch-empty");
                }
                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making empty: " + app);
                }
            }
    
            // Examine all activities if not already foreground.
            // foregroundActivities目前只有top app才是true,除了了top它的值都是false
            // 只要进程有activity(getCachedHasActivities其实是hasActivities),则getCachedHasActivities返回true
            if (!foregroundActivities && state.getCachedHasActivities()) {
                // 通过activity不同的状态计算其adj
                //  if ((flags & ACTIVITY_STATE_FLAG_IS_VISIBLE) != 0) {// 如果activity可见,则处理onVisibleActivity
                //            callback.onVisibleActivity();
                //        } else if ((flags & ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED) != 0) {//如果activity处于pausing~paused,则执行onPausedActivity
                //            callback.onPausedActivity();
                //        } else if ((flags & ACTIVITY_STATE_FLAG_IS_STOPPING) != 0) {//如果activity stop,则执行onStoppingActivity
                //            callback.onStoppingActivity(
                //                    (flags & ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING) != 0);
                //        } else {//其他情况
                //            callback.onOtherActivity();
                //        }
                //   }
                // 以上的callback就是mTmpComputeOomAdjWindowCallback
                // 会根据activity不同的状态,调整不同的adj,例如onVisibleActivity被调用了,则会执行以下逻辑:
                // 1.activity处于可见状态,但是其adj<VISIBLE_APP_ADJ,那么就会重新修改adj = VISIBLE_APP_ADJ
                // 2.然后进程分组会分配到默认分组
                // 3.进程状态会被设置为processStateCurTop,且setEmpty和setCache会改为false
                // 4.设置为具有前台activity且设置为可见状态foregroundActivities = true mHasVisibleActivities = true;
                state.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback,
                        adj, foregroundActivities, hasVisibleActivities, procState, schedGroup,
                        appUid, logUid, PROCESS_STATE_CUR_TOP);
    
                // getCachedAdj的值会在computeOomAdjFromActivitiesIfNecessary重新修改
                // 将computeOomAdjFromActivitiesIfNecessary重新计算的值赋值给adj
                adj = state.getCachedAdj();
                // foregroundActivities和hasVisibleActivities也会被重新修改,在computeOomAdjFromActivitiesIfNecessary重新计算
                foregroundActivities = state.getCachedForegroundActivities();
                hasVisibleActivities = state.getCachedHasVisibleActivities();
                // 同上,computeOomAdjFromActivitiesIfNecessary会重新计算
                procState = state.getCachedProcState();
                schedGroup = state.getCachedSchedGroup();
            }
    
            // 有activity在最近任务mRecentTasks,代表最近启动的activity,进程状态设置成小于等于PROCESS_STATE_CACHED_RECENT
            // 这里的条件就是:近期任务中存在该进程,但是进程状态又比PROCESS_STATE_CACHED_RECENT高,则设置回PROCESS_STATE_CACHED_RECENT
            if (procState > PROCESS_STATE_CACHED_RECENT && state.getCachedHasRecentTasks()) {
                procState = PROCESS_STATE_CACHED_RECENT;
                state.setAdjType("cch-rec");
                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app);
                }
            }
    
            // 如果adj大于可感知的优先级,或者进程状态大于前台服务
            // 这里主要就是处理 adj和进程状态都处于低迷状态,但是实际上进程处于前台,则将进程状态和adj重新拉高,保证其保活率
            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
                    || procState > PROCESS_STATE_FOREGROUND_SERVICE) {
                // 属于前台服务,但是adj不属于可感知范围内,进程状态也不在前台服务范围内
                // 那么就调整回PERCEPTIBLE_APP_ADJ:可感知优先级
                // 进程状态也改回前台服务PROCESS_STATE_FOREGROUND_SERVICE
                if (psr.hasForegroundServices()) {
                    // The user is aware of this app, so make it visible.
                    adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                    procState = PROCESS_STATE_FOREGROUND_SERVICE;
                    state.bumpAllowStartFgsState(PROCESS_STATE_FOREGROUND_SERVICE);
                    state.setAdjType("fg-service");
                    state.setCached(false);
                    // 默认分组
                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + state.getAdjType() + ": "
                                + app + " ");
                    }
                } else if (state.hasOverlayUi()) {// 如果是overlay ui(需要设置TYPE_APPLICATION_OVERLAY,有覆盖之上的界面,类似悬浮窗)
                    // The process is display an overlay UI.
                    // adj也设置为可感知adj
                    adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                    // 进程状态设置为重要的前台进程
                    procState = PROCESS_STATE_IMPORTANT_FOREGROUND;
                    state.setCached(false);
                    state.setAdjType("has-overlay-ui");
                    // 默认分组
                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to overlay ui: " + app);
                    }
                }
            }
    
            // If the app was recently in the foreground and moved to a foreground service status,
            // allow it to get a higher rank in memory for some time, compared to other foreground
            // services so that it can finish performing any persistence/processing of in-memory state.
            // 1.如果应用程序最近处于前台并移动到前台服务状态,则与其他前台服务相比,
            // 允许它在一段时间内在内存中获得更高的级别,以便它可以完成对内存状态的任何持久化/处理。
            // 2.也就是 : 调整最近活跃的前台服务进程,在一段时间内(15s),继续像对待前台应用程序一样对待它。
            // 3.处理,进程被移动到后台,不活跃时的状态:15s内在top级别(最前台),会被调整跟前台进程一样的待遇。
            // 4.或者进程处于PROCESS_STATE_TOP(进程正在主持当前的顶级活动)状态,也会进来
            if (psr.hasForegroundServices() && adj > ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
                    && (state.getLastTopTime() + mConstants.TOP_TO_FGS_GRACE_DURATION > now
                    || state.getSetProcState() <= PROCESS_STATE_TOP)) {
                // 设置为PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,即虽然进程在调整到后台了或者不活跃了,在一段时间内,继续像对待前台应用程序一样对待它
                adj = ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
                state.setAdjType("fg-service-act");
                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app);
                }
            }
    
            // 处理:adj大于了可感知优先级,进程大于了后台状态,但是进程又处于很重要的状态
            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
                    || procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {
                // 进程处于重要状态(调用AMS的setProcessImportant设置-->强制设置为进程重要状态)
                // 这种情况用于:想要进程让用户感知,但又不想设置其优先级,就可以通过setProcessImportant来设置
                // 一般toast是这么做的
                if (state.getForcingToImportant() != null) {
                    // This is currently used for toasts...  they are not interactive, and
                    // we don't want them to cause the app to become fully foreground (and
                    // thus out of background check), so we yes the best background level we can.
                    // 默认调整到可感知adj
                    adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                    // 设置进程状态为后台--进程处于临时后台,因此我们将尝试继续运行
                    procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
                    state.setCached(false);
                    state.setAdjType("force-imp");
                    state.setAdjSource(state.getForcingToImportant());
                    // 默认分组
                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to force imp: " + app);
                    }
                }
            }
    
            // 当app无法保存状态,如AndroidManifest.xml设置了"android:cantSaveState="true"",
            // 同时系统配置了<feature name="android.software.cant_save_state" />这个feature
            // 由于其无法保存状态,故尽量不要杀死, 一般只有一个,adj类型是"heavy"
            if (state.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;
                    state.setCached(false);
                    state.setAdjType("heavy");
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app);
                    }
                }
                if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
                    procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
                    state.setAdjType("heavy");
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to heavy: " + app);
                    }
                }
            }
    
            // 如果是launcher应用,该应用的优先级都不算太高
            // 但根据实际验证,桌面会一直保持在100(可见的优先级),如果回到桌面 ,则会调整到0:前台应用
            // 因为一般走不到这里来,会在以上的top app中进行处理了
            if (state.getCachedIsHomeProcess()) {
                // 桌面应用的adj不能大于600(HOME_APP_ADJ),launcher应用的adj大于了HOME_APP_ADJ,则默认调整回HOME_APP_ADJ
                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;
                    // launcher是个特殊应用,一直都会处于后台分组,但又需要保证不被kill
                    schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
                    state.setCached(false);
                    state.setAdjType("home");
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app);
                    }
                }
                // 进程状态也设置为PROCESS_STATE_HOME
                if (procState > ActivityManager.PROCESS_STATE_HOME) {
                    procState = ActivityManager.PROCESS_STATE_HOME;
                    state.setAdjType("home");
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app);
                    }
                }
            }
    
            // 1. getCachedIsPreviousProcess: 在上一个前台进程activity stop的时候,则会将进程ActivityRecord的app(WindowProcessController)加入到mPreviousProcess,做为上一个进程
            // 2. getCachedHasActivities:在realStartActivityLocked()的时候将activity add进来,在detachFromProcess或者destroyImmediately()的时候移除activity,
            // 也就是说是否有启动过但是未destory的activity(上个进程仍存在的情况),只针对activity
            // PREVIOUS_APP_ADJ解释:这是用户所在的上一个应用程序的过程。这个过程高于其他过程,因为切换回上一个程序是非常常见的
            if (state.getCachedIsPreviousProcess()/*上一个进程*/ && state.getCachedHasActivities()/*此次进程*/) {
                // 如果存在上一个进程activity,则adj默认给到PREVIOUS_APP_ADJ
                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;
                    state.setCached(false);
                    state.setAdjType("previous");
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
                    }
                }
                // 进程状态设置为最新启动的activity
                if (procState > PROCESS_STATE_LAST_ACTIVITY) {
                    procState = PROCESS_STATE_LAST_ACTIVITY;
                    state.setAdjType("previous");
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
                    }
                }
            }
    
            if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
                    + " reason=" + state.getAdjType());
    
            // By default, we use the computed adjustment.  It may be changed if
            // there are applications dependent on our services or providers, but
            // this gives us a baseline and makes sure we don't get into an
            // infinite recursion. If we're re-evaluating due to cycles, use the previously computed
            // values.
            // cycleReEval只有在设置了setContainsCycle true(如正在计算computeOomAdjLSP的时候还没计算完又跑进来,
            // 如上面的mAdjSeq == state.getAdjSeq()或者(services/provider的客户端有这种情况的时候---也就是会递归调用computeOomAdjLSP)),
            // 在updateOomAdjInnerLSP会让cycleReEval=true
            // 如果是循环计算的话
            if (cycleReEval) {
                // 取之前的进程状态getCurRawProcState和上面计算出来的进程状态procState的最小值,值越小,保活率越高
                procState = Math.min(procState, state.getCurRawProcState());
                // 取之前的进程adj(getCurRawAdj--原始adj)和上面计算出来的adj的最小值,值越小,保活率越高
                adj = Math.min(adj, state.getCurRawAdj());
                // 取最大分组
                schedGroup = Math.max(schedGroup, state.getCurrentSchedulingGroup());
            }
            // 设置原始adj,也就是未被约束的adj
            state.setCurRawAdj(adj);
            // 设置原始进程状态,也就是未被约束的procState
            state.setCurRawProcState(procState);
    
            state.setHasStartedServices(false);
            // 设置此次的序列号
            state.setAdjSeq(mAdjSeq);
    
            // 处理backup应用
            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 up
                if (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;
                    }
                    state.setAdjType("backup");
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app);
                    }
                    state.setCached(false);
                }
                if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
                    procState = ActivityManager.PROCESS_STATE_BACKUP;
                    state.setAdjType("backup");
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to backup: " + app);
                    }
                }
            }
    
            // 这里开始就是处理四大组件的service了
            // service服务自身和服务依赖关系调整,也就是activity客户端和services服务端的adj调整,他们之间有依赖关系
            // 用于计算前台服务都拥有哪些能力,能力默认0
            int capabilityFromFGS = 0; // capability from foreground service.
            // 用来用来判断分组是否需要将分组设置成top
            boolean scheduleLikeTopApp = false;
            // 获取正在运行的服务(只要startService了就会放入ProcessServiceRecord的mServices中,stopService或者kill会从mServices移除)
            // 且必须adj大于前台app,或者属于后台分组,或者比PROCESS_STATE_TOP状态还大
            // 处理正在运行的服务的adj状态和进程状态---也就是服务正在运行,但是adj却太高了,做进一步处理
            for (int is = psr.numberOfRunningServices() - 1;
                    is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                            || procState > PROCESS_STATE_TOP);
                    is--) {
                // 获取当前进程运行的其中一个服务
                ServiceRecord s = psr.getRunningServiceAt(is);
                // startRequested是在startServiceInnerLocked中设置(服务启动过程),
                // 在realStartServiceLocked(会将service放入mServices)之前
                if (s.startRequested) {
                    // 设置服务正处于运行,因为startRequested为true
                    state.setHasStartedServices(true);
                    // PROCESS_STATE_SERVICE(10) > PROCESS_STATE_TOP(2)
                    // 如果进程状态比PROCESS_STATE_SERVICE还大,则默认调整回PROCESS_STATE_SERVICE
                    if (procState > PROCESS_STATE_SERVICE) {
                        procState = PROCESS_STATE_SERVICE;
                        state.setAdjType("started-services");
                        if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                            reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                    "Raise procstate to started service: " + app);
                        }
                    }
    
                    // hasShownUi只要该进程有onStartActivity的操作,就会设置成true,不管activity是否destroy,只要进程还在都是true
                    // 因为activity启动后默认是带有UI属性的:hasShownUi为true
                    if (!s.mKeepWarming && state.hasShownUi() && !state.getCachedIsHomeProcess()) {
                        // If this process has shown some UI, let it immediately
                        // go to the LRU list because it may be pretty heavy with
                        // UI stuff.  We'll tag it with a label just to help
                        // debug and understand what is going on.
                        if (adj > ProcessList.SERVICE_ADJ) {
                            state.setAdjType("cch-started-ui-services");
                        }
                    } else {
                        // 1.lastActivity用于记录服务最后一次活动的时间戳
                        // 2.当服务被启动或绑定时,系统会更新lastActivity的值,以表示服务的最新活动时间
                        // 3.系统会定期检查服务的lastActivity值,如果服务长时间没有活动,系统就会认为该服务已经不再需要,自动停止该服务
                        // 4.now < lastActivity的值(服务最后活跃的时间戳)+30分钟,也就是lastActivity服务启动或者bind时间在30分钟以内
                        // ,因为服务启动和bind都会更新lastActivity值
                        // 5.那为什么是30分钟呢:解释如下-->
                        // MAX_SERVICE_INACTIVITY是一个常量,用于定义服务在没有activity的情况下可以保持运行的最长时间(activity销毁,服务还在,能存活的最大时间)
                        // 具体来说,它表示服务在没有任何客户端绑定或调用它的情况下可以保持运行的最长时间,超过这个时间,系统将自动停止该服务以释放系统资源
                        if (s.mKeepWarming
                                || now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY/*30分钟*/)) {
                            // This service has seen some activity within
                            // recent memory, so we will keep its process ahead
                            // of the background processes.
                            // 此服务在最近的内存中存在一些activity,因此我们将使其进程领先于后台进程
                            // service的对端存在activity,故调整服务adj为SERVICE_ADJ
                            if (adj > ProcessList.SERVICE_ADJ) {
                                adj = ProcessList.SERVICE_ADJ;
                                state.setAdjType("started-services");
                                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                            "Raise adj to started service: " + app);
                                }
                                state.setCached(false);
                            }
                        }
                        // If we have let the service slide into the background
                        // state, still have some text describing what it is doing
                        // even though the service no longer has an impact.
                        if (adj > ProcessList.SERVICE_ADJ) {
                            state.setAdjType("cch-started-services");
                        }
                    }
                }
    
                // 如果是前台服务
                if (s.isForeground) {
                    // foregroundServiceType一般是AndroidManifest中设置,代表着前台服务type,所拥有哪些能力
                    // 例如:android:foregroundServiceType="dataSync|mediaPlayback|phoneCall|location|connectedDevice"
                    final int fgsType = s.foregroundServiceType;
                    // 是否允许前台服务中的使用权限
                    if (s.mAllowWhileInUsePermissionInFgs) {
                        // 拥有前台权限的位置信息
                        capabilityFromFGS |=
                                (fgsType & FOREGROUND_SERVICE_TYPE_LOCATION)
                                        != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
    
                        final boolean enabled = state.getCachedCompatChange(
                                CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY);
                        if (enabled) {
                            // 拥有前台权限的相机调用
                            capabilityFromFGS |=
                                    (fgsType & FOREGROUND_SERVICE_TYPE_CAMERA)
                                            != 0 ? PROCESS_CAPABILITY_FOREGROUND_CAMERA : 0;
                            // 拥有前台权限的麦克风使用
                            capabilityFromFGS |=
                                    (fgsType & FOREGROUND_SERVICE_TYPE_MICROPHONE)
                                            != 0 ? PROCESS_CAPABILITY_FOREGROUND_MICROPHONE : 0;
                        } else {
                            // 如果是版本过低,这默认将camera、听筒的权限赋值给前台服务
                            capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_CAMERA
                                    | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
                        }
                    }
                }
    
                ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = s.getConnections();
                // 获取service中对应的所有连接,也就是bindService中的连接对象
                // 遍历所有的服务,ArrayMap是按照key的hashCode/identityHashCode进行排序的,不是按照add进去的时间进行排序
                // 这里是从serviceConnections尾部向头部遍历
                // 如果当前进程的adj > 0或者进程分组schedGroup是后台或者进程状态 > top,才进行调整(adj低的不进行依赖调整,减少调整次数)
                for (int conni = serviceConnections.size() - 1;
                        conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                                || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                                || procState > PROCESS_STATE_TOP);
                        conni--) {
                    // 获取service的连接对象ConnectionRecord,这里是list,因为一个服务可能会被绑定(连接)多次
                    ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni);
                    // 只调整adj > 前台app,处于后台分组,或者进程状态非top,这三个条件的意思就是:非前台的都进行调整,进入此case
                    for (int i = 0;
                            i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
                                    || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                                    || procState > PROCESS_STATE_TOP);
                            i++) {
                        // XXX should compute this based on the max of
                        // all connected clients.
                        // 应根据所有连接客户端的adj最大值来计算此值
                        // 获取单个连接
                        ConnectionRecord cr = clist.get(i);
                        // cr.binding.client代表着谁启动的该服务
                        // 如果服务链接的客户端是当前app,则跳过,客户端的adj不会由于服务端的adj提升而升高---但服务端的adj受客户端app的影响
                        // 只有服务端的adj,才会由于客户端的adj提升而得到相应的提升(不然会出现客户端在使用的时候服务端由于优先级低给回收掉了)
                        if (cr.binding.client == app) {
                            // 获取了所有运行的服务,发现该服务的客户端是该app,则不处理
                            // Binding to oneself is not interesting.
                            continue;
                        }
    
                        // 通过mProcessStats.mTrackingAssociations.add(this);将其放入记录ProcessState列表中
                        // 在updateOomAdjInnerLSP的updateTrackingAssociationsLocked会去更新这些add进去的进程状态
                        // data/system/procstats里面有保存相关数据
                        boolean trackedProcState = false;
    
                        // 获取service对端的客户端
                        ProcessRecord client = cr.binding.client;
                        // 客户端的进程状态
                        final ProcessStateRecord cstate = client.mState;
                        // 递归调用一次,computeClients是否需要计算当前进程的时候并计算服务、provider提供者对端client的adj
                        // 也就是当前正在处理服务的adj,同时去调整服务对端的客户端client的adj
                        if (computeClients) {
                            computeOomAdjLSP(client/*传入了客户端的app*/, cachedAdj, topApp, doingAll, now,
                                    cycleReEval, true);
                        } else {
                            // 否则直接设置客户端的原始adj和原始进程状态
                            cstate.setCurRawAdj(cstate.getCurAdj());
                            cstate.setCurRawProcState(cstate.getCurProcState());
                        }
    
                        // 获取客户端的原始adj和进程状态
                        int clientAdj = cstate.getCurRawAdj();
                        int clientProcState = cstate.getCurRawProcState();
    
                        // 如果客户端属于系统进程
                        final boolean clientIsSystem = clientProcState < PROCESS_STATE_TOP;
    
                        // 如果客户端不会被冻结,那么该app端(服务端所在进程)也不会被冻结---则服务端也被设置不允许冻结
                        if (client.mOptRecord.shouldNotFreeze()) {
                            // Propagate the shouldNotFreeze flag down the bindings.
                            app.mOptRecord.setShouldNotFreeze(true);
                        }
    
                        // bindService的特有标志,不影响 Service 进程的优先级的情况下,允许 Service 进程被加入后台队列中
                        if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {
                            // 判断客户端adj是否计算完成,也就是如果cycleReEval为真,则设置为循环依赖计算
                            // 判断客户端是否具有循环计算,若循环计算中getCompletedAdjSeq还未达到预期,则返回true,跳过此次,进入下一次继续计算
                            if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
                                continue;
                            }
    
                            // 如果包含BIND_INCLUDE_CAPABILITIES,则会将客户端的能力全部赋值给服务端
                            if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
                                capability |= cstate.getCurCapability();
                            }
    
                            // If an app has network capability by default
                            // (by having procstate <= BFGS), then the apps it binds to will get
                            // elevated to a high enough procstate anyway to get network unless they
                            // request otherwise, so don't propagate the network capability by default
                            // in this case unless they explicitly request it.
                            // 如果客户端具有网络能力
                            if ((cstate.getCurCapability() & PROCESS_CAPABILITY_NETWORK) != 0) {
                                // 如果属于前台服务
                                if (clientProcState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
                                    // 如果客户端在属于前台服务的情况,必须带有BIND_BYPASS_POWER_NETWORK_RESTRICTIONS,才会赋予网络能力
                                    if ((cr.flags & Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS)
                                            != 0) {
                                        capability |= PROCESS_CAPABILITY_NETWORK;
                                    }
                                } else {
                                    // 如果客户端不属于前台服务,则默认给网络能力
                                    capability |= PROCESS_CAPABILITY_NETWORK;
                                }
                            }
    
                            // 如果客户端的进程状态,比cache activity 还大,有可能是Empty,或进程不存在状态
                            if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                                // If the other app is cached for any reason, for purposes here
                                // we are going to consider it empty.  The specific cached state
                                // doesn't propagate except under certain conditions.
                                // 如果其他应用程序由于任何原因被缓存,出于这里的目的,我们将认为它是空的。除非在特定条件下,否则特定的缓存状态不会传播。
                                // 直接赋予empty
                                clientProcState = PROCESS_STATE_CACHED_EMPTY;
                            }
                            String adjType = null;
                            // BIND_ALLOW_OOM_MANAGEMENT:允许服务所在的进程进行正常的内存管理。它将被视为一个正在运行的服务,
                            // 允许系统在内存不足或其他突发奇想的情况下(暂时)删除进程,并在长时间运行时更积极地使其成为被杀死(并重新启动)的候选者
                            // 也就是更容易被杀死:允许内存管理系统管理 Service 的进程,允许系统在内存不足的时候删除这个进程
                            if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
                                // Similar to BIND_WAIVE_PRIORITY, keep it unfrozen.
                                // 设置不被冻结
                                if (clientAdj < ProcessList.CACHED_APP_MIN_ADJ) {
                                    app.mOptRecord.setShouldNotFreeze(true);
                                }
                                // Not doing bind OOM management, so treat
                                // this guy more like a started service.
                                // 如果有ui---例如activity,且不是home进程
                                if (state.hasShownUi() && !state.getCachedIsHomeProcess()) {
                                    // If this process has shown some UI, let it immediately
                                    // go to the LRU list because it may be pretty heavy with
                                    // UI stuff.  We'll tag it with a label just to help
                                    // debug and understand what is going on.
                                    if (adj > clientAdj) {
                                        adjType = "cch-bound-ui-services";
                                    }
                                    state.setCached(false);
                                    // 设置客户端adj和服务端adj一样
                                    clientAdj = adj;
                                    clientProcState = procState;
                                } else {
                                    // 如果service最后活跃的时间在30分钟内
                                    if (now >= (s.lastActivity
                                            + mConstants.MAX_SERVICE_INACTIVITY)) {
                                        // This service has not seen activity within
                                        // recent memory, so allow it to drop to the
                                        // LRU list if there is no other reason to keep
                                        // it around.  We'll also tag it with a label just
                                        // to help debug and undertand what is going on.
                                        if (adj > clientAdj) {
                                            adjType = "cch-bound-services";
                                        }
                                        clientAdj = adj;
                                    }
                                }
                            }
                            // 如果服务端adj > 客户端adj,也就是客户端比服务端更为重要
                            if (adj > clientAdj) {
                                // If this process has recently shown UI, and
                                // the process that is binding to it is less
                                // important than being visible, then we don't
                                // care about the binding as much as we care
                                // about letting this process get into the LRU
                                // list to be killed and restarted if needed for
                                // memory.
                                // 如果存在ui,不是home进程,客户端adj > 可感知adj,服务端adj >= 900优先级
                                if (state.hasShownUi() && !state.getCachedIsHomeProcess()
                                        && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                                    if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
                                        adjType = "cch-bound-ui-services";
                                    }
                                } else {
                                    int newAdj;
                                    // 如果服务连接时里面包含BIND_ABOVE_CLIENT或者BIND_IMPORTANT的时候
                                    // BIND_ABOVE_CLIENT: 代表服务的优先级需要比客户端client的高
                                    // BIND_IMPORTANT:代表此服务很重要,adj可以到0(fg)或者-700(PERSISTENT_SERVICE)
                                    // 而一般情况下,客户端属于前台,服务端会处于可见adj
                                    if ((cr.flags&(Context.BIND_ABOVE_CLIENT
                                            |Context.BIND_IMPORTANT)) != 0) {
                                        // 如果>=常驻服务adj,则客户端和服务端保持一致adj
                                        if (clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
                                            newAdj = clientAdj;
                                        } else {
                                            // 如果客户端adj < PERSISTENT_SERVICE_ADJ,那至少都是常驻进程
                                            // 将服务端设置为常驻服务,设置默认分组,进程状态也是常驻state
                                            // make this service persistent
                                            newAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
                                            schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                                            procState = ActivityManager.PROCESS_STATE_PERSISTENT;
                                            cr.trackProcState(procState, mAdjSeq, now);
                                            trackedProcState = true;
                                        }
                                        // 如果是不可感知服务 且客户端比服务端优先级更低的情况:客户端adj <= 可感知adj。服务端adj >= 最低可感知adj
                                    } else if ((cr.flags & Context.BIND_NOT_PERCEPTIBLE) != 0
                                            && clientAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
                                            && adj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
                                        // 则服务端adj赋予最低可感知adj(250)
                                        newAdj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
                                        // 如果服务端带有BIND_ALMOST_PERCEPTIBLE:允许服务端所在进程被视为对用户来说就像一个可感知的应用程序一样重要,
                                        // 并避免oom杀手在内存不足的情况下杀死这个进程,也是提高服务端进程优先级
                                        // 且客户端adj < 可感知,服务端adj >= 媒体音乐可感知,也就是客户端比服务端adj更底的情况
                                    } else if ((cr.flags & Context.BIND_ALMOST_PERCEPTIBLE) != 0
                                            && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
                                            && adj >= ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ) {
                                        // 也就是客户端比服务端adj更底的情况,调整服务端adj为PERCEPTIBLE_MEDIUM_APP_ADJ
                                        newAdj = ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ;
                                        // 如果服务端不可见,且客户端<可感知 ,服务端 >= 可感知
                                    } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
                                            && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
                                            && adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
                                        // 也就是客户端比服务端adj更底的情况,则服务端默认也给到PERCEPTIBLE_APP_ADJ
                                        newAdj = ProcessList.PERCEPTIBLE_APP_ADJ;
                                    } else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {// 如果客户端大于等于可感知adj,则服务端和客户端adj保持一致
                                        newAdj = clientAdj;
                                    } else {
                                        // 服务端大于VISIBLE_APP_ADJ(100),则从clientAdj和VISIBLE_APP_ADJ取一个最大值
                                        // 大概情况也就是从可感知adj(200~250)和可见adj(100)选一个
                                        if (adj > ProcessList.VISIBLE_APP_ADJ) {
                                            // TODO: Is this too limiting for apps bound from TOP?
                                            newAdj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ);
                                        } else {
                                            // 反之不做调整
                                            newAdj = adj;
                                        }
                                    }
                                    if (!cstate.isCached()) {
                                        state.setCached(false);
                                    }
                                    // 如果重新计算过后的newAdj比adj要小,说明进程优先级被拉低了,更容易保活,调整有效
                                    if (adj >  newAdj) {
                                        // 赋值给adj
                                        adj = newAdj;
                                        state.setCurRawAdj(adj);
                                        adjType = "service";
                                    }
                                }
                            }
                            // 如果服务端没有设置BIND_NOT_FOREGROUND和BIND_IMPORTANT_BACKGROUND
                            // 即非前台服务(后台服务),又不是重要的后台服务
                            if ((cr.flags & (Context.BIND_NOT_FOREGROUND
                                    | Context.BIND_IMPORTANT_BACKGROUND)) == 0) {
                                // This will treat important bound services identically to
                                // the top app, which may behave differently than generic
                                // foreground work.
                                // 获取客户端cgroup设置的分组信息赋值给curSchedGroup
                                final int curSchedGroup = cstate.getCurrentSchedulingGroup();
                                // 如果客户端分组大于服务端分组,分组越大,能力越强
                                if (curSchedGroup > schedGroup) {
                                    // 在服务端分组能力比客户端分组能力弱的情况,且服务端还带有重要进程flag
                                    // 则将客户端分组能力分配给服务端
                                    if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
                                        schedGroup = curSchedGroup;
                                    } else {
                                        // 反之服务端分组到默认(2)
                                        schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                                    }
                                }
                                // 如果客户端进程状态 < top(2),所以客户端进程状态有点强,也就是说客户端是常驻进程
                                if (clientProcState < PROCESS_STATE_TOP) {
                                    // Special handling for above-top states (persistent
                                    // processes).  These should not bring the current process
                                    // into the top state, since they are not on top.  Instead
                                    // give them the best bound state after that.
                                    // 如有拥有BIND_FOREGROUND_SERVICE(绑定前台服务)
                                    if (cr.hasFlag(Context.BIND_FOREGROUND_SERVICE)) {
                                        // 客户端进程状态改为:绑定前台服务,也就是服务端带有BIND_FOREGROUND_SERVICE,影响到了客户端的进程状态
                                        clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
                                        state.bumpAllowStartFgsState(
                                                PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
                                        // 如果处于亮屏 且 带有BIND_FOREGROUND_SERVICE_WHILE_AWAKE(在唤醒时绑定前台服务)
                                    } else if (mService.mWakefulness.get()
                                            == PowerManagerInternal.WAKEFULNESS_AWAKE
                                            && (cr.flags & Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)
                                                    != 0) {
                                        // 客户端进程状态改为:绑定前台服务
                                        clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
                                    } else {
                                        // 反之客户端进程状态改为重要的前台
                                        clientProcState =
                                                PROCESS_STATE_IMPORTANT_FOREGROUND;
                                    }
                                    // 如果客户端进程状态处于TOP(2)
                                } else if (clientProcState == PROCESS_STATE_TOP) {
                                    // Go at most to BOUND_TOP, unless requested to elevate
                                    // to client's state.
                                    // 客户端进程状态降低一档,到PROCESS_STATE_BOUND_TOP(3)
                                    clientProcState = PROCESS_STATE_BOUND_TOP;
                                    state.bumpAllowStartFgsState(PROCESS_STATE_BOUND_TOP);
                                    // sdk判断
                                    final boolean enabled = cstate.getCachedCompatChange(
                                            CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY);
                                    if (enabled) {
                                        // 如果服务端具有BIND_INCLUDE_CAPABILITIES
                                        // 则将客户端能力全部赋值给服务端
                                        if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
                                            // TOP process passes all capabilities to the service.
                                            capability |= cstate.getCurCapability();
                                        } else {
                                            // TOP process passes no capability to the service.
                                        }
                                    } else {
                                        // TOP process passes all capabilities to the service.
                                        capability |= cstate.getCurCapability();
                                    }
                                }
                                // 如果服务端不具有重要的后台flag
                            } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) {
                                // 则将客户端进程状态设置为短暂后台状态
                                if (clientProcState <
                                        PROCESS_STATE_TRANSIENT_BACKGROUND) {
                                    clientProcState =
                                            PROCESS_STATE_TRANSIENT_BACKGROUND;
                                }
                            } else {
                                // 如果具有重要的后台服务flag,并且客户端进程状态也要低于PROCESS_STATE_IMPORTANT_BACKGROUND
                                // 才会将客户端进程状态设置为PROCESS_STATE_IMPORTANT_BACKGROUND
                                // 如果满足条件,算是变相把客户端进程优先级拉高了,算个调整策略
                                if (clientProcState <
                                        PROCESS_STATE_IMPORTANT_BACKGROUND) {
                                    clientProcState =
                                            PROCESS_STATE_IMPORTANT_BACKGROUND;
                                }
                            }
    
                            // 如果服务端分组< top,可能是默认default分组
                            // 且带有SCHEDULE_LIKE_TOP_APP:像对待top app那样对待服务
                            // 且客户端是系统服务
                            if (schedGroup < ProcessList.SCHED_GROUP_TOP_APP
                                    && (cr.flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0
                                    && clientIsSystem) {
                                // 则直接将服务端的分组调整到第二高
                                schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
                                scheduleLikeTopApp = true;
                            }
    
                            if (!trackedProcState) {
                                cr.trackProcState(clientProcState, mAdjSeq, now);
                            }
    
                            // 如果服务进程状态 > 客户端进程状态,很正常,服务端都要比客户端状态要高,顶天只能相等
                            // 将客户端进程状态赋值给服务端进程状态
                            if (procState > clientProcState) {
                                procState = clientProcState;
                                state.setCurRawProcState(procState);
                                if (adjType == null) {
                                    adjType = "service";
                                }
                            }
                            // 如果进程状态 < PROCESS_STATE_IMPORTANT_BACKGROUND,说明至少是个重要的前台进程
                            // 且带有UI显示
                            if (procState < PROCESS_STATE_IMPORTANT_BACKGROUND
                                    && (cr.flags & Context.BIND_SHOWING_UI) != 0) {
                                // 带有BIND_SHOWING_UI的标签,则设置ProcessProfileRecord的mPendingUiClean为true
                                // mPendingUiClean会在更新内存等级的时候会使用,用户触发thread.scheduleTrimMemory进行应用不同级别的内存回收
                                app.setPendingUiClean(true);
                            }
                            // 设置此次优先级调整时,记录谁调整了adj,根据谁调整的adj,源头和target是谁
                            if (adjType != null) {
                                state.setAdjType(adjType);
                                state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
                                        .REASON_SERVICE_IN_USE);
                                // setAdjSource设置此次调整的来源:客户端ProcessRecord
                                state.setAdjSource(cr.binding.client);
                                state.setAdjSourceProcState(clientProcState);
                                // setAdjTarget设置调整的原因的服务组件是instanceName
                                state.setAdjTarget(s.instanceName);
                                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
                                            + ": " + app + ", due to " + cr.binding.client
                                            + " adj=" + adj + " procState="
                                            + ProcessList.makeProcStateString(procState));
                                }
                            }
                        } else { // BIND_WAIVE_PRIORITY == true,代表不影响 Service 进程的优先级的情况下,允许 Service 进程被加入后台队列中
                            // BIND_WAIVE_PRIORITY bindings are special when it comes to the
                            // freezer. Processes bound via WPRI are expected to be running,
                            // but they are not promoted in the LRU list to keep them out of
                            // cached. As a result, they can freeze based on oom_adj alone.
                            // Normally, bindToDeath would fire when a cached app would die
                            // in the background, but nothing will fire when a running process
                            // pings a frozen process. Accordingly, any cached app that is
                            // bound by an unfrozen app via a WPRI binding has to remain
                            // unfrozen.
                            // 设置服务端不被冻结
                            if (clientAdj < ProcessList.CACHED_APP_MIN_ADJ) {
                                app.mOptRecord.setShouldNotFreeze(true);
                            }
                        }
                        // 服务端psr则设置setTreatLikeActivity的标记位,需要像对待activity一样对待该服务进程
                        if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
                            psr.setTreatLikeActivity(true);
                        }
                        // 获取客户端对象的activity
                        final ActivityServiceConnectionsHolder a = cr.activity;
                        // 如果从一个 Activity 绑定,则这个 Service 进程的优先级和 Activity 是否对用户可见有关
                        // 简言之:可以根据客户端activity是否可见来调整优先级
                        if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
                            // activity > 前台app(0)(即activity非前台的情况--可能是recent的top app,或者可感知activity)
                            // ,一般activity处于前台时,adj都为0,处于后台时,一般是700
                            // 简言之这个条件:activity非前台,但是具有可见性质
                            if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
                                    && a.isActivityVisible()) {
                                // 服务端调整到可见状态
                                adj = ProcessList.FOREGROUND_APP_ADJ;
                                // 设置服务端的原始adj
                                state.setCurRawAdj(adj);
                                // 如果服务端非前台
                                if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
                                    // 但是服务端处于重要的后台,则将分组分配到最高(4)
                                    if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
                                        schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
                                    } else {
                                        schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                                    }
                                }
                                // 因为客户端activity可见,再次设置adj type
                                state.setCached(false);
                                state.setAdjType("service");
                                state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
                                        .REASON_SERVICE_IN_USE);
                                state.setAdjSource(a);
                                state.setAdjSourceProcState(procState);
                                state.setAdjTarget(s.instanceName);
                                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                                    reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                            "Raise to service w/activity: " + app);
                                }
                            }
                        }
                    }
                }
            }
    
            // 开始处理contentProvider优先级
            final ProcessProviderRecord ppr = app.mProviders;
            // 遍历该进程的所有contentProvider
            for (int provi = ppr.numberOfProviders() - 1;
                    provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                            || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                            || procState > PROCESS_STATE_TOP);
                    provi--) {
                // 获取ContentProvider
                ContentProviderRecord cpr = ppr.getProviderAt(provi);
                // 遍历所有连接了该ContentProvider的连接对象
                for (int i = cpr.connections.size() - 1;
                        i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                                || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                                || procState > PROCESS_STATE_TOP);
                        i--) {
                    // ContentProvider连接对象
                    ContentProviderConnection conn = cpr.connections.get(i);
                    // 因为连接的话,都是在客户端发起连接的,这里获取客户端的ProcessRecord
                    ProcessRecord client = conn.client;
                    // 客户端的进程状态
                    final ProcessStateRecord cstate = client.mState;
                    // 如果处于同个进程,不做调整
                    if (client == app) {
                        // Being our own client is not interesting.
                        continue;
                    }
                    // 是否带有循环计算标志
                    if (computeClients) {
                        computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true);
                    } else {
                        // 如果没有,客户端原始的adj设置为约束过后的adj
                        cstate.setCurRawAdj(cstate.getCurAdj());
                        cstate.setCurRawProcState(cstate.getCurProcState());
                    }
    
                    if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
                        continue;
                    }
    
                    // 获取客户端原始的adj,经过上面的步骤,获取的其实是被约束过后的adj
                    int clientAdj = cstate.getCurRawAdj();
                    int clientProcState = cstate.getCurRawProcState();
    
                    // 客户端如果处于cached状态,则默认给到empty
                    if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                        // If the other app is cached for any reason, for purposes here
                        // we are going to consider it empty.
                        clientProcState = PROCESS_STATE_CACHED_EMPTY;
                    }
                    // 如果客户端应该不会被冻结,那么服务端也会被设置为不被冻结
                    if (client.mOptRecord.shouldNotFreeze()) {
                        // Propagate the shouldNotFreeze flag down the bindings.
                        app.mOptRecord.setShouldNotFreeze(true);
                    }
                    String adjType = null;
                    // 服务端adj比客户端adj大,再正常不过了
                    if (adj > clientAdj) {
                        // 如果该进程有界面的,而且不是桌面,而且客户端clientAdj大于200,这类不调整adj大小,只设置adj类型
                        if (state.hasShownUi() && !state.getCachedIsHomeProcess()
                                && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
                            adjType = "cch-ui-provider";
                        } else {// 反之客户端如果大于前台app,则服务端默认使用前台adj,如果客户端小于等于前台adj,则服务端也调整到前台adj
                            adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
                                    ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
                            state.setCurRawAdj(adj);
                            adjType = "provider";
                        }
                        state.setCached(state.isCached() & cstate.isCached());
                    }
    
                    // 如果客户端进程状态 <= 前台服务
                    if (clientProcState <= PROCESS_STATE_FOREGROUND_SERVICE) {
                        if (adjType == null) {
                            adjType = "provider";
                        }
                        // 客户端进程状态在top状态,则默认调低一档到PROCESS_STATE_BOUND_TOP
                        if (clientProcState == PROCESS_STATE_TOP) {
                            clientProcState = PROCESS_STATE_BOUND_TOP;
                        } else {// 反之调整到BOUND_FOREGROUND_SERVICE(5),绑定前台服务
                            clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
                        }
                    }
    
                    conn.trackProcState(clientProcState, mAdjSeq, now);
                    // 如果服务端进程状态 > 客户端进程状态,则服务端跟客户端保持一致
                    if (procState > clientProcState) {
                        procState = clientProcState;
                        // 设置原始的进程状态
                        state.setCurRawProcState(procState);
                    }
                    // 如果客户端分组大于服务端分组,则将服务端分组设置为默认
                    if (cstate.getCurrentSchedulingGroup() > schedGroup) {
                        schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                    }
                    if (adjType != null) {
                        state.setAdjType(adjType);
                        state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
                                .REASON_PROVIDER_IN_USE);
                        state.setAdjSource(client);
                        state.setAdjSourceProcState(clientProcState);
                        state.setAdjTarget(cpr.name);
                        if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                            reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
                                    + ": " + app + ", due to " + client
                                    + " adj=" + adj + " procState="
                                    + ProcessList.makeProcStateString(procState));
                        }
                    }
                }
                // If the provider has external (non-framework) process
                // dependencies, ensure that its adjustment is at least
                // FOREGROUND_APP_ADJ.
                // 如果ContentProvider具有外部(非框架)进程依赖关系,请确保其调整至少为FOREGROUND_APP_ADJ
                if (cpr.hasExternalProcessHandles()) {
                    if (adj > ProcessList.FOREGROUND_APP_ADJ) {
                        adj = ProcessList.FOREGROUND_APP_ADJ;
                        state.setCurRawAdj(adj);
                        schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                        state.setCached(false);
                        state.setAdjType("ext-provider");
                        state.setAdjTarget(cpr.name);
                        if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                            reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                    "Raise adj to external provider: " + app);
                        }
                    }
                    if (procState > PROCESS_STATE_IMPORTANT_FOREGROUND) {
                        procState = PROCESS_STATE_IMPORTANT_FOREGROUND;
                        state.setCurRawProcState(procState);
                        if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                            reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                    "Raise procstate to external provider: " + app);
                        }
                    }
                }
            }
    
            // 以上ContentProvider就调整完成了
            // 大概意思是provider上一次使用的时间,设置了这个时间的话provider会延迟一小会释放(kill),
            // CONTENT_PROVIDER_RETAIN_TIME是20s,上一次使用在20s以内会进来这里,避免低内存状态下频繁启动回收provider进程
            if (ppr.getLastProviderTime() > 0
                    && (ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
                // 设置为700的优先级,也就是后台情况
                if (adj > ProcessList.PREVIOUS_APP_ADJ) {
                    adj = ProcessList.PREVIOUS_APP_ADJ;
                    // 设置后台分组
                    schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
                    state.setCached(false);
                    state.setAdjType("recent-provider");
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                "Raise adj to recent provider: " + app);
                    }
                }
                // 设置为最后显示activity的进程状态
                if (procState > PROCESS_STATE_LAST_ACTIVITY) {
                    procState = PROCESS_STATE_LAST_ACTIVITY;
                    state.setAdjType("recent-provider");
                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                        reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                "Raise procstate to recent provider: " + app);
                    }
                }
            }
    
            // 如果服务端是empty,但是客户端存在activity,将服务端进程状态调整为PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
            // 缓存进程,缓存了activity信息
            if (procState >= PROCESS_STATE_CACHED_EMPTY) {
                if (psr.hasClientActivities()) {
                    // This is a cached process, but with client activities.  Mark it so.
                    procState = PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
                    state.setAdjType("cch-client-act");
                } else if (psr.isTreatedLikeActivity()) {// 如果服务端看起来向activity,像对待activity那样对待该进程
                    // This is a cached process, but somebody wants us to treat it like it has
                    // an activity, okay!
                    // 稍微提升一点优先级
                    procState = PROCESS_STATE_CACHED_ACTIVITY;
                    state.setAdjType("cch-as-act");
                }
            }
    
            // 如果服务端adj处于(500),只有上一次lastActivity服务启动或者bind时间是30分钟以内的才会设置成SERVICE_ADJ级别
            if (adj == ProcessList.SERVICE_ADJ) {
                // 调整A/B服务
                if (doingAll && !cycleReEval) {
                    // 当A服务数量 > 服务个数 / 3 时,也就是1/3的服务会被设置成B services
                    state.setServiceB(mNewNumAServiceProcs > (mNumServiceProcs / 3));
                    mNewNumServiceProcs++;
                    // 如果排在1/3之前的服务,也就是可能会调整成A services的进程
                    if (!state.isServiceB()) {
                        // This service isn't far enough down on the LRU list to
                        // normally be a B service, but if we are low on RAM and it
                        // is large we want to force it down since we would prefer to
                        // keep launcher over it.
                        // isLastMemoryLevelNormal获取是内存压力级别 ,返回false,代表此时有内存压力
                        // getCachedRestoreThresholdKb,如果是32bit是184320/3 = 60M, 64bit是322560/3=105M,具体逻辑在ProcessList.java中
                        // 也就是进程的mLastPss大于60M(32bit)/105M(64bit),
                        // 而且是有内存压力的时候会将这类服务标记成高内存用量,而且回落到B serviceB
                        if (!mService.mAppProfiler.isLastMemoryLevelNormal()
                                && app.mProfile.getLastPss()
                                >= mProcessList.getCachedRestoreThresholdKb()) {
                            state.setServiceHighRam(true);
                            // 从A回落到B
                            state.setServiceB(true);
                            //Slog.i(TAG, "ADJ " + app + " high ram!");
                        } else {
                            mNewNumAServiceProcs++;
                            //Slog.i(TAG, "ADJ " + app + " not high ram!");
                        }
                    } else {
                        // 反之设置为该服务没有内存压力,没有高用量内存(B服务没有高用量)
                        state.setServiceHighRam(false);
                    }
                }
                // 如果是服务B,则adj设置为SERVICE_B_ADJ(800):这些都是老旧的服务,没有A列表中的那么闪亮和有趣
                if (state.isServiceB()) {
                    adj = ProcessList.SERVICE_B_ADJ;
                }
            }
    
            state.setCurRawAdj(adj);
    
            // 服务端adj如果大于了设置的最大adj,则默认使用最大adj
            // 最大不能超过getMaxAdj
            if (adj > state.getMaxAdj()) {
                adj = state.getMaxAdj();
                if (adj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
                    schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
                }
            }
    
            // Put bound foreground services in a special sched group for additional
            // restrictions on screen off
            // 如果灭屏状态且不像是activity,分组到SCHED_GROUP_RESTRICTED
            if (procState >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE
                    && mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
                    && !scheduleLikeTopApp) {
                if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) {
                    schedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
                }
            }
    
            // apply capability from FGS.
            // 进程是前台服务,则将当前进程的能力capability设置成前台服务的能力capabilityFromFGS
            if (psr.hasForegroundServices()) {
                capability |= capabilityFromFGS;
            }
    
            // 通过进程状态,如果进程状态是常驻进程或者top级别,则拥有全部能力
            // 如果进程状态是PROCESS_STATE_BOUND_TOP,则只有网络能力
            // 如果进程状态是前台服务,则只能网络能力
            capability |= getDefaultCapability(psr, procState);
    
            // Do final modification to adj.  Everything we do between here and applying
            // the final setAdj must be done in this function, because we will also use
            // it when computing the final cached adj later.  Note that we don't need to
            // worry about this for max adj above, since max adj will always be used to
            // keep it out of the cached vaues.
            // modifyRawOomAdj需要仔细分析,这里就是约束过的adj在复制给mCurAdj,而且一般都是adj调整完成后设置
            // 这里就算针对调整后的adj,再进行一次约束
            state.setCurAdj(psr.modifyRawOomAdj(adj));
            // 设置进程能力
            state.setCurCapability(capability);
            // 设置进程分组
            state.setCurrentSchedulingGroup(schedGroup);
            // 设置进程状态
            state.setCurProcState(procState);
            // 设置进程原始状态---未被约束的情况
            state.setCurRawProcState(procState);
            state.updateLastInvisibleTime(hasVisibleActivities);
            // 设置是否前台服务
            state.setHasForegroundActivities(foregroundActivities);
            // 设置此次的调整完成
            state.setCompletedAdjSeq(mAdjSeq);
    
            // if curAdj or curProcState improved, then this process was promoted
            // 只有adj调整到更底,或者进程状态调整到更底,或者能力变更了才返回true
            // 如果adj调整到更高,则返回false,但也是调整了的
            return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState
                    || state.getCurCapability() != prevCapability;
        }
    • 四大组件部分

      | 🍎 1.冷启动时ProcessRecord刚创建时ActivityThread为空,会调整adj为:CACHED_APP_MAX_ADJ(999),进程状态会调整为Cache_Empty,并调整进程分组为background,能力为none,然后设置CompletedAdjSeq,代表此次调整完成--->因为ActivityThread实际进程还没创建,所以此次只调整初始状态(empty,后台分组,最大adj)
      |
      | 2.进程开始调整时会记录当前进程状态,进程adj值,进程能力,uid等,用于调整时进行笔记
      |
      | 3.首先调整的是系统进程,即getMaxAdj <=FOREGROUND_APP_ADJ(0),将进程能力拉到最大,分组到Default,adjType设置为fixed代表固定的adj值(adj值将会是maxAdj),将进程状态拉到PROESS_STATE_PERSISTENT(常驻进程--也代表系统进程),判断该进程是否是topapp,若是则将进程分组调整到TOP_APP;满足具有activity,具有topui,都会赋予系统进程带有UI标志。 如果是topapp的情况并且亮屏的状态,进程状态会调整到persistent_ui(非常高的进程状态优先级)
      |
      | 【----所以总结一下就是优先处理系统级别进程,并把进程状态,进程能力,进程adj拉到最大, 然后判断进程是否具有topapp,topui增加带有UI的标志,如果是topapp则会重新分组到TOP_APP,若是topapp+亮屏情况则进程状态调整为persistent_ui】
      |
      | 4.其次是处理进程状态为PROCESS_STATE_TOP(优先级非常高---再往上就是persistent_ui优先级了)的进程,一般非系统进程最高是TOP,也就是当前app处于前台,那么adj默认给到0:FOREGROUND_APP_ADJ,分组到TOP_APP,进程状态再次调整为PROCESS_STATE_TOP
      |
      | 5.再者是处理是否播放动画的进程,进程adj会调整到100(VISIBLE_APP_ADJ)
      |
      | 6.再者继续处理是否正在接收广播,因为正在接收广播,所以也算是前台app,进程adj值提高到FOREGROUND_APP_ADJ(0),跟topapp和top进程一样,adjtype为broadcast,进程状态为:PROCESS_STATE_RECEIVER(正在接收广播,优先级比service要高)
      |
      | 7.再者继续处理是否有正在运行的service服务,进程adj调整到FOREGROUND_APP_ADJ,同广播一样,进程状态调整为PROCESS_STATE_SERVICE(没有广播优先级高),adjtype为exec-service
      |
      | 8.再者继续处理topapp,进程adj也是FOREGROUND_APP_ADJ,分组属于SCHED_GROUP_BACKGROUND,进程状态PROCESS_STATE_TOP
      |
      | 9.如果不是top进程,不是正在接收广播,不是正在运行的服务,不是topapp,那么则将进程adj调整为cacheAdj,进程状态调整为empty---如果超过16个empty进程则会被kill
      |
      | 10.再处理非前台activity,但是存在activity的进程情况(foregroundActivities目前只有top app才是true):会跟进activity是否可见,是否onPause,是否stop,通过activity不同的状态计算其adj,去重新计算adj值,例如activity属于visible状态,则adj调整为VISIBLE_APP_ADJ,调整到默认分组,并设置foregroundActivities为true,代表top app
      |
      | 11.以上进程的四大组件状态就处理完成了
      |
      | 12.然后开始处理进程adj大于可感知优先级和进程状态大于前台服务的情况---代表进程状态低迷,容易被kill,如果发现此类进程仍然存在前台service,则会赋予可感知adj值(PERCEPTIBLE_APP_ADJ---200),进程状态为前台服务(PROCESS_STATE_FOREGROUND_SERVICE),拉高优先级,防止被kill
      |
      | 13.然后在处理前台服务的情况---如果ForegroundServices最近不在前台运行,在一定时间内(15s),继续像对待前台进程那样对待该前台服务--换个说法--调整最近活跃的前台服务进程,在一段时间内(15s),继续像对待前台应用程序一样对待它,进程adj又会被重新拉高到PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,但进程状态不变,相当于垂死挣扎15s
      |
      | 14.然后再处理进程adj大于可感知,且调用AMS的setProcessImportant设置-->强制设置为进程重要状态,那么进程adj会被重新拉回到可感知进程adj值,这个作用就是强制将优先级不高的进程,提高优先级,拉高保活率
      |
      | 15.然后再处理home进程(launcher),此时进程adj会被赋予600(HOME_APP_ADJ),会被赋予SCHED_GROUP_BACKGROUND和PROCESS_STATE_HOME进程状态(取决于当前launcher的adj值是否处于后台,>600),因为launcher属于桌面,需要长久在后台运行,总不能我在使用其他app,返回桌面的时候发现launcher被kill没了...
      |
      | 16.然后在处理app进程被按下home,返回键,后台,不可见的情况---这种情况属于又另外启动了activity进程,那么该进程状态则为PREVIOUS_APP_ADJ(上次),则回调整到后台分组,并设置进程adj为700

    • 服务端(Service)与客户端部分

      | 🍎 【Service服务端】---service调整,只调整进程 > FOREGROUND_APP_ADJ(0)和分组属于back group和进程状态 >PROCESS_STATE_TO的情况,简单的说就是服务端进程如果处于活跃状态,前台状态,不调整,只调整service进程低迷的情况
      |
      | 1.获取该进程正在运行的所有服务,循环调整,每次取出服务端对象:ServiceRecord ,如果发现正在运行则会将进程状态调整为PROCESS_STATE_SERVICE
      |
      | 2.然后如果发现该service运行/活跃的时间在30分钟以内,则会赋予SERVICE_ADJ进程adj值;如果超过30分钟,并且没有客户端绑定使用,则会自动销毁----表示服务在没有任何客户端绑定或调用它的情况下可以保持运行的最长时间,超过这个时间,系统将自动停止该服务以释放系统资源
      |
      | 3.然后开始处理是前台服务的情况isForeground:会根据androidManifest声明的前台服务能力(网络,信息位置,相机,麦克风)等标签,提供对应的访问能力
      |
      | 4.然后获取每个service对应客户端的连接对象:服务对应了一个ServiceRecord,客户端通过bindService(Intent,ServiceConnection,flag)来连接服务端,所以获取服务端连接对象就是ConnectionRecord(ServiceConnection),因为一个服务会被多个客户端进行连接,所以这里获取了服务端对端的所有连接对象。主要作用就是根据客户端进程状态,调整服务端进程状态,依赖性
      |
      | 5.如果发现连接对象ConnectionRecord(连接对象属于服务端持有)的客户端(发起连接者),属于同一个进程,例如自己进程创建服务端,自己进程的activity去bindService,则是同一个进程,这种就不会调整进程优先级了(因为服务端和客户端属于同一个进程,没有依赖性,不做调整)
      |
      | 6.如果computeClients为真,就会根据服务端进程调整的时候,同时去调整客户端进程状态,会继续调用computeOomAdjLSP(client/传入了客户端的app/...)计算客户端adj值
      |
      | 7.如果客户端shouldNotFreeze返回true表示不会被冻结,那么服务端也不会被冻结,表示两者的状态同步
      |
      | 8.然后就是开始处理各种连接对象持有的flags,因为连接是客户端连接的,可以发起flag进行连接,例如:
      |
      | 1).不带有该flag,Context.BIND_WAIVE_PRIORITY,表示不影响 Service 进程的优先级的情况下,允许 Service 进程被加入后台队列中
      |
      | 2).带有Context.BIND_INCLUDE_CAPABILITIES,表示会将客户端的能力全部赋值给服务端
      |
      | 3).带有PROCESS_CAPABILITY_NETWORK,表示客户端具有网络能力,那么服务端也会被赋予网络能力---其他能力同此一样
      |
      | 4).带有Context.BIND_ALLOW_OOM_MANAGEMENT,表示允许服务所在的进程进行正常的内存管理,托管管理,也就是更容易被杀死:允许oom管理Service的进程,允许系统在内存不足的时候删除这个进程----如果服务端具有UI(例如存在activity),那么会将服务端的adj赋值给客户端adj,保持一样的adj(一般是客户端改变服务端的adj,服务端最多可以让客户端跟自己保持一样的adj)
      |
      | 9.如果客户端adj < 服务端adj,也就是客户端进程优先级比服务端要高,将降低服务端的adj值,拉高优先级。
      |
      | 1).如果包含BIND_ABOVE_CLIENT(代表服务的优先级需要比客户端client的高)或者BIND_IMPORTANT(代表此服务很重要,adj可以到0(fg)或-700(PERSISTENT_SERVICE))的时候,客户端属于持久常驻进程,那么服务端也会被调整为-700(跟客户端一样)
      |
      | 2).如果带有Context.BIND_NOT_PERCEPTIBLE,表示不可感知,那么服务端的adj也会被设置为PERCEPTIBLE_LOW_APP_ADJ,表示不可感知优先级
      |
      | 3).如果带有BIND_ALMOST_PERCEPTIBLE,表示服务端所在进程被视为对用户来说就像一个可感知的应用程序一样重要,并且客户端可感知的情况下(小于PERCEPTIBLE_APP_ADJ),服务端则会被赋予可感知的优先级--PERCEPTIBLE_MEDIUM_APP_ADJ
      |
      | 4).以及客户端adj < 服务端adj时(客户端可感知,服务端不可感知),则会将服务端的adj设置为至少可感知级别(PERCEPTIBLE_APP_ADJ)
      |
      | 5).如果客户端adj >=PERCEPTIBLE_APP_ADJ可感知级别,代表客户端刚好可以感知或者不可感知的情况,那么服务端也会被赋予跟客户端一样的adj
      |
      | 10.然后开始处理后台服务,但是又不重要的后台服务,例如不具有BIND_IMPORTANT_BACKGROUND标志,但发现客户端很强,客户端进程状态 < PROCESS_STATE_TOP的情况,客户端进程状态变更为PROCESS_STATE_IMPORTANT_FOREGROUND,注意是客户端的进程状态被改变
      |
      | 11.如果后台服务具有BIND_IMPORTANT_BACKGROUND标志,则会将客户端的进程状态改变为BIND_IMPORTANT_BACKGROUND---->通过服务端改变了客户端的进程状态,也仅仅只是相等的进程状态而已
      |
      | 12.如果具有Context.BIND_SCHEDULE_LIKE_TOP_APP且服务端分组又小于TOP_APP,那么则会将服务端分配到TOP_APP组,因为该标志表示要像对待activity那样对待该服务
      |
      | 13.如果客户端adj < 服务端adj,那么会将客户端adj赋值给服务端,让服务端进程优先级拉高
      |
      | 14.然后开始获取客户端的activity(如果客户端具有activity),可以根据客户端activity是否可见来调整服务端优先级,例如客户端activity可见且属于top app,那么服务端会被赋予FOREGROUND_APP_ADJ进程adj值,如果是重要的后台服务,则会分组到TOP_APP_BOUND组内,反之default组内

      什么时候客户端adj会根据服务端调整?拥有BIND_ALLOW_OOM_MANAGEMENT的时候会被调整

      什么时候客户端进程状态会根据服务端调整?客户端进程状态 < PROCESS_STATE_TOP的时候,会根据是否具有前台服务,赋予STATE_BOUND_FORCESERVICE状态,如果没有前台服务,则会赋予PROCESS_STATE_IMPORTANT_FOREGROUND----第10点和第11点

    • 服务端(ContentProvider)与客户端部分

      | 💡 【服务端】---ContentProvider 1.获取该进程正在运行的所有内容提供者,循环调整,每次取出服务端对象:ContentProviderRecord
      |
      | 2.然后获取每个服务端的连接端ContentProviderConnection(客户端发起连接),如果是客户端和服务端是同一个进程,则不做调整---因为此次调整是客户端和服务端依赖关系的进程调整,相同进程不存在依赖关系
      |
      | 3.如果computeClients为真,就会根据服务端进程调整的时候,同时去调整客户端进程状态,会继续调用computeOomAdjLSP(client/传入了客户端的app/...)计算客户端adj值
      |
      | 4.如果客户端进程状态属于PROCESS_STATE_CACHED_ACTIVITY,则会默认重新赋值到empty进程状态,随时被kill
      |
      | 5.如果客户端应该不会被冻结,那么服务端也会被设置为不被冻结
      |
      | 6.如果客户端adj < 服务端adj的情况,如果客户端adj > FOREGROUND_APP_ADJ(0)前台adj,则服务端也保持跟客户端一样的adj,反之客户端adj <=前台adj,则默认给服务端赋予 FOREGROUND_APP_ADJ,因为客户端如果是持久进程,那么服务端不可能也给到持久进程状态和adj,所以最多只能给到 FOREGROUND_APP_ADJ
      |
      | 7.如果客户端进程状态 <= 前台服务 ,说明客户端优先级较高,那么adjtype会被赋予provider;如果客户端进程状态是PROCESS_STATE_TOP的情况,则会调整到PROCESS_STATE_BOUND_TOP,如果非TOP则会调整到BOUND_FOREGROUND_SERVICE;
      |
      | 8.如果服务端进程状态 > 客户端进程状态,那么需要调整服务端进程状态(向客户端看齐),客户端会将自己的进程状态赋值给服务端
      |
      | 9.客户端分组 > 服务端分组,也会将服务端分组调整为default
      |
      | 10.如果服务端的hasExternalProcessHandles为真,说明有外部依赖关系(我想可能是服务端持有了某些数据,依赖了客户端之类),并发现服务端进程adj值非前台(非FOREGROUND_APP_ADJ),则会直接赋予前台adj值,进程状态也会被赋予PROCESS_STATE_IMPORTANT_FOREGROUND----也就是服务端非前台adj的时候,具有该标志性,会将服务端的adj和进程状态优先级拉高,保活率更高
      |
      | 11.然后就是近期使用的服务端,在20s以内,会被赋予700(PREVIOUS_APP_ADJ),adjtype为recent-provider,进程状态为PROCESS_STATE_LAST_ACTIVITY
      |
      | 12.如果服务端是empty进程状态,但又客户端又具有activity,服务端进程状态会被调整为PROCESS_STATE_CACHED_ACTIVITY_CLIENT---cache activity client;如果服务端被标志为像对待activity那样对待服务端,那么服务端进程状态会被调整为PROCESS_STATE_CACHED_ACTIVITY----也就是从empty进程状态拉高了优先级,拉高了一点,提高了一点保活率
      |
      | 13.如果服务端启动时间在30分钟以内,并且有客户端连接的情况,1/3的服务端会被标记为ServiceB;其余的可能会被标为ServiceA,然后获取内存压力级别,32bit是184320/3 = 60M, 64bit是322560/3=105M,具体逻辑在ProcessList.java中,也就是进程的mLastPss大于60M(32bit)/105M(64bit),而且是有内存压力的时候会将这类服务标记成高内存用量,会从A回落到serviceB。如果是服务B,则adj设置为SERVICE_B_ADJ(800):这些都是老旧的服务,没有A列表中的那么闪亮和有趣---更容易被回收,也是因为高内存用量

  • applyOomAdjLSP

相关推荐
冰帝海岸21 分钟前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象1 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了1 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·1 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic2 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王2 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康2 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神2 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
小喵要摸鱼3 小时前
Python 神经网络项目常用语法
python
qq_327342733 小时前
Java实现离线身份证号码OCR识别
java·开发语言