Android App啟動流程
前言
在使用Android手機時,我們總會啟動各種各樣的App以滿足生活的各種需求,你是否想過,我們是怎樣啟動這些APP的?今天我將帶著讀者一起探索在Android系統中,一個App是如何被啟動的。
在開始分析之前,我們先回想下啟動一個App的流程:
Android系統桌面->點選應用圖示->啟動App
從這個過程來看,只要弄明白:
- Android系統桌面是什麼
- 點選應用圖示後Android系統執行了什麼操作
就可以解決我們“App如何被啟動”的疑問。
Android系統桌面是什麼
如何分析Android系統桌面是什麼
在Android系統中,Activity是檢視存在的根本,那麼我們可以通過命令adb shell dumpsys activity activities
以我的小米5為例,通過USB連上電腦後,輸入命令adb shell dumpsys activity activities
得到結果如下:
可以看到,顯示桌面檢視的Activity是com.miui.home包下的名為Launcher的Activity。
因為虛擬機器編譯AOSP實在是太慢了,所以我沒有編譯AOSP得到系統映象,然後執行模擬器跑AOSP,再通過Ubuntu的Shell跑命令。國內手機廠商雖然會對Android系統進行定製,但是命名和包名都會和原生儘可能保持一致的。
那麼我們在IDE中搜索Launcher,看看這個Activity是什麼。結果如下:
這裡摘選的是Launcher2的Launcher進行分析,雖然新版本Android已經使用Launcher3作為桌面App了,但是我進入原始碼看了看發現核心的邏輯是沒有變化的,所以選取了程式碼更短的Launcher2的Launcher進行分析。
點選應用圖示後Android系統執行了什麼操作
既然Launcher是Activity,那就意味著我們點選桌面的事件可以表達為:
呈現Android桌面檢視(View)->點選View上某個應用圖示->產生點選事件->點選事件被響應->通知Android系統的某個/某些程序->Android系統執行某些操作->啟動App
Launcher如何響應由我們產生的點選事件
/**
* Launches the intent referred by the clicked shortcut.
*
* @param v The view representing the clicked shortcut.
*/
public void onClick(View v) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
if (v.getWindowToken() == null) {
return;
}
if (!mWorkspace.isFinishedSwitchingState()) {
return;
}
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
// Open shortcut
final Intent intent = ((ShortcutInfo) tag).intent;
int[] pos = new int[2];
v.getLocationOnScreen(pos);
intent.setSourceBounds(new Rect(pos[0], pos[1],
pos[0] + v.getWidth(), pos[1] + v.getHeight()));
boolean success = startActivitySafely(v, intent, tag);
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
FolderIcon fi = (FolderIcon) v;
handleFolderClick(fi);
}
} else if (v == mAllAppsButton) {
if (isAllAppsVisible()) {
showWorkspace(true);
} else {
onClickAllAppsButton(v);
}
}
}
boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
try {
success = startActivity(v, intent, tag);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
}
return success;
}
從程式碼來看,產生點選事件後,如果產生點選事件的View的Tag是ShortcutInfo(即啟動應用的快捷方式),就會取得ShortcutInfo中儲存的Intent(這個Intent指向我們要啟動的App),然後執行startActivitySafely(v, intent, tag)
方法,而startActivitySafely方法只是對startActivity方法的簡單封裝。
所以,Launcher響應我們產生的點選事件後,實際上就是啟動一個新的Activity。
我們現在回想下App開發時,每個App都需要有一個“MainActivity”,這個Activity必須在AndroidManifest.xml檔案中有以下配置:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
在配置AndroidManifest.xml檔案時,將Activity的Action指定為android.intent.action.MAIN
,會使Activity在一個新的Task中啟動(Task是一個Activity棧)。將category指定為android.intent.category.LAUNCHER
,表示通過Intent啟動此Activity時,只接受category為LAUNCHER的Intent。
所以,Launcher將會通過App的快捷方式(ShortcutInfo)得到應用的Intent,並通過這個Intent啟動應用的“MainActivity”,從而啟動應用。
所以我們研究的問題就從“App啟動流程”變為“Activity啟動流程”。
Launcher通過Binder通知ActivityManagerService啟動Activity
現在我們就進入Launcher的startActivity方法裡面探索“Activity啟動流程”吧:
boolean startActivity(View v, Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
// Only launch using the new animation if the shortcut has not opted out (this is a
// private contract between launcher and may be ignored in the future).
boolean useLaunchAnimation = (v != null) &&
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
UserHandle user = (UserHandle) intent.getParcelableExtra(ApplicationInfo.EXTRA_PROFILE);
LauncherApps launcherApps = (LauncherApps)
this.getSystemService(Context.LAUNCHER_APPS_SERVICE);
if (useLaunchAnimation) {
ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
v.getMeasuredWidth(), v.getMeasuredHeight());
if (user == null || user.equals(android.os.Process.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, opts.toBundle());
} else {
launcherApps.startMainActivity(intent.getComponent(), user,
intent.getSourceBounds(),
opts.toBundle());
}
} else {
if (user == null || user.equals(android.os.Process.myUserHandle())) {
startActivity(intent);
} else {
launcherApps.startMainActivity(intent.getComponent(), user,
intent.getSourceBounds(), null);
}
}
return true;
} catch (SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Launcher does not have the permission to launch " + intent +
". Make sure to create a MAIN intent-filter for the corresponding activity " +
"or use the exported attribute for this activity. "
+ "tag="+ tag + " intent=" + intent, e);
}
return false;
}
在這個方法中,首先,將Intent的Flag設為Intent.FLAG_ACTIVITY_NEW_TASK
,使得Android系統將建立一個新的Task來放置即將被開啟的新Activity(應用的“MainActivity)。然後獲取一個布林值以用於後續判斷是否顯示啟動App的動畫。
然後獲取Intent中是否傳輸了Parcelable格式的使用者控制代碼,並通過Context.LAUNCHER_APPS_SERVICE
獲取用於在多使用者情境下啟動App的系統服務。
不管是否顯示啟動App的動畫,最終都會執行startActivity(intent)
或launcherApps.startMainActivity
方法以啟動應用的“MainActivity”。而launcherApps.startMainActivity
只在使用者控制代碼不為空且使用者控制代碼不等於當前程序控制代碼時(其他使用者的控制代碼)呼叫。
為什麼使用者控制代碼會影響Activity的啟動方式
這一點和Android的多使用者安全機制有關。
假設我們有使用者A和使用者B在使用同一臺手機,使用者A是無法訪問到使用者B的檔案或者和使用者B的App通訊的。所以假如我們現在是使用者A,但我們想啟動使用者B的App,是無法直接實現的,因為使用者A沒有許可權訪問到使用者B的資料,即使我們在程式碼中強行把user id設為使用者B的user id,交給核心執行時也會丟擲SecurityException
。因此我們需要取得使用者A的控制代碼(和使用者A相關的資料),將我們想啟動的使用者B的App的Intent、使用者A的控制代碼交給核心,讓擁有許可權的Android系統服務(核心態程序)去訪問使用者B的資料並執行相關的操作。
假如是單使用者情境,就會相對簡單了。因為此時只有一個使用者,而該使用者始終有許可權直接訪問自己的資料。
startActivity(intent)如何啟動Activity
進入Activity類後層層深入就可以看到最終呼叫的是startActivityForResult
方法:
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
從程式碼上看,如果Launcher有mParent Activity,就會執行mParent.startActivityFromChild
;如果沒有,就會執行mInstrumentation.execStartActivity
。進入mParent.startActivityFromChild
方法會看到最終也是執行了mInstrumentation.execStartActivity
。執行完成後,會取得一個ActivityResult物件,用於給呼叫者Activity傳遞一些資料,最後在Activity切換時顯示Transition動畫。
這裡有一點需要指出的是:這裡的ParentActivity指的是類似TabActivity、ActivityGroup關係的巢狀Activity。之所以要強調parent和child,是要避免混亂的Activity巢狀關係。
我們進入Instrumentation類看看execStartActivity方法吧:
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult() : null;
}
break;
}
}
}
}
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
首先,我們通過引數IBinder contextThread
取得一個IApplicationThread型別的物件whoThread,而contextThread是由mMainThread.getApplicationThread()
取得的ApplicationThread物件,此時mMainThread指的就是Launcher應用的主執行緒,所以whoThread指代的自然是Launcher的ApplicationThread。
因為Activity的onProvideReferrer()
方法預設返回null,除非該方法被重寫,而我們使用的Launcher並沒有重寫該方法,所以不用管referrer。
然後判斷是否有ActivityMonitor,如果有,則即將要開啟的Activity是否和ActivityMonitor中儲存的IntentFilter匹配,如果匹配則增加ActivityMonitor的計數。大致是用於監控符合匹配規則的Activity的數量的。
最後呼叫ActivityManagerNative.getDefault().startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
啟動Activity,並檢查啟動是否成功。換句話說,最終負責啟動Activity的是ActivityManager,前面得到的ApplicationThread也是在這裡使用的。
那麼ActivityManager、ApplicationThread、ActivityThread都是什麼呢?
ActivityManagerService通過Binder將Launcher切換到pause狀態
首先,呼叫ActivityManagerNative.getDefault()
方法實際呼叫的是asInterface(IBinder obj)
方法,也就意味著我們使用的其實是ActivityManagerProxy,而ActivityManagerProxy則是ActivityManagerService的代理,詳見下面的程式碼:
static public IActivityManager getDefault() {
return gDefault.get();
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
那麼進入ActivityManagerProxy:
class ActivityManagerProxy implements IActivityManager
{
public ActivityManagerProxy(IBinder remote)
{
mRemote = remote;
}
public IBinder asBinder()
{
return mRemote;
}
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
// 建立兩個Parcel物件,data用於傳輸啟動Activity需要的資料,reply用於獲取
// 啟動Activity操作執行後系統返回的響應
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
// caller 就是Launcher提供的ApplicationThread(也就是前面提到的whoThread)
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
// 記錄啟動新Activity的應用的包名,也就是Launcher的包名
data.writeString(callingPackage);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
// 將resultTo這個IBinder物件寫入data,實際寫入的就是前面的引數——IBinder token
// 而這個token是什麼,我們暫時不管,後面會給出解釋
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(startFlags);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
if (options != null) {
data.writeInt(1);
options.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}
……省略餘下程式碼……
}
將資料都寫入後,就通過mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0)
傳輸資料並得到響應(寫入reply)。
前面已經提到了,ActivityManagerProxy是ActivityManagerService的代理,那麼呼叫mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0)
實際上就是通過Binder建立Launcher所在的程序與system_server程序(Android Framework層的服務幾乎都由system_server程序管理,因此ActivityManagerService執行在system_server程序中)的通訊,並把我們寫入data的資料通過Binder傳遞給ActivityManagerService。
ActivityManagerService得到我們用Parcelable封裝的data後就會呼叫startActivity
方法為Launcher啟動Activity:
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, options,
UserHandle.getCallingUserId());
}
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
enforceNotIsolatedCaller("startActivity");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, ALLOW_FULL_ONLY, "startActivity", null);
// TODO: Switch to user app stacks here.
return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, options, false, userId, null, null);
}
void enforceNotIsolatedCaller(String caller) {
if (UserHandle.isIsolated(Binder.getCallingUid())) {
throw new SecurityException("Isolated process not allowed to call " + caller);
}
}
enforceNotIsolatedCaller("startActivity");
作安全性檢查,判斷當前使用者是否允許啟動Activity,然後對之前傳入的userId進行轉換和安全性檢查。最後呼叫mStackSupervisor.startActivityMayWait
。這裡的mStackSupervisor是ActivityStackSupervisor物件,前面提到過,Task是以堆疊形式組織Activity的集合,而Task又由ActivityStack管理,ActivityStackSupervisor則是管理ActivityStack的類。
由於程式碼太長,下面只擷取部分關鍵程式碼講解:
首先,通過下面程式碼段呼叫PackageManagerService解析Intent(我們想要開啟的App的用於啟動MainActivity的Intent),將解析的結果儲存到ActivityInfo型別的物件裡:
// Collect information about the target of the Intent.
ActivityInfo aInfo =
resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
// Method - resolveActivity
ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
ProfilerInfo profilerInfo, int userId) {
// Collect information about the target of the Intent.
ActivityInfo aInfo;
try {
ResolveInfo rInfo =
AppGlobals.getPackageManager().resolveIntent(
intent, resolvedType,
PackageManager.MATCH_DEFAULT_ONLY
| ActivityManagerService.STOCK_PM_FLAGS, userId);
aInfo = rInfo != null ? rInfo.activityInfo : null;
} catch (RemoteException e) {
aInfo = null;
}
……省略,大致是做一些安全性檢查和相關資訊的設定……
return aInfo;
}
然後互斥鎖鎖住ActivityManagerService的例項mService,如果解析的ActivityInfo不為空,且ApplicationInfo有ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE
標記,意味著呼叫者App是屬於heavy-weight process,如果現在有另一個heavy-weight process正在執行,則需要進行一些額外的處理。然後進入到startActivityLocked
方法。
這裡通過註釋我們可以發現,若App有
ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE
標記,App就可視為heavy-weight process,該標記可以在AndroidManifest.xml中設定,它是用於宣告App是否享受系統提供的Activity狀態儲存/恢復功能的。但是似乎沒有App能成為heavy-weight process,因為PackageParser的parseApplication方法並不會解析該標籤。
在startActivityLocked
方法中,得到Launcher(Activity)的ActivityRecord(Activity相關的資訊),並建立我們要啟動的Activity的ActivityRecord,最終執行startActivityUncheckedLocked
繼續啟動Activity:
ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null;
if (resultTo != null) {
sourceRecord = isInAnyStackLocked(resultTo);
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
"Will send result to " + resultTo + " " + sourceRecord);
if (sourceRecord != null) {
if (requestCode >= 0 && !sourceRecord.finishing) {
resultRecord = sourceRecord;
}
}
}
…………
ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
requestCode, componentSpecified, voiceSession != null, this, container, options);
……
err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, true, options, inTask);
進入startActivityUncheckedLocked方法,完成一些簡單的初始化後,向下執行到這段程式碼:如果Intent裡有Intent.FLAG_ACTIVITY_NEW_DOCUMENT標記(在AndroidManifest.xml中宣告),且即將要開啟的Activity的啟動模式又被宣告為SingleInstance或SingleTask,那麼Intent中攜帶的標記和AndroidManifest中宣告的標記出現衝突,而AndroidManifest的優先順序是高於Intent的,因此將launchFlags的對應位置為0。
然後是對launchFlags一系列的置位,目的是設定啟動模式。
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
(launchSingleInstance || launchSingleTask)) {
// We have a conflict between the Intent and the Activity manifest, manifest wins.
Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
"\"singleInstance\" or \"singleTask\"");
launchFlags &=
~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
} else {
switch (r.info.documentLaunchMode) {
case ActivityInfo.DOCUMENT_LAUNCH_NONE:
break;
case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
launchFlags &= ~Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
break;
}
}
}
……
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
// If we are actually going to launch in to a new task, there are some cases where
// we further want to do multiple task.
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
if (launchTaskBehind
|| r.info.documentLaunchMode == ActivityInfo.DOCUMENT_LAUNCH_ALWAYS) {
launchFlags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
}
}
……
if (inTask == null) {
if (sourceRecord == null) {
// This activity is not being started from another... in this
// case we -always- start a new task.
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
"Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
} else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
// The original activity who is starting us is running as a single
// instance... this new activity it is starting must go on its
// own task.
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
} else if (launchSingleInstance || launchSingleTask) {
// The activity being started is a single instance... it always
// gets launched into its own task.
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
}
……
// 因為我們是從Launcher啟動目的Activity,所以sourceRecord不為null,值為Launcher的ActivityRecord
if (sourceRecord != null) {
if (sourceRecord.finishing) {
// 如果sourceRecord表示的Activity正在結束/被銷燬,那麼我們不能把該Activity看作啟動目的
// Activity的源Activity,因為和源Activity關聯的Task現在可能是空的(沒有Activity)或者
// 也在結束/被銷燬的過程中,所以我們不能盲目地把目的Activity放到該Task中。取而代之的是,
// 我們會為它找到一個可用的Task,但我們要先儲存源Activity的Task的資訊,使得我們在建立新
// 的可用的Task時能用到裡面的一些資訊。
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
Slog.w(TAG, "startActivity called from finishing " + sourceRecord
+ "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
newTaskInfo = sourceRecord.info;
newTaskIntent = sourceRecord.task.intent;
}
sourceRecord = null;
sourceStack = null;
} else {
sourceStack = sourceRecord.task.stack;
}
} else {
sourceStack = null;
}
……
// 為目的Activity建立新的Task
if (r.resultTo == null && inTask == null && !addingToTask
&& (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
targetStack = computeStackFocus(r, newTask);
targetStack.moveToFront("startingNewTask");
if (reuseTask == null) {
r.setTask(targetStack.createTaskRecord(getNextTaskId(),
newTaskInfo != null ? newTaskInfo : r.info,
newTaskIntent != null ? newTaskIntent : intent,
voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
taskToAffiliate);
if (DEBUG_TASKS) Slog.v(TAG_TASKS,
"Starting new activity " + r + " in new task " + r.task);
} else {
r.setTask(reuseTask, taskToAffiliate);
}
if (isLockTaskModeViolation(r.task)) {
Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
if (!movedHome) {
if ((launchFlags &
(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
// Caller wants to appear on home activity, so before starting
// their own activity we will bring home to the front.
r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
}
}
完成上面一系列的處理後,呼叫ActivityStack的startActivityLocked
方法繼續執行啟動Activity需要的操作,targetStack是通過這行程式碼targetStack = computeStackFocus(r, newTask)
為使用者新建的ActivityStack:
mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
intent, r.getUriPermissionsLocked(), r.userId);
if (sourceRecord != null && sourceRecord.isRecentsActivity()) {
r.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
}
if (newTask) {
EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
}
ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
targetStack.mLastPausedActivity = null;
targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
if (!launchTaskBehind) {
// Don't set focus on an activity that's going to the back.
mService.setFocusedActivityLocked(r, "startedActivity");
}
return ActivityManager.START_SUCCESS;
進入到ActivityStack的startActivityLocked
方法,首先為目的Activity建立ProcessRecord,然後用WindowManager進行一些切換視窗的操作,最後呼叫mStackSupervisor.resumeTopActivitiesLocked(this, r, options)
。
……
if (!isHomeStack() || numActivities() > 0) {
// We want to show the starting preview window if we are
// switching to a new task, or the next activity's process is
// not currently running.
boolean showStartingIcon = newTask;
ProcessRecord proc = r.app;
if (proc == null) {
proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid);
}
if (proc == null || proc.thread == null) {
showStartingIcon = true;
}
……呼叫WindowManager切換視窗……
}
……
if (doResume) {
mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
}
進入到resumeTopActivitiesLocked
方法,呼叫resumeTopActivityLocked
方法將所有ActivityStack(多個顯示裝置,每個裝置對應一個ActivityStack)棧頂的Activity切換到resume狀態(生命週期的onResume),而resumeTopActivityLocked
方法先避免遞迴呼叫,然後呼叫ActivityStack的resumeTopActivityInnerLocked
方法。
boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
Bundle targetOptions) {
if (targetStack == null) {
targetStack = mFocusedStack;
}
// Do targetStack first.
boolean result = false;
if (isFrontStack(targetStack)) {
result = targetStack.resumeTopActivityLocked(target, targetOptions);
}
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack == targetStack) {
// Already started above.
continue;
}
if (isFrontStack(stack)) {
stack.resumeTopActivityLocked(null);
}
}
}
return result;
}
下面這段程式碼主要就是做一些前期的檢查,避免做多餘的工作浪費時間,並且確保目標Activity處於正確的“狀態”,使得我們後面能把它切換到resume狀態並顯示。
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
// 判斷ActivityManagerService是否已經啟動完畢
if (!mService.mBooting && !mService.mBooted) {
// Not ready yet!
return false;
}
// 獲取parentActivity,如果parentActivity還未處於resume狀態,則不能將stack棧頂的Activity切換為resume狀態(Activity的巢狀關係不能弄亂)
ActivityRecord parent = mActivityContainer.mParentActivity;
if ((parent != null && parent.state != ActivityState.RESUMED) ||
!mActivityContainer.isAttachedLocked()) {
// Do not resume this stack if its parent is not resumed.
// TODO: If in a loop, make sure that parent stack resumeTopActivity is called 1st.
return false;
}
// 如果有正在初始化的Activity沒有位於ActivityStack的棧頂,且正在執行window的啟動和顯示,
// 則要將window相關的操作取消。因為這類Activity的視窗有可能被孤立,那麼它們有可能永遠也不會進入resume狀態
cancelInitializingActivities();
// 取得當前ActivityStack棧頂Activity的ActivityRecord
final ActivityRecord next = topRunningActivityLocked(null);
// 記住我們怎樣處理pause/resume狀態切換,並確保無論何時結束處理都會重置狀態
final boolean userLeaving = mStackSupervisor.mUserLeaving;
mStackSupervisor.mUserLeaving = false;
final TaskRecord prevTask = prev != null ? prev.task : null;
if (next == null) {
// next為null表示當前ActivityStack沒有要顯示的Activity
final String reason = "noMoreActivities";
if (!mFullscreen) {
// 如果當前ActivityStack不是全屏的,將焦點切換到下一個擁有Activity的可見ActivityStack中
final ActivityStack stack = getNextVisibleStackLocked();
if (adjustFocusToNextVisibleStackLocked(stack, reason)) {
return mStackSupervisor.resumeTopActivitiesLocked(stack, prev, null);
}
}
// 如果ActivityStack是全屏的,卻沒有可以顯示的Activity,那麼就顯示桌面(Launcher)
ActivityOptions.abort(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: No more activities go home");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
return isOnHomeDisplay() &&
mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, reason);
}
next.delayedResume = false;
// 如果當前棧頂Activity處於resume狀態,且就是我們要開啟的Activity,則直接結束
if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
mStackSupervisor.allResumedActivitiesComplete()) {
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
mWindowManager.executeAppTransition();
mNoAnimActivities.clear();
ActivityOptions.abort(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Top activity resumed " + next);
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
// 對prevActivity(Launcher)所在的Task進行一些判斷,如果prevTask和nextTask相同,那麼直接將
// prevTask直接設為棧頂Task;如果prevTask不是當前ActivityStack棧頂的Task,那麼它後面的Task
// 都應該放到Launcher的Task後面;後面則是有關是否為桌面的判斷和處理了。
final TaskRecord nextTask = next.task;
if (prevTask != null && prevTask.stack == this &&
prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) {
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
if (prevTask == nextTask) {
prevTask.setFrontOfTask();
} else if (prevTask != topTask()) {
// This task is going away but it was supposed to return to the home stack.
// Now the task above it has to return to the home task instead.
final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
mTaskHistory.get(taskNdx).setTaskToReturnTo(HOME_ACTIVITY_TYPE);
} else if (!isOnHomeDisplay()) {
return false;
} else if (!isHomeStack()){
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Launching home next");
final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
return isOnHomeDisplay() &&
mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, "prevFinished");
}
}
// 如果ActivityManagerService處於休眠狀態,而且此時沒有Activity處於resume狀態
// 且棧頂Activity處於pause狀態,則中斷排程
if (mService.isSleepingOrShuttingDown()
&& mLastPausedActivity == next
&& mStackSupervisor.allPausedActivitiesComplete()) {
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
mWindowManager.executeAppTransition();
mNoAnimActivities.clear();
ActivityOptions.abort(options);
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Going to sleep and all paused");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
// Make sure that the user who owns this activity is started. If not,
// we will just leave it as is because someone should be bringing
// another user's activities to the top of the stack.
if (mService.mStartedUsers.get(next.userId) == null) {
Slog.w(TAG, "Skipping resume of top activity " + next
+ ": user " + next.userId + " is stopped");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
// 確保我們要啟動的Activity沒有處於stop佇列、休眠佇列、等待變為可見佇列中
mStackSupervisor.mStoppingActivities.remove(next);
mStackSupervisor.mGoingToSleepActivities.remove(next);
next.sleeping = false;
mStackSupervisor.mWaitingVisibleActivities.remove(next);
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
// If we are currently pausing an activity, then don't do anything
// until that is done.
if (!mStackSupervisor.allPausedActivitiesComplete()) {
if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
"resumeTopActivityLocked: Skip resume: some activity pausing.");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return false;
}
……待續……
}
後面做的工作就是:將Launcher切換到pause狀態,用WindowManager將Launcher的視窗隱藏。現在只完成了Activity相關的預處理工作,目標應用的程序和主執行緒還沒有建立,因此後面會進入if的false分支呼叫mStackSupervisor.startSpecificActivityLocked
方法建立應用程序;如果目標Activity的程序和主執行緒已經建立,則進入if語句的true分支直接將目標Activity切換到resume狀態,並顯示目標Activity的視窗。
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
……續上……
// 步入setLaunchSource方法後可以知道,該方法實際是通過PowerManager的setWorkSource方法
// 設定WakeLock,使得在執行後面的工作時系統不會進入休眠狀態
mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
// 現在開始將當前Activity切換到pause狀態,使得棧頂Activity可以切換到resume狀態
boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0;
// 將後臺ActivityStack的Activity切換到pause狀態
boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
// 將當前ActivityStack中正在顯示Activity切換到pause狀態
if (mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Pausing " + mResumedActivity);
pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
}
if (pausing) {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,
"resumeTopActivityLocked: Skip resume: need to start pausing");
// At this point we want to put the upcoming activity's process
// at the top of the LRU list, since we know we will be needing it
// very soon and it would be a waste to let it get killed if it
// happens to be sitting towards the end.
if (next.app != null && next.app.thread != null) {
mService.updateLruProcessLocked(next.app, true, null);
}
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
……
ActivityStack lastStack = mStackSupervisor.getLastStack();
if (next.app != null && next.app.thread != null) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next);
// 目標Activity已經可見
mWindowManager.setAppVisibility(next.appToken, true);
next.startLaunchTickingLocked();
ActivityRecord lastResumedActivity =
lastStack == null ? null :lastStack.mResumedActivity;
ActivityState lastState = next.state;
mService.updateCpuStats();
// 目標Activity切換到resume狀態