Android 重學(xué)系列 Activity的啟動(dòng)流程(一)

如果遇到錯(cuò)誤,請(qǐng)?jiān)诒疚牡刂罚?http://www.itdecent.cn/p/91feec107d4b

背景

經(jīng)過前期的奮斗,我們終于來到Android開發(fā)者熟悉的部分,四大組件之一的Activity。Activity可以說是每個(gè)Android程序員最為熟悉的組件,其承載App應(yīng)用和人交互的橋梁。因此想要明白Android我們必須要把Activity摸透。由于我在3年前已經(jīng)寫過一次Activity的啟動(dòng)流程的解析,因此這次我嘗試著盡可能的看到詳細(xì)的源碼,來和大家聊聊在Android 9.0中的Activity的啟動(dòng)。

正文

比較Android9.0和Android 7.0

首先看看startActivity在Android7.0的時(shí)序圖。


Activity的啟動(dòng)流程.jpg

這里實(shí)際上就是從Android誕生之初,一直到Android 9.0的啟動(dòng)主要脈絡(luò)。實(shí)際上外面的文章已經(jīng)講得滾瓜爛熟。包括我也看這些源碼了這個(gè)不下10遍,應(yīng)該更多。

本文會(huì)繼續(xù)看看Android 9.0的Acitvity啟動(dòng)流程的源碼??纯碅ndroid 9.0比起7.0來說進(jìn)步在哪里。

我們先上時(shí)序圖:


Android O Activity啟動(dòng)流程.png

注意:紅色線代表跨越Binder一次進(jìn)程

從時(shí)序圖上,無論怎么Android的啟動(dòng)架構(gòu)怎么演變,其根本流程都沒有變。Android都是通過Binder通行到AMS,接著經(jīng)過AMS的一系列中棧處理之后,把ActivityRecord返回到AppThread(App進(jìn)程中)。

Android9.0看起來確實(shí)比Android7.0的經(jīng)歷的步驟更多,但是我們深入看源碼發(fā)現(xiàn)Android9.0比起7.0的易讀(如果對(duì)4.4的啟動(dòng)流程可以看我畢業(yè)那篇文章)。

主要的原因,我們可以從Android 9.0的時(shí)序圖中可以到,在Android 9.0中,AMS不再是通過簡(jiǎn)單的調(diào)用IPC來控制App端的Activity生命周期。而是通過一個(gè)狀態(tài)設(shè)計(jì)模式,將每個(gè)Activity每一個(gè)生命周期都抽象成一個(gè)狀態(tài),接著通過狀態(tài)機(jī)去管理整個(gè)生命周期。

實(shí)際上這種思想在我平時(shí)寫代碼的時(shí)候,也經(jīng)常用到。往往用在需求比較復(fù)雜,而且狀態(tài)比較多,還會(huì)不斷切換那種。

本文將會(huì)著重打討論AMS中的流程。再次聊App進(jìn)程準(zhǔn)備工作也未免有點(diǎn)濫竽充數(shù)的嫌疑。讓我們站在更高的高度,去看看AMS在啟動(dòng)中的細(xì)節(jié)以及設(shè)計(jì)思路。

提示:從上面幾篇文章能看到,實(shí)際上AMS隸屬于SystemServer進(jìn)程。和App進(jìn)程不在同一處。

啟動(dòng)流程中AMS內(nèi)的各個(gè)角色

在Activity中啟動(dòng)中,AMS擔(dān)任最為重要的角色,下面列出的都是AMS中承擔(dān)各個(gè)主要功能的類

    1. ActivityStack 代表著Activity的棧(不精準(zhǔn)稍后會(huì)具體解釋)
  • 2.ActivityStarter 代表著Activity正式啟動(dòng)的控制類
  • 3.ActivityManagerService 代表著一切Activity行為在系統(tǒng)中的控制中心
  • 4.ActivityStackSupervisor 代表著ActivityStack的監(jiān)控中心

實(shí)際上對(duì)于我們來說在整個(gè)Activity的啟動(dòng)需要關(guān)注這么四個(gè)核心類。
而在這里面往往涉及到Activity棧的變化,而這個(gè)過程涉及到的核心類有:

  • 1.ActivityRecord
  • 2.TaskRecord
  • 3.mRecentTasks
  • 4.mTaskHistory
  • 5.ProcessRecord

我們稍微根據(jù)源碼時(shí)序圖,來看看AMS中的啟動(dòng)流程。上面的數(shù)據(jù)結(jié)構(gòu)將會(huì)一一剖析。

AMS跨進(jìn)程通信創(chuàng)建Activity,第一步。

文件:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
            boolean validateIncomingUser) {
        enforceNotIsolatedCaller("startActivity");

        userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");


        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)//調(diào)用方的AppThread的IBinder
                .setCallingPackage(callingPackage)//調(diào)用方的包名
                .setResolvedType(resolvedType)//調(diào)用type
                .setResultTo(resultTo)//調(diào)用方的ActivityClientRecord的binder(實(shí)際上是AMS的ActivityRecord對(duì)應(yīng)在App端的binder對(duì)象)
                .setResultWho(resultWho)//調(diào)用方的標(biāo)示
                .setRequestCode(requestCode)//需要返回的requestCode
                .setStartFlags(startFlags)//啟動(dòng)標(biāo)志位
                .setProfilerInfo(profilerInfo)//啟動(dòng)時(shí)帶上的權(quán)限文件對(duì)象
                .setActivityOptions(bOptions)//ActivityOptions的Activity的啟動(dòng)項(xiàng),在一般的App中此時(shí)是null,不需要關(guān)注
                .setMayWait(userId)//是否是同步打開Actvivity 默認(rèn)一般是true
                .execute();//執(zhí)行方法。

從這里面節(jié)能很清晰的明白,在啟動(dòng)過程中需要什么參數(shù)。比起當(dāng)初Android7.0的源碼看來設(shè)計(jì)上確實(shí)進(jìn)步不少,雖然看起來像是一個(gè)建造者設(shè)計(jì)模式。但是實(shí)際上工廠設(shè)計(jì)模式+享元設(shè)計(jì)+鏈?zhǔn)秸{(diào)用。通過obtainStarter把DefaultFactory從mStarterPool中獲取一個(gè)ActivityStarter(池子中最多設(shè)置3個(gè)),接著通過鏈?zhǔn)秸{(diào)用,把啟動(dòng)時(shí)需要的參數(shù)傳遞進(jìn)去。

當(dāng)設(shè)置完成之后,我們直接看看execute方法.

ActivityStarter 正式開始啟動(dòng)Activity

文件:/frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java

 int execute() {
        try {
            // TODO(b/64750076): Look into passing request directly to these methods to allow
            // for transactional diffs and preprocessing.
            if (mRequest.mayWait) {
                return startActivityMayWait(mRequest.caller, mRequest.callingUid,
                        mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
                        mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
                        mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
                        mRequest.inTask, mRequest.reason,
                        mRequest.allowPendingRemoteAnimationRegistryLookup);
            } else {
           ...
            }
        } finally {
            onExecutionComplete();
        }
    }

從execute我們可以看到,在這個(gè)過程Google工程師靈活的運(yùn)用了try-final機(jī)制,通過onExecutionComplete在ActivityStartController清除數(shù)據(jù)放回startPool池子中。提一句實(shí)際上這種設(shè)計(jì),我在Glide等經(jīng)典第三方庫已經(jīng)看到了無數(shù)遍。

此時(shí)我們是一個(gè)同步操作,所以看看startActivityMayWait方法。

startActivityMayWait

這段我們分為三段來看:

1.從PackageManagerService準(zhǔn)備activity需要的數(shù)據(jù)
private int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult,
            Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
            int userId, TaskRecord inTask, String reason,
            boolean allowPendingRemoteAnimationRegistryLookup) {
      ....
      
        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                0 /* matchFlags */,
                        computeResolveFilterUid(
                                callingUid, realCallingUid, mRequest.filterCallingUid));
        if (rInfo == null) {
            UserInfo userInfo = mSupervisor.getUserInfo(userId);
            if (userInfo != null && userInfo.isManagedProfile()) {
                // Special case for managed profiles, if attempting to launch non-cryto aware
                // app in a locked managed profile from an unlocked parent allow it to resolve
                // as user will be sent via confirm credentials to unlock the profile.
                UserManager userManager = UserManager.get(mService.mContext);
                boolean profileLockedAndParentUnlockingOrUnlocked = false;
                long token = Binder.clearCallingIdentity();
                try {
                    UserInfo parent = userManager.getProfileParent(userId);
                    profileLockedAndParentUnlockingOrUnlocked = (parent != null)
                            && userManager.isUserUnlockingOrUnlocked(parent.id)
                            && !userManager.isUserUnlockingOrUnlocked(userId);
                } finally {
                    Binder.restoreCallingIdentity(token);
                }
                if (profileLockedAndParentUnlockingOrUnlocked) {
                    rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                            PackageManager.MATCH_DIRECT_BOOT_AWARE
                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                            computeResolveFilterUid(
                                    callingUid, realCallingUid, mRequest.filterCallingUid));
                }
            }
        }
        // Collect information about the target of the Intent.
        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);

可以大致分為以下3步:
1.從ActivityStackSupervisor調(diào)用PMS獲取ResolveInfo。

private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
            int flags, int userId, boolean resolveForStart, int filterCallingUid) {
        try {

            if (!sUserManager.exists(userId)) return null;
            final int callingUid = Binder.getCallingUid();
            flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart);
            mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                    false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");

            final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
                    flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/);

            final ResolveInfo bestChoice =
                    chooseBestActivity(intent, resolvedType, flags, query, userId);
            return bestChoice;
        } finally {
          ....
        }
    }

從上面代碼,我們可以看到,這個(gè)方法是通過intent來從找到一個(gè)最合適的選擇。我們可以推測(cè),實(shí)際上這個(gè)ResolveInfo是指當(dāng)我們安裝了App之后,加載到PackageManagerService(后面稱PMS)系統(tǒng)中的AndroidManifest.xml的數(shù)據(jù)。

queryIntentActivitiesInternal分步驟來說:

  • 1.查看當(dāng)前Intent是否是顯式Intent。是則取出其中的class對(duì)象和AndroidManifest的進(jìn)行匹配,匹配成功返回。
  • 2.如果沒有指定包名則全系統(tǒng)的查找匹配intent
  • 3.如果指定包名,則從當(dāng)前的包名尋找匹配規(guī)則相符合的intent的Activity

因此此時(shí)可能會(huì)匹配多個(gè)合適的Intent,再通過chooseBestActivity進(jìn)一步篩選Activity。

為什么加上這一段,實(shí)際上這一段有一個(gè)關(guān)鍵的邏輯就是AppLink。開發(fā)經(jīng)常用到,在AndroidManifest中設(shè)置好schme等Intent參數(shù),讓外部app來喚醒我們自己的app。
當(dāng)喚醒的目的地只有一個(gè)直接返回,如果有多個(gè)則替換intent中的類,變成系統(tǒng)的ResolveActivity。用來選擇我們的目的App,如下圖。


image.png
  • 2.查不到ResolveInfo則嘗試從直接啟動(dòng)中獲取
    自Android 5.0之后。Android系統(tǒng)將開始支持多用戶系統(tǒng),這些用戶的配置都由UserManager控制,其中AccountManager則是控制每個(gè)用戶下的賬號(hào)。

在Android7.0之后,為應(yīng)用新增了一種啟動(dòng)模式Direct Boot(直接啟動(dòng)模式)。這種模式是指設(shè)備啟動(dòng)后進(jìn)入的一個(gè)新模式,直到用戶解鎖(unlock)設(shè)備此階段結(jié)束。這種模式,會(huì)為程序創(chuàng)建Device protected storage私有的存儲(chǔ)空間。

這種模式比較特殊,我們需要在AndroidManifest中設(shè)置 android:directBootAware="true"。

因此,這種模式下,需要喚醒特殊的Activity,確定此時(shí)已經(jīng)解鎖,需要從特殊的私有空間去查找對(duì)應(yīng)的ResolveInfo。

  • 3.通過PMS的getActivityInfo讀取ActivityInfo

當(dāng)我們確定好了ResolveInfo,就要AMS就通過resolveActivity從PMS讀取ResolveInfo中的Activity信息。

   ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
            ProfilerInfo profilerInfo) {
        final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
        if (aInfo != null) {
            intent.setComponent(new ComponentName(
                    aInfo.applicationInfo.packageName, aInfo.name));

            // Don't debug things in the system process
            if (!aInfo.processName.equals("system")) {
                if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
                    mService.setDebugApp(aInfo.processName, true, false);
                }

   ...
        return aInfo;
    }

找到后,就顯示的設(shè)置ComponentName,包名和類名。

2.處理重量級(jí)進(jìn)程
  synchronized (mService) {
            final ActivityStack stack = mSupervisor.mFocusedStack;
            stack.mConfigWillChange = globalConfig != null
                    && mService.getGlobalConfiguration().diff(globalConfig) != 0;
...

            final long origId = Binder.clearCallingIdentity();

            if (aInfo != null &&
                    (aInfo.applicationInfo.privateFlags
                            & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 &&
                    mService.mHasHeavyWeightFeature) {
                // This may be a heavy-weight process!  Check to see if we already
                // have another, different heavy-weight process running.
                if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
                    final ProcessRecord heavy = mService.mHeavyWeightProcess;
                    if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid
                            || !heavy.processName.equals(aInfo.processName))) {
                        int appCallingUid = callingUid;
                        if (caller != null) {
                            ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
                            if (callerApp != null) {
                                appCallingUid = callerApp.info.uid;
                            } else {
                                Slog.w(TAG, "Unable to find app for caller " + caller
                                        + " (pid=" + callingPid + ") when starting: "
                                        + intent.toString());
                                SafeActivityOptions.abort(options);
                                return ActivityManager.START_PERMISSION_DENIED;
                            }
                        }

                        IIntentSender target = mService.getIntentSenderLocked(
                                ActivityManager.INTENT_SENDER_ACTIVITY, "android",
                                appCallingUid, userId, null, null, 0, new Intent[] { intent },
                                new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
                                        | PendingIntent.FLAG_ONE_SHOT, null);

                        Intent newIntent = new Intent();
                        if (requestCode >= 0) {
                            // Caller is requesting a result.
                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
                        }
                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
                                new IntentSender(target));
                        if (heavy.activities.size() > 0) {
                            ActivityRecord hist = heavy.activities.get(0);
                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
                                    hist.packageName);
                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
                                    hist.getTask().taskId);
                        }
                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
                                aInfo.packageName);
                        newIntent.setFlags(intent.getFlags());
                        newIntent.setClassName("android",
                                HeavyWeightSwitcherActivity.class.getName());
                        intent = newIntent;
                        resolvedType = null;
                        caller = null;
                        callingUid = Binder.getCallingUid();
                        callingPid = Binder.getCallingPid();
                        componentSpecified = true;
                        rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId,
                                0 /* matchFlags */, computeResolveFilterUid(
                                        callingUid, realCallingUid, mRequest.filterCallingUid));
                        aInfo = rInfo != null ? rInfo.activityInfo : null;
                        if (aInfo != null) {
                            aInfo = mService.getActivityInfoForUser(aInfo, userId);
                        }
                    }
                }
            }

這個(gè)重量級(jí)進(jìn)程實(shí)際上很早就存在,但是允許我們?cè)O(shè)置是在sdk 28(Android 9.0)之后才能開放給我們。

重量級(jí)的進(jìn)程一般是指在整個(gè)系統(tǒng)唯一存在一個(gè)進(jìn)程,不會(huì)正常走保存恢復(fù)機(jī)制,而是一直運(yùn)行在后臺(tái),不會(huì)被后臺(tái)殺死,因此需要用戶顯示退出進(jìn)入該進(jìn)程。

而這段代碼就是當(dāng)后臺(tái)已經(jīng)啟動(dòng)了一個(gè)重量進(jìn)程的時(shí)候,用戶又一次想要啟動(dòng)另一個(gè)重量級(jí)進(jìn)程,就會(huì)彈出一個(gè)界面讓用戶進(jìn)行選擇。


image.png

就是這個(gè)彈窗選擇界面。這種行為一般是給游戲這種極其消耗資源的進(jìn)程處理。

3.進(jìn)行下一步的啟動(dòng)

 final ActivityRecord[] outRecord = new ActivityRecord[1];
            int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
                    ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
                    allowPendingRemoteAnimationRegistryLookup);

...
 if (outResult != null) {
                outResult.result = res;

                final ActivityRecord r = outRecord[0];

                switch(res) {
                    case START_SUCCESS: {
                        mSupervisor.mWaitingActivityLaunched.add(outResult);
                        do {
                            try {
                                mService.wait();
                            } catch (InterruptedException e) {
                            }
                        } while (outResult.result != START_TASK_TO_FRONT
                                && !outResult.timeout && outResult.who == null);
                        if (outResult.result == START_TASK_TO_FRONT) {
                            res = START_TASK_TO_FRONT;
                        }
                        break;
                    }
                    case START_DELIVERED_TO_TOP: {
                        outResult.timeout = false;
                        outResult.who = r.realActivity;
                        outResult.totalTime = 0;
                        outResult.thisTime = 0;
                        break;
                    }
                    case START_TASK_TO_FRONT: {
                        // ActivityRecord may represent a different activity, but it should not be
                        // in the resumed state.
                        if (r.nowVisible && r.isState(RESUMED)) {
                            outResult.timeout = false;
                            outResult.who = r.realActivity;
                            outResult.totalTime = 0;
                            outResult.thisTime = 0;
                        } else {
                            outResult.thisTime = SystemClock.uptimeMillis();
                            mSupervisor.waitActivityVisible(r.realActivity, outResult);
                            // Note: the timeout variable is not currently not ever set.
                            do {
                                try {
                                    mService.wait();
                                } catch (InterruptedException e) {
                                }
                            } while (!outResult.timeout && outResult.who == null);
                        }
                        break;
                    }
                }
            }

我們可以看到進(jìn)行下一個(gè)的啟動(dòng)之后,如果返回的狀態(tài)碼START_SUCCESS,就會(huì)阻塞AMS,等待喚醒。

一般來說如果startActivity正常完成了整個(gè)流程就返回狀態(tài)代碼為START_SUCCESS.進(jìn)入到了第一次阻塞狀態(tài),而這個(gè)阻塞會(huì)不斷判斷的當(dāng)前的狀態(tài)是否是START_TASK_TO_FRONT,是才退出。然而如果此時(shí)Activity是第一次創(chuàng)建,則通過ActivityStarter.startActivitySTART_SUCCESS后,通過postStartActivityProcessing 把結(jié)果轉(zhuǎn)化為START_TASK_TO_FRONT.因此能夠立即退出當(dāng)前的狀態(tài)。

    private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                ActivityRecord[] outActivity) {
        int result = START_CANCELED;
        try {
            mService.mWindowManager.deferSurfaceLayout();
            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, outActivity);
        } finally {
     ...
        }

        postStartActivityProcessing(r, result, mTargetStack);

        return result;
    }

我們先把目光放在ActivityStartController.startActivity這個(gè)后續(xù)核心方法中。

startActivity處理ActivityInfo轉(zhuǎn)化為ActivityRecord

這里分為3步聊聊:

1.準(zhǔn)備ActivtyRecord的基礎(chǔ)數(shù)據(jù)

private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
            SafeActivityOptions options,
            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
            TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup) {
        int err = ActivityManager.START_SUCCESS;
        // Pull the optional Ephemeral Installer-only bundle out of the options early.
        final Bundle verificationBundle
                = options != null ? options.popAppVerificationBundle() : null;

        ProcessRecord callerApp = null;
        if (caller != null) {
            callerApp = mService.getRecordForAppLocked(caller);
            if (callerApp != null) {
                callingPid = callerApp.pid;
                callingUid = callerApp.info.uid;
            } else {
               ...
            }
        }

        final int userId = aInfo != null && aInfo.applicationInfo != null
                ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;

        ....
        ActivityRecord sourceRecord = null;
        ActivityRecord resultRecord = null;
        if (resultTo != null) {
            sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
...
            if (sourceRecord != null) {
                if (requestCode >= 0 && !sourceRecord.finishing) {
                    resultRecord = sourceRecord;
                }
            }
        }

   ....
    }

為了實(shí)例化ActivityRecord,Android系統(tǒng)通過IApplicationThread獲取當(dāng)前Activity所處在進(jìn)程數(shù)據(jù)。

也就是調(diào)用 mService.getRecordForAppLocked(caller); 獲取ProcessRecord。

AMS尋找進(jìn)程

我們追蹤一下AMS是怎么通過IApplicationThread這個(gè)AppThread遠(yuǎn)程binder對(duì)象獲得的。

    private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
        final IBinder threadBinder = thread.asBinder();
        // Find the application record.
        for (int i=mLruProcesses.size()-1; i>=0; i--) {
            final ProcessRecord rec = mLruProcesses.get(i);
            if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
                return i;
            }
        }
        return -1;
    }

 ProcessRecord getRecordForAppLocked(IApplicationThread thread) {
        if (thread == null) {
            return null;
        }

        int appIndex = getLRURecordIndexForAppLocked(thread);
        if (appIndex >= 0) {
            return mLruProcesses.get(appIndex);
        }

        // Validation: if it isn't in the LRU list, it shouldn't exist, but let's
        // double-check that.
        final IBinder threadBinder = thread.asBinder();
        final ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
        for (int i = pmap.size()-1; i >= 0; i--) {
            final SparseArray<ProcessRecord> procs = pmap.valueAt(i);
            for (int j = procs.size()-1; j >= 0; j--) {
                final ProcessRecord proc = procs.valueAt(j);
                if (proc.thread != null && proc.thread.asBinder() == threadBinder) {
                    Slog.wtf(TAG, "getRecordForApp: exists in name list but not in LRU list: "
                            + proc);
                    return proc;
                }
            }
        }

        return null;
    }

從這里我們稍微能看出Google對(duì)性能的追求。在整個(gè)AMS中,有兩個(gè)數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)著進(jìn)程對(duì)象:

1.mLruProcesses 一個(gè)LRU的ArrayList存儲(chǔ)著數(shù)據(jù)。這個(gè)數(shù)據(jù)結(jié)構(gòu)雖然不是我們常用的LRUMap(LinkHashMap 經(jīng)過處理后能夠自動(dòng)處理LRU算法,將會(huì)在算法專欄和大家聊聊)最近最少使用算法。但是Google 工程師選擇自己處理。
2.mProcessNames 存儲(chǔ)著所有進(jìn)程的數(shù)據(jù),可以通過Binde的引用名反過來找到進(jìn)程的數(shù)據(jù)。

所以用我們常用的話來說,AMS在進(jìn)程查找中用了二級(jí)緩存。

趁熱打鐵看看進(jìn)程是怎么更新的LRU算法:


    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
            ProcessRecord client) {
        final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
                || app.treatLikeActivity || app.recentTasks.size() > 0;
        final boolean hasService = false; // not impl yet. app.services.size() > 0;
        if (!activityChange && hasActivity) {
            // The process has activities, so we are only allowing activity-based adjustments
            // to move it.  It should be kept in the front of the list with other
            // processes that have activities, and we don't want those to change their
            // order except due to activity operations.
            return;
        }

        mLruSeq++;
        final long now = SystemClock.uptimeMillis();
        app.lastActivityTime = now;

        // First a quick reject: if the app is already at the position we will
        // put it, then there is nothing to do.
        //步驟一
        if (hasActivity) {
            final int N = mLruProcesses.size();
            if (N > 0 && mLruProcesses.get(N-1) == app) {
...
                return;
            }
        } else {
            if (mLruProcessServiceStart > 0
                    && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
...
                return;
            }
        }
      //獲取最近使用相同ProcessRecord的索引
        int lrui = mLruProcesses.lastIndexOf(app);

....

        if (lrui >= 0) {
            if (lrui < mLruProcessActivityStart) {
                mLruProcessActivityStart--;
            }
            if (lrui < mLruProcessServiceStart) {
                mLruProcessServiceStart--;
            }
        ...
            mLruProcesses.remove(lrui);
        }

       ...

        int nextIndex;
        if (hasActivity) {
            //處理Activity詳細(xì)看下面帶著Activity的進(jìn)程情況套路
        } else if (hasService) {
            // Process has services, put it at the top of the service list.
...
            mLruProcesses.add(mLruProcessActivityStart, app);
            nextIndex = mLruProcessServiceStart;
            mLruProcessActivityStart++;
        } else  {
//詳細(xì)看處理沒有Actvity以及Service的進(jìn)程
        }

        // If the app is currently using a content provider or service,
        // bump those processes as well.
        for (int j=app.connections.size()-1; j>=0; j--) {
            ConnectionRecord cr = app.connections.valueAt(j);
            if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
                    && cr.binding.service.app != null
                    && cr.binding.service.app.lruSeq != mLruSeq
                    && !cr.binding.service.app.persistent) {
                nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
                        "service connection", cr, app);
            }
        }
        for (int j=app.conProviders.size()-1; j>=0; j--) {
            ContentProviderRecord cpr = app.conProviders.get(j).provider;
            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
                        "provider reference", cpr, app);
            }
        }
    }
額外知識(shí)的補(bǔ)充

要看懂這一段邏輯,我們必須要普及一個(gè)基礎(chǔ)知識(shí),Android的uid和userId。

雖然Android是沿用Linux內(nèi)核,其uid充滿著迷惑性。android的uid和Linux的uid有區(qū)別。在Linux中uid和task_struct(進(jìn)程描述符)是一對(duì)一綁定一起,代表著當(dāng)前進(jìn)程用戶的使用者id。而Android相似卻不同,Android在framework層的userid是在PMS按照時(shí)候通過PackageParser.Package.scanPackageDirtyLI()分配好的.

每一次獲取Uid都是經(jīng)過下面這一段算法:

public static int getUid(@UserIdInt int userId, @AppIdInt int appId) {
        if (MU_ENABLED) {//是否支持多用戶
            //PER_USER_RANGE  為 100000
            return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
        } else {
            return appId;
        }
    }

通過userId獲取uid

public static final int getUserId(int uid) {
        if (MU_ENABLED) {
            return uid / PER_USER_RANGE;
        } else {
            return 0;
        }
    }

userId * 100000 + (appId % 100000)
這樣就能把uid和userId互相轉(zhuǎn)化。

因此這種設(shè)計(jì)導(dǎo)致了uid可以共享,雖然用的少,但是實(shí)際上確實(shí)存在。當(dāng)包名相同,但是為組件在AndroidManifest設(shè)置了android:process這個(gè)標(biāo)簽,就能在不同的進(jìn)程共享一個(gè)uid。也能設(shè)置android:shareUserid,不同包名時(shí)候可以共享(相同包名,相同簽名則會(huì)覆蓋)。這里就繼續(xù)不探究,后續(xù)會(huì)在PMS解析之后再來詳細(xì)看看。

回到進(jìn)程的LRU算法

我們先要弄明白一般的LRU算法是為了讓最近最少用的放到隊(duì)尾,最近最常用放在隊(duì)頭,目的是為了在某種常用這個(gè)對(duì)象,能夠減少搜索時(shí)間,從而達(dá)到性能優(yōu)化目的。

而這個(gè)進(jìn)程的LRU算法稍微有點(diǎn)不一樣。最近最常用的放在隊(duì)末,最近最少用放在隊(duì)首。

因此在循環(huán)的時(shí)候,AMS是從隊(duì)末開始搜索進(jìn)程對(duì)象(ProcessRecord)。弄懂設(shè)計(jì)原型,再來看看Google工程師的設(shè)計(jì)。

每一次通過update方法調(diào)整進(jìn)程在LRU算法,首先會(huì)判斷當(dāng)前進(jìn)程是否包含Activity或者Service。

不管包含著什么,只要發(fā)現(xiàn)當(dāng)前要查找的ProcessRecord在隊(duì)末,則立即返回。接著再次搜索最近一次使用相同的進(jìn)程的索引,并且刪除。

同時(shí)這里可以看到在這個(gè)LRU中有兩個(gè)位置標(biāo)簽

    1. mLruProcessActivityStart
    1. mLruProcessServiceStart

這兩個(gè)位置標(biāo)簽把整個(gè)LRU的list切割為3部分,從mLruProcessActivityStart到隊(duì)末,就是帶著Activity的進(jìn)程集合,mLruProcessServiceStart到mLruProcessActivityStart就是帶著service的集合,從mLruProcessServiceStart到隊(duì)首則是上面兩者都不帶。

因此在調(diào)整的時(shí)候,我們帶著Activity的進(jìn)程只需要調(diào)整mLruProcessActivityStart到隊(duì)末那一段。帶著service只需要調(diào)整mLruProcessServiceStart到mLruProcessActivityStart這一段。

因此當(dāng)我們刪除ProcessRecord這兩個(gè)索引必須向后移動(dòng)。

接下來分情況討論:

  • 1.帶著Activity的進(jìn)程:
final int N = mLruProcesses.size();
            if ((app.activities.size() == 0 || app.recentTasks.size() > 0)
                    && mLruProcessActivityStart < (N - 1)) {
...
                mLruProcesses.add(N - 1, app);
                // To keep it from spamming the LRU list (by making a bunch of clients),
                // we will push down any other entries owned by the app.
                final int uid = app.info.uid;
                for (int i = N - 2; i > mLruProcessActivityStart; i--) {
                    ProcessRecord subProc = mLruProcesses.get(i);
                    if (subProc.info.uid == uid) {
                        if (mLruProcesses.get(i - 1).info.uid != uid) {
...
                            ProcessRecord tmp = mLruProcesses.get(i);
                            mLruProcesses.set(i, mLruProcesses.get(i - 1));
                            mLruProcesses.set(i - 1, tmp);
                            i--;
                        }
                    } else {
                        // A gap, we can stop here.
                        break;
                    }
                }
            } else {
...
                mLruProcesses.add(app);
            }
            nextIndex = mLruProcessServiceStart;

從這里我們看到當(dāng)要添加進(jìn)LRU或者重新調(diào)整ProcessRecord會(huì)判斷當(dāng)前進(jìn)程中有沒有最近使用的TaskRecord集合或者ProcessRecord中的Activity集合大于0.則插入到隊(duì)末。

接著開始循環(huán)后面的集合,查看有沒有ProcessRecord和當(dāng)前的共享一個(gè)uid。找到有個(gè)這個(gè)共享的,則交換位置,讓共享uid的進(jìn)程往隊(duì)末靠,這樣就是實(shí)現(xiàn)了LRU關(guān)鍵算法。

最后nextIndex設(shè)置為mLruProcessServiceStart

  • 2.當(dāng)插入的進(jìn)程是有service的。
    直接插入到mLruProcessActivityStart的位置,并且mLruProcessActivityStart加一,讓Activity的集合向后移動(dòng)。
    最后nextIndex = mLruProcessServiceStart;

  • 3.當(dāng)插入的進(jìn)程是沒有Activity和Service的。

            // Process not otherwise of interest, it goes to the top of the non-service area.
            int index = mLruProcessServiceStart;
            if (client != null) {
                // If there is a client, don't allow the process to be moved up higher
                // in the list than that client.
                int clientIndex = mLruProcesses.lastIndexOf(client);
....
                if (clientIndex <= lrui) {
                    // Don't allow the client index restriction to push it down farther in the
                    // list than it already is.
                    clientIndex = lrui;
                }
                if (clientIndex >= 0 && index > clientIndex) {
                    index = clientIndex;
                }
            }
...
            mLruProcesses.add(index, app);
            nextIndex = index-1;
            mLruProcessActivityStart++;
            mLruProcessServiceStart++;

這里又分為有沒有帶上client,和沒有client端,帶上client端這種情況一般是service通過Binder綁定了遠(yuǎn)程端的進(jìn)程并且在重啟Service情況下。這個(gè)client是指遠(yuǎn)程端的ProcessRecord。因此這里有兩種情況,一種是本身遠(yuǎn)程端就帶著Activity/Service,一種就是都沒有帶。

  • 1.當(dāng)沒有帶上client端
    那么當(dāng)前進(jìn)程將會(huì)插在mLruProcessServiceStart,之后這個(gè)位置并且mLruProcessServiceStart和mLruProcessActivityStart都向后移動(dòng)一位。
    nextindex此時(shí)為移動(dòng)前mLruProcessServiceStart - 1.

  • 2.當(dāng)帶上client端
    當(dāng)client端本身存在,并且比當(dāng)前的進(jìn)程在LRU位置考后(更加靠近前端),或者不存在,則設(shè)置clientIndex(為client原先在LRU中位置)為設(shè)置為當(dāng)前進(jìn)程調(diào)整前在LRU的位置。

如果client不存在,則插入位置為mLruProcessServiceStart。

接著判斷如果client如果存在,且client位置比當(dāng)前進(jìn)程的原先的位置靠前,并且當(dāng)前位置mLruProcessServiceStart小,比則插入位置為原先的位置。

如果當(dāng)client存在,且比當(dāng)前進(jìn)程(app)靠后,且client的位置比mLruProcessServiceStart小,插入的位置是mLruProcessServiceStart。

如果當(dāng)client存在,且比當(dāng)前進(jìn)程(app)靠后,且client的位置比mLruProcessServiceStart大,插入的位置是client的在LRU位置。

nextIndex設(shè)置為mLruProcessServiceStart -1,或者client在LRU位置-1.

進(jìn)程的LRU后續(xù)算法

處理了Activity和Service,會(huì)繼續(xù)后續(xù)處理。處理進(jìn)程中Service綁定遠(yuǎn)程端,ContentProvider。

 for (int j=app.connections.size()-1; j>=0; j--) {
            ConnectionRecord cr = app.connections.valueAt(j);
            if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
                    && cr.binding.service.app != null
                    && cr.binding.service.app.lruSeq != mLruSeq
                    && !cr.binding.service.app.persistent) {
                nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
                        "service connection", cr, app);
            }
        }
        for (int j=app.conProviders.size()-1; j>=0; j--) {
            ContentProviderRecord cpr = app.conProviders.get(j).provider;
            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
                        "provider reference", cpr, app);
            }
        }
private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
            String what, Object obj, ProcessRecord srcApp) {
        app.lastActivityTime = now;

        if (app.activities.size() > 0 || app.recentTasks.size() > 0) {
      
            return index;
        }

        int lrui = mLruProcesses.lastIndexOf(app);
        if (lrui < 0) {
            return index;
        }

        if (lrui >= index) {
    
            return index;
        }

        if (lrui >= mLruProcessActivityStart) {
            return index;
        }

        mLruProcesses.remove(lrui);
        if (index > 0) {
            index--;
        }

        mLruProcesses.add(index, app);
        return index;

這里的邏輯聯(lián)動(dòng)上面的函數(shù):

  • 1.當(dāng)service的遠(yuǎn)程進(jìn)程存在Activity就不存移動(dòng)了。
  • 2.當(dāng)service的遠(yuǎn)程進(jìn)程或者ContentProvider不存在在LRU中也不調(diào)整了。
  • 3.當(dāng)service的遠(yuǎn)程進(jìn)程或者ContentProvider比nextIndex的位置大也不調(diào)整。
  • 4.當(dāng)service的遠(yuǎn)程進(jìn)程或者ContentProvider在mLruProcessActivityStart后面也不調(diào)整。
  • 5.否則就逐個(gè)添加mLruProcessServiceStart之后;不帶Activity/Service的進(jìn)程插在mLruProcessServiceStart之前或者client之后。

用一副圖表示整個(gè)進(jìn)程的LRU計(jì)算就是如下


Android進(jìn)程LRU緩存調(diào)整.png

以上就是進(jìn)程處理LRU全部?jī)?nèi)容。這也為什么Google工程師選擇使用ArrayList而不是用LinkHashMap做LRU處理。因?yàn)镚oogle工程機(jī)為這個(gè)LRU做了浮標(biāo),劃分了調(diào)整的區(qū)域,這樣就能進(jìn)一步的壓縮搜索和調(diào)整時(shí)間。

接下來讓我繼續(xù)回到AMS的startActivity方法。

isInAnyStackLocked

上面的長(zhǎng)篇大論只是為了找到緩存在AMS中的進(jìn)程。接下來我們要j檢驗(yàn)對(duì)應(yīng)Activity的棧。

文件:/frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

這個(gè)方法中傳進(jìn)來一個(gè)關(guān)鍵的resultTo的Binder代理對(duì)象。不同的是,這個(gè)指代并不是任何Activity,而是指代在啟動(dòng)Activity時(shí)候,綁定的WindowManager
Binder代理,之后就會(huì)看到了。

ActivityRecord isInAnyStackLocked(IBinder token) {
        int numDisplays = mActivityDisplays.size();
        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
            final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = display.getChildAt(stackNdx);
                final ActivityRecord r = stack.isInStackLocked(token);
                if (r != null) {
                    return r;
                }
            }
        }
        return null;
    }

能看到的是,如果啟動(dòng)的時(shí)候,帶的調(diào)用方WindowManager的Binder代理對(duì)象。此時(shí)說明此時(shí)Activity很可能是和當(dāng)前的Activity在同一個(gè)進(jìn)程。那么Android將會(huì)從mActivityDisplays獲取ActivityDisplay對(duì)象,從中找到我們需要ActivityStack,從名字就能明白,這就是就是我們的Activity棧。

這個(gè)mActivityDisplays是個(gè)什么數(shù)據(jù)結(jié)構(gòu),實(shí)際上這是一個(gè)聯(lián)通Activity的display邏輯顯示器和Activity關(guān)聯(lián)。換句話說,就是WindowManager和Activity關(guān)聯(lián)起來的一個(gè)數(shù)據(jù)結(jié)構(gòu)。在ActivityDisplay保存這個(gè)ActivityStack,為了WIndowManager能夠跟著ActivityStack變化而變化。

這里的場(chǎng)景是,先拿到當(dāng)前的ActivityStack,并且從中獲取到啟動(dòng)者的ActivityRecord(Activity在AMS保存著信息)。

startActivity根據(jù)當(dāng)前啟動(dòng)flag做第一次調(diào)整

 final int launchFlags = intent.getFlags();

        if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
            // Transfer the result target from the source activity to the new
            // one being started, including any failures.
            if (requestCode >= 0) {
                SafeActivityOptions.abort(options);
                return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
            }
            resultRecord = sourceRecord.resultTo;//這個(gè)resultTo是指Activity中的屬性,和上面的不一樣。
            if (resultRecord != null && !resultRecord.isInStackLocked()) {
                resultRecord = null;
            }
            resultWho = sourceRecord.resultWho;
            requestCode = sourceRecord.requestCode;
            sourceRecord.resultTo = null;
            if (resultRecord != null) {
                resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
            }
            if (sourceRecord.launchedFromUid == callingUid) {
                callingPackage = sourceRecord.launchedFromPackage;
            }
        }

...

final ActivityStack resultStack = resultRecord == null ? null : resultRecord.getStack();
...

這一段代碼就是第一次獲取intent中的啟動(dòng)flag。首先處理的flag是FORWARD_RESULT。

這個(gè)intent的flag用的不多,意思是透?jìng)鱮equestCode。也就是說當(dāng)設(shè)置了這個(gè)flag,那么被啟動(dòng)的這個(gè)Activity將不會(huì)接受這個(gè)requestCode。而是透?jìng)鞯絾?dòng)的下一個(gè)Activity。但是作為透?jìng)髡卟荒茉O(shè)置任何的requestCode,設(shè)置了則會(huì)報(bào)錯(cuò) FORWARD_RESULT_FLAG used while also requesting a result。


image.png

了解到用法,我們可以直接從這里看到當(dāng)我們?cè)O(shè)置了requestCode大于0則,會(huì)立即返回錯(cuò)誤。否則的話當(dāng)成并沒有發(fā)送這個(gè)requestcode。此時(shí)將會(huì)取出啟動(dòng)這個(gè)sourceRecord的requestCode,resultWho設(shè)置給下一個(gè)Activity,把喚起的包名更換為sourceRecord,這樣就完成了透?jìng)鲃?dòng)作。

同時(shí),這個(gè)已經(jīng)啟動(dòng)過的sourceRecord清空掉resultTo,保證透?jìng)鞯哪繕?biāo)為這個(gè)新建的。

startActivity校驗(yàn)權(quán)限,生成ActivityRecord

 boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
                requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
                inTask != null, callerApp, resultRecord, resultStack);
        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                callingPid, resolvedType, aInfo.applicationInfo);

        // Merge the two options bundles, while realCallerOptions takes precedence.
        ActivityOptions checkedOptions = options != null
                ? options.getOptions(intent, aInfo, callerApp, mSupervisor)
                : null;
        if (allowPendingRemoteAnimationRegistryLookup) {
            checkedOptions = mService.getActivityStartController()
                    .getPendingRemoteAnimationRegistry()
                    .overrideOptionsIfNeeded(callingPackage, checkedOptions);
        }
        if (mService.mController != null) {
            try {
                // The Intent we give to the watcher has the extra data
                // stripped off, since it can contain private information.
                Intent watchIntent = intent.cloneFilter();
                abort |= !mService.mController.activityStarting(watchIntent,
                        aInfo.applicationInfo.packageName);
            } catch (RemoteException e) {
                mService.mController = null;
            }
        }

        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
        if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid,
                callingUid, checkedOptions)) {
            // activity start was intercepted, e.g. because the target user is currently in quiet
            // mode (turn off work) or the target application is suspended
            intent = mInterceptor.mIntent;
            rInfo = mInterceptor.mRInfo;
            aInfo = mInterceptor.mAInfo;
            resolvedType = mInterceptor.mResolvedType;
            inTask = mInterceptor.mInTask;
            callingPid = mInterceptor.mCallingPid;
            callingUid = mInterceptor.mCallingUid;
            checkedOptions = mInterceptor.mActivityOptions;
        }

        if (abort) {
            if (resultRecord != null) {
                resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
                        RESULT_CANCELED, null);
            }
            ActivityOptions.abort(checkedOptions);
            return START_ABORTED;
        }

        // If permissions need a review before any of the app components can run, we
        // launch the review activity and pass a pending intent to start the activity
        // we are to launching now after the review is completed.
        if (mService.mPermissionReviewRequired && aInfo != null) {
            if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                    aInfo.packageName, userId)) {
                IIntentSender target = mService.getIntentSenderLocked(
                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
                        callingUid, userId, null, null, 0, new Intent[]{intent},
                        new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
                                | PendingIntent.FLAG_ONE_SHOT, null);

                final int flags = intent.getFlags();
                Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
                newIntent.setFlags(flags
                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
                newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
                if (resultRecord != null) {
                    newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true);
                }
                intent = newIntent;

                resolvedType = null;
                callingUid = realCallingUid;
                callingPid = realCallingPid;

                rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, 0,
                        computeResolveFilterUid(
                                callingUid, realCallingUid, mRequest.filterCallingUid));
                aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
                        null /*profilerInfo*/);

         
            }
        }


        if (rInfo != null && rInfo.auxiliaryInfo != null) {
            intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent,
                    callingPackage, verificationBundle, resolvedType, userId);
            resolvedType = null;
            callingUid = realCallingUid;
            callingPid = realCallingPid;

            aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
        }

        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
                callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
                resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
                mSupervisor, checkedOptions, sourceRecord);
        if (outActivity != null) {
            outActivity[0] = r;
        }

在這個(gè)代碼片段中,有兩個(gè)關(guān)鍵函數(shù)做權(quán)限判斷。

  • 1.checkStartAnyActivityPermission
    這個(gè)函數(shù)最后調(diào)用到PermissionManagerService中,對(duì)當(dāng)前的uid精心檢驗(yàn)是否合法。

  • 2.mInterceptor.intercept 該函數(shù)是一個(gè)攔截器對(duì)當(dāng)前的參數(shù)精心攔截,里面的攔截判斷主要有三點(diǎn):
    文件:/frameworks/base/services/core/java/com/android/server/am/ActivityStartInterceptor.java

boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
            TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions) {
        mUserManager = UserManager.get(mServiceContext);

        mIntent = intent;
        mCallingPid = callingPid;
        mCallingUid = callingUid;
        mRInfo = rInfo;
        mAInfo = aInfo;
        mResolvedType = resolvedType;
        mInTask = inTask;
        mActivityOptions = activityOptions;

        if (interceptSuspendedPackageIfNeeded()) {
            return true;
        }
        if (interceptQuietProfileIfNeeded()) {
            return true;
        }
        if (interceptHarmfulAppIfNeeded()) {
            return true;
        }
        return interceptWorkProfileChallengeIfNeeded();
    }
  • 1.當(dāng)當(dāng)前要啟動(dòng)的包被管理員是否被掛起,不允許操作

  • 2.當(dāng)此時(shí)的用戶在安靜模式,這個(gè)安靜模式不是指音量,而是指UserManager中設(shè)置的requestQuietModeEnabled,在這個(gè)模式下,應(yīng)用不會(huì)真正的運(yùn)行。關(guān)閉安靜模式時(shí)候就有個(gè)彈窗。

-3.當(dāng)前的應(yīng)用被判斷為有害

以上三種情況下,只要想要打開Activity都會(huì)有個(gè)新的ActivityInfo替代原來的Activity,用來提示用戶。

還有一種常見情況,當(dāng)我們的權(quán)限判斷彈窗并不是直接攔截,而是等到Activity啟動(dòng)后,作為一個(gè)彈窗攔截在上面的情況。

        if (mService.mPermissionReviewRequired && aInfo != null) {
            if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                    aInfo.packageName, userId)) {
                IIntentSender target = mService.getIntentSenderLocked(
                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
                        callingUid, userId, null, null, 0, new Intent[]{intent},
                        new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
                                | PendingIntent.FLAG_ONE_SHOT, null);

                final int flags = intent.getFlags();
                Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
                newIntent.setFlags(flags
                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
                newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
                if (resultRecord != null) {
                    newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true);
                }
                intent = newIntent;

                resolvedType = null;
                callingUid = realCallingUid;
                callingPid = realCallingPid;

                rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, 0,
                        computeResolveFilterUid(
                                callingUid, realCallingUid, mRequest.filterCallingUid));
                aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
                        null /*profilerInfo*/);

         
            }
        }

此時(shí),就能看到熟悉IIntentSender這個(gè)類。如果閱讀過pendingIntent源碼的朋友,就能知道pendingItent本質(zhì)上IIntentSender就是這個(gè)類在延后操作。這里將不會(huì)鋪開講,之后會(huì)詳細(xì)分析pendingIntent。這樣就能附著一個(gè)intent等到Activity啟動(dòng)后在彈出一個(gè)彈窗Activity。

最后在根據(jù)這些數(shù)據(jù)生成一個(gè)新的ActivityRecord(這個(gè)ActivityRecord是目標(biāo)對(duì)象的ActivityRecord),并且把發(fā)起者的sourceRecord和當(dāng)前的作為參數(shù)傳入。正式開始操作ActivityStack。因此,我們可以知道Activity在AMS中將會(huì)對(duì)應(yīng)一個(gè)ActivityRecord。

小節(jié)

本文就先分析到這里,稍后會(huì)重點(diǎn)分析ActvityStack在intent的各種startflag下的變化。

本文總結(jié)進(jìn)程的緩存LRU算法,實(shí)際上就是分成三段進(jìn)行管理,包含Activity,Service,兩者不包含的。在通過ContentProvider以及Service綁定的遠(yuǎn)程端,再對(duì)兩者可能鏈接到的進(jìn)程進(jìn)行管緩存理。因此我們可以清楚,在四大組件中只有Boardcast不會(huì)對(duì)進(jìn)程LRU的優(yōu)先進(jìn)行影響。

不過請(qǐng)注意,四大組件都會(huì)Android系統(tǒng)中進(jìn)程adj調(diào)度產(chǎn)生影響,兩者不同。

于此同時(shí)通過Activitstarter.startActivity的方法為目標(biāo)Activity準(zhǔn)備好了ActivityRecord,目標(biāo)對(duì)象是什么。接下來就是如何把這個(gè)ActivityRecord插入棧中。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容