
和你一起終身學習,這里是程式員Android
經典好文推薦,通過閱讀本文,您將識訓以下知識點:
一、Android Acitvity啟動流程概覽
二、啟動流程中AMS內的各個角色
三、AMS跨行程通信創建Activity,第一步
四、ActivityStarter 正式開始啟動Activity
五、startActivityMayWait
六、startActivity處理ActivityInfo轉化為ActivityRecord
七、小結
一、Android Acitvity啟動流程概覽

注意:紅色線代表跨越Binder一次行程
從時序圖上,無論怎么Android的啟動架構怎么演變,其根本流程都沒有變,Android都是通過Binder通行到AMS,接著經過AMS的一系列中堆疊處理之后,把ActivityRecord回傳到AppThread(App行程中),
在Android 9.0中,AMS不再是通過簡單的呼叫IPC來控制App端的Activity生命周期,而是通過一個狀態設計模式,將每個Activity每一個生命周期都抽象成一個狀態,接著通過狀態機去管理整個生命周期,
提示:從上面幾篇文章能看到,實際上AMS隸屬于SystemServer行程,和App行程不在同一處,
二、啟動流程中AMS內的各個角色
在Activity中啟動中,AMS擔任最為重要的角色,下面列出的都是AMS中承擔各個主要功能的類
1.ActivityStack 代表著Activity的堆疊(不精準稍后會具體解釋)
2.ActivityStarter 代表著Activity正式啟動的控制類
3.ActivityManagerService 代表著一切Activity行為在系統中的控制中心
4.ActivityStackSupervisor 代表著ActivityStack的監控中心
實際上對于我們來說在整個Activity的啟動需要關注這么四個核心類,
而在這里面往往涉及到Activity堆疊的變化,而這個程序涉及到的核心類有:
1.ActivityRecord
2.TaskRecord
3.mRecentTasks
4.mTaskHistory
5.ProcessRecord
三、AMS跨行程通信創建Activity,第一步
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)//呼叫方的AppThread的IBinder
.setCallingPackage(callingPackage)//呼叫方的包名
.setResolvedType(resolvedType)//呼叫type
.setResultTo(resultTo)//呼叫方的ActivityClientRecord的binder(實際上是AMS的ActivityRecord對應在App端的binder物件)
.setResultWho(resultWho)//呼叫方的標示
.setRequestCode(requestCode)//需要回傳的requestCode
.setStartFlags(startFlags)//啟動標志位
.setProfilerInfo(profilerInfo)//啟動時帶上的權限檔案物件
.setActivityOptions(bOptions)//ActivityOptions的Activity的啟動項,在一般的App中此時是null,不需要關注
.setMayWait(userId)//是否是同步打開Actvivity 默認一般是true
.execute();//執行方法,
復制代碼
從這里面節能很清晰的明白,在啟動程序中需要什么引數,雖然看起來像是一個建造者設計模式,但是實際上工廠設計模式+享元設計+鏈式呼叫,通過obtainStarter把DefaultFactory從mStarterPool中獲取一個ActivityStarter(池子中最多設定3個),接著通過鏈式呼叫,把啟動時需要的引數傳遞進去,
當設定完成之后,我們直接看看execute方法,
四、ActivityStarter 正式開始啟動Activity
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我們可以看到,在這個程序Google工程師靈活的運用了try-final機制,通過onExecutionComplete在ActivityStartController清除資料放回startPool池子中,
此時我們是一個同步操作,所以看看startActivityMayWait方法,
五、startActivityMayWait
這段我們分為三段來看:
5.1.從PackageManagerService準備activity需要的資料
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呼叫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 {
....
}
}
復制代碼
從上面代碼,我們可以看到,這個方法是通過intent來從找到一個最合適的選擇,我們可以推測,實際上這個ResolveInfo是指當我們安裝了App之后,加載到PackageManagerService(后面稱PMS)系統中的AndroidManifest.xml的資料,
queryIntentActivitiesInternal分步驟來說:
1.查看當前Intent是否是顯式Intent,是則取出其中的class物件和AndroidManifest的進行匹配,匹配成功回傳,
2.如果沒有指定包名則全系統的查找匹配intent
3.如果指定包名,則從當前的包名尋找匹配規則相符合的intent的Activity
因此此時可能會匹配多個合適的Intent,再通過chooseBestActivity進一步篩選Activity,
為什么加上這一段,實際上這一段有一個關鍵的邏輯就是AppLink,開發經常用到,在AndroidManifest中設定好schme等Intent引數,讓外部app來喚醒我們自己的app,當喚醒的目的地只有一個直接回傳,如果有多個則替換intent中的類,變成系統的ResolveActivity,用來選擇我們的目的App,如下圖,
[圖片上傳失敗...(image-402965-1628853436643)]
2.查不到ResolveInfo則嘗試從直接啟動中獲取
自Android 5.0之后,Android系統將開始支持多用戶系統,這些用戶的配置都由UserManager控制,其中AccountManager則是控制每個用戶下的賬號,
在Android7.0之后,為應用新增了一種啟動模式Direct Boot(直接啟動模式),這種模式是指設備啟動后進入的一個新模式,直到用戶解鎖(unlock)設備此階段結束,這種模式,會為程式創建Device protected storage私有的存盤空間,
這種模式比較特殊,我們需要在AndroidManifest中設定 android:directBootAware="true",
因此,這種模式下,需要喚醒特殊的Activity,確定此時已經解鎖,需要從特殊的私有空間去查找對應的ResolveInfo,
3.通過PMS的getActivityInfo讀取ActivityInfo
當我們確定好了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;
}
復制代碼
找到后,就顯示的設定ComponentName,包名和類名,
5.2.處理重量級行程
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);
}
}
}
}
復制代碼
這個重量級行程實際上很早就存在,但是允許我們設定是在sdk 28(Android 9.0)之后才能開放給我們,
重量級的行程一般是指在整個系統唯一存在一個行程,不會正常走保存恢復機制,而是一直運行在后臺,不會被后臺殺死,因此需要用戶顯示退出進入該行程,
而這段代碼就是當后臺已經啟動了一個重量行程的時候,用戶又一次想要啟動另一個重量級行程,就會彈出一個界面讓用戶進行選擇,
[圖片上傳失敗...(image-967a9-1628853436643)]
5.3.進行下一步的啟動
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;
}
}
}
復制代碼
我們可以看到進行下一個的啟動之后,如果回傳的狀態碼START_SUCCESS,就會阻塞AMS,等待喚醒,
一般來說如果startActivity正常完成了整個流程就回傳狀態代碼為START_SUCCESS.進入到了第一次阻塞狀態,而這個阻塞會不斷判斷的當前的狀態是否是START_TASK_TO_FRONT,是才退出,然而如果此時Activity是第一次創建,則通過ActivityStarter.startActivity先START_SUCCESS后,通過postStartActivityProcessing 把結果轉化為START_TASK_TO_FRONT.因此能夠立即退出當前的狀態,
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;
}
復制代碼
六、startActivity處理ActivityInfo轉化為ActivityRecord
這里分為3步聊聊:
6.1.準備ActivtyRecord的基礎資料
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;
}
}
}
....
}
復制代碼
為了實體化ActivityRecord,Android系統通過IApplicationThread獲取當前Activity所處在行程資料,
也就是呼叫 mService.getRecordForAppLocked(caller); 獲取ProcessRecord,
6.2 AMS尋找行程
我們追蹤一下AMS是怎么通過IApplicationThread這個AppThread遠程binder物件獲得的,
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對性能的追求,在整個AMS中,有兩個資料結構存盤著行程物件:
1.mLruProcesses 一個LRU的ArrayList存盤著資料,這個資料結構雖然不是我們常用的LRUMap(LinkHashMap 經過處理后能夠自動處理LRU演算法,將會在演算法專欄和大家聊聊)最近最少使用演算法,但是Google 工程師選擇自己處理,
2.mProcessNames 存盤著所有行程的資料,可以通過Binde的參考名反過來找到行程的資料,
所以用我們常用的話來說,AMS在行程查找中用了二級快取,
趁熱打鐵看看行程是怎么更新的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詳細看下面帶著Activity的行程情況套路
} else if (hasService) {
// Process has services, put it at the top of the service list.
...
mLruProcesses.add(mLruProcessActivityStart, app);
nextIndex = mLruProcessServiceStart;
mLruProcessActivityStart++;
} else {
//詳細看處理沒有Actvity以及Service的行程
}
// 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);
}
}
}
復制代碼
額外知識的補充
要看懂這一段邏輯,我們必須要普及一個基礎知識,Android的uid和userId,
雖然Android是沿用Linux內核,其uid充滿著迷惑性,android的uid和Linux的uid有區別,在Linux中uid和task_struct(行程描述符)是一對一系結一起,代表著當前行程用戶的使用者id,而Android相似卻不同,Android在framework層的userid是在PMS按照時候通過PackageParser.Package.scanPackageDirtyLI()分配好的.
每一次獲取Uid都是經過下面這一段演算法:
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互相轉化,
因此這種設計導致了uid可以共享,雖然用的少,但是實際上確實存在,當包名相同,但是為組件在AndroidManifest設定了android:process這個標簽,就能在不同的行程共享一個uid,也能設定android:shareUserid,不同包名時候可以共享(相同包名,相同簽名則會覆寫),
回到行程的LRU演算法
我們先要弄明白一般的LRU演算法是為了讓最近最少用的放到隊尾,最近最常用放在隊頭,目的是為了在某種常用這個物件,能夠減少搜索時間,從而達到性能優化目的,
而這個行程的LRU演算法稍微有點不一樣,最近最常用的放在隊末,最近最少用放在隊首,
因此在回圈的時候,AMS是從隊末開始搜索行程物件(ProcessRecord),弄懂設計原型,再來看看Google工程師的設計,
每一次通過update方法調整行程在LRU演算法,首先會判斷當前行程是否包含Activity或者Service,
不管包含著什么,只要發現當前要查找的ProcessRecord在隊末,則立即回傳,接著再次搜索最近一次使用相同的行程的索引,并且洗掉,
同時這里可以看到在這個LRU中有兩個位置標簽
mLruProcessActivityStart
mLruProcessServiceStart
這兩個位置標簽把整個LRU的list切割為3部分,從mLruProcessActivityStart到隊末,就是帶著Activity的行程集合,mLruProcessServiceStart到mLruProcessActivityStart就是帶著service的集合,從mLruProcessServiceStart到隊首則是上面兩者都不帶,
因此在調整的時候,我們帶著Activity的行程只需要調整mLruProcessActivityStart到隊末那一段,帶著service只需要調整mLruProcessServiceStart到mLruProcessActivityStart這一段,
因此當我們洗掉ProcessRecord這兩個索引必須向后移動,
接下來分情況討論:
1.帶著Activity的行程:
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; 復制代碼2.當插入的行程是有service的,
直接插入到mLruProcessActivityStart的位置,并且mLruProcessActivityStart加一,讓Activity的集合向后移動,
最后nextIndex = mLruProcessServiceStart;3.當插入的行程是沒有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系結了遠程端的行程并且在重啟Service情況下,這個client是指遠程端的ProcessRecord,因此這里有兩種情況,一種是本身遠程端就帶著Activity/Service,一種就是都沒有帶,
1.當沒有帶上client端
那么當前行程將會插在mLruProcessServiceStart,之后這個位置并且mLruProcessServiceStart和mLruProcessActivityStart都向后移動一位,
nextindex此時為移動前mLruProcessServiceStart - 1.2.當帶上client端
當client端本身存在,并且比當前的行程在LRU位置考后(更加靠近前端),或者不存在,則設定clientIndex(為client原先在LRU中位置)為設定為當前行程調整前在LRU的位置,
如果client不存在,則插入位置為mLruProcessServiceStart,
接著判斷如果client如果存在,且client位置比當前行程的原先的位置靠前,并且當前位置mLruProcessServiceStart小,比則插入位置為原先的位置,
如果當client存在,且比當前行程(app)靠后,且client的位置比mLruProcessServiceStart小,插入的位置是mLruProcessServiceStart,
如果當client存在,且比當前行程(app)靠后,且client的位置比mLruProcessServiceStart大,插入的位置是client的在LRU位置,
nextIndex設定為mLruProcessServiceStart -1,或者client在LRU位置-1.
行程的LRU后續演算法
處理了Activity和Service,會繼續后續處理,處理行程中Service系結遠程端,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; 復制代碼這里的邏輯聯動上面的函式:
1.當service的遠程行程存在Activity就不存移動了,
2.當service的遠程行程或者ContentProvider不存在在LRU中也不調整了,
3.當service的遠程行程或者ContentProvider比nextIndex的位置大也不調整,
4.當service的遠程行程或者ContentProvider在mLruProcessActivityStart后面也不調整,
5.否則就逐個添加mLruProcessServiceStart之后;不帶Activity/Service的行程插在mLruProcessServiceStart之前或者client之后,
用一副圖表示整個行程的LRU計算就是如下
[圖片上傳失敗...(image-fbc50c-1628853436641)]
以上就是行程處理LRU全部內容,這也為什么Google工程師選擇使用ArrayList而不是用LinkHashMap做LRU處理,因為Google工程機為這個LRU做了浮標,劃分了調整的區域,這樣就能進一步的壓縮搜索和調整時間,
isInAnyStackLocked
上面的長篇大論只是為了找到快取在AMS中的行程,接下來我們要j檢驗對應Activity的堆疊,
這個方法中傳進來一個關鍵的resultTo的Binder代理物件,不同的是,這個指代并不是任何Activity,而是指代在啟動Activity時候,系結的WindowManager Binder代理.
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; } 復制代碼能看到的是,如果啟動的時候,帶的呼叫方WindowManager的Binder代理物件,此時說明此時Activity很可能是和當前的Activity在同一個行程,那么Android將會從mActivityDisplays獲取ActivityDisplay物件,從中找到我們需要ActivityStack,從名字就能明白,這就是就是我們的Activity堆疊,
這個mActivityDisplays是個什么資料結構,實際上這是一個聯通Activity的display邏輯顯示幕和Activity關聯,換句話說,就是WindowManager和Activity關聯起來的一個資料結構,在ActivityDisplay保存這個ActivityStack,為了WIndowManager能夠跟著ActivityStack變化而變化,
這里的場景是,先拿到當前的ActivityStack,并且從中獲取到啟動者的ActivityRecord(Activity在AMS保存著資訊),
startActivity根據當前啟動flag做第一次調整
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;//這個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中的啟動flag,首先處理的flag是FORWARD_RESULT,
這個intent的flag用的不多,意思是透傳requestCode,也就是說當設定了這個flag,那么被啟動的這個Activity將不會接受這個requestCode,而是透傳到啟動的下一個Activity,但是作為透傳者不能設定任何的requestCode,設定了則會報錯 FORWARD_RESULT_FLAG used while also requesting a result,
[圖片上傳失敗...(image-2f1c62-1628853436641)]
了解到用法,我們可以直接從這里看到當我們設定了requestCode大于0則,會立即回傳錯誤,否則的話當成并沒有發送這個requestcode,此時將會取出啟動這個sourceRecord的requestCode,resultWho設定給下一個Activity,把喚起的包名更換為sourceRecord,這樣就完成了透傳動作,
同時,這個已經啟動過的sourceRecord清空掉resultTo,保證透傳的目標為這個新建的,
startActivity校驗權限,生成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; } 復制代碼在這個代碼片段中,有兩個關鍵函式做權限判斷,
1.checkStartAnyActivityPermission
這個函式最后呼叫到PermissionManagerService中,對當前的uid精心檢驗是否合法,2.mInterceptor.intercept 該函式是一個攔截器對當前的引數精心攔截,里面的攔截判斷主要有三點:
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.當當前要啟動的包被管理員是否被掛起,不允許操作
2.當此時的用戶在安靜模式,這個安靜模式不是指音量,而是指UserManager中設定的requestQuietModeEnabled,在這個模式下,應用不會真正的運行,關閉安靜模式時候就有個彈窗,
-3.當前的應用被判斷為有害
以上三種情況下,只要想要打開Activity都會有個新的ActivityInfo替代原來的Activity,用來提示用戶,
還有一種常見情況,當我們的權限判斷彈窗并不是直接攔截,而是等到Activity啟動后,作為一個彈窗攔截在上面的情況,
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*/); } } 復制代碼此時,就能看到熟悉IIntentSender這個類,如果閱讀過pendingIntent原始碼的朋友,就能知道pendingItent本質上IIntentSender就是這個類在延后操作,這里將不會鋪開講,之后會詳細分析pendingIntent,這樣就能附著一個intent等到Activity啟動后在彈出一個彈窗Activity,
最后在根據這些資料生成一個新的ActivityRecord(這個ActivityRecord是目標物件的ActivityRecord),并且把發起者的sourceRecord和當前的作為引數傳入,正式開始操作ActivityStack,因此,我們可以知道Activity在AMS中將會對應一個ActivityRecord,
七、小結
本文就先分析到這里,稍后會重點分析ActvityStack在intent的各種startflag下的變化,
本文總結行程的快取LRU演算法,實際上就是分成三段進行管理,包含Activity,Service,兩者不包含的,在通過ContentProvider以及Service系結的遠程端,再對兩者可能鏈接到的行程進行管快取理,因此我們可以清楚,在四大組件中只有Boardcast不會對行程LRU的優先進行影響,
不過請注意,四大組件都會Android系統中行程adj調度產生影響,兩者不同,
于此同時通過Activitstarter.startActivity的方法為目標Activity準備好了ActivityRecord,目標物件是什么,接下來就是如何把這個ActivityRecord插入堆疊中
作者:貓尾巴
鏈接:https://juejin.cn/post/6987394039507058702友情推薦:
Android 開發干貨集錦
至此,本篇已結束,轉載網路的文章,小編覺得很優秀,歡迎點擊閱讀原文,支持原創作者,如有侵權,懇請聯系小編洗掉,歡迎您的建議與指正,同時期待您的關注,感謝您的閱讀,謝謝!

點擊閱讀原文,為大佬點贊!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/294249.html
標籤:其他
