Activity啟動過程中你所不知道的細節
在安卓面試過程中,經常被問到以下幾個問題:
1、Activity的生命週期
2、Activity啟動後到頁面展現出來的過程
3、應用啟動過程
其實這些問題都可以在Activity的啟動過程中(應用啟動其實也是Activity啟動的一個分支)尋找到答案,下面就讓我們來探究下Activity啟動過程到底經歷了什麼東西吧。
核心流程
先上一張核心的流程圖:

startActivity.png
由上圖可知,Activity啟動涉及到應用層、framework層兩個部分,應用層的互動主要由Instrumentation類承擔,作為與framework層通訊的入口管理類,framework層主要涉及ActivityManager對全域性Activity棧相關的管理、Application程序的管理以及最終ApplicationThread對Activity生命週期的排程。
整個過程涉及了兩次AIDL跨程序通訊,第一次是從startActivity發起方的程序切換到系統程序,呼叫入口程式碼位於類ActivityManager中:
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; } };
在系統程序中完成了Intent flag等資料處理以及Activity棧、TaskRecord的管理,然後再次使用AIDL跨程序呼叫,切換到應用程序完成Activity的建立及相關生命週期方法的回撥。所以,要理清楚Activity的啟動過程主要就是要理清楚framework層的程式碼流程,這時就需要去分析framework層的原始碼了,sdk裡面是不包含framework層的程式碼的,需要翻牆檢視: ofollow,noindex">https://android.googlesource.com/platform/frameworks/base/+refs ,筆者這邊分析的是android-cts-7.1_r20 tag的程式碼,不同版本的程式碼可能會有細微的差異。
Framework原始碼分析
首先從最開始的地方也就是ActivityManagerService開始(程式碼地址: https://android.googlesource.com/platform/frameworks/base/+/android-cts-7.1_r20/services/core/java/com/android/server/am/ActivityManagerService.java )
public class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ... @Override public final int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) { return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, UserHandle.getCallingUserId()); } final int startActivity(Intent intent, ActivityStackSupervisor.ActivityContainer container) { enforceNotIsolatedCaller("ActivityContainer.startActivity"); final int userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), mStackSupervisor.mCurrentUser, false, ActivityManagerService.ALLOW_FULL_ONLY, "ActivityContainer", null); // TODO: Switch to user app stacks here. String mimeType = intent.getType(); final Uri data = intent.getData(); if (mimeType == null && data != null && "content".equals(data.getScheme())) { mimeType = getProviderMimeType(data, userId); } container.checkEmbeddedAllowedInner(userId, intent, mimeType); intent.addFlags(FORCE_NEW_TASK_FLAGS); return mActivityStarter.startActivityMayWait(null, -1, null, intent, mimeType, null, null, null, null, 0, 0, null, null, null, null, false, userId, container, null); } @Override 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) { enforceNotIsolatedCaller("startActivity"); userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, "startActivity", null); // TODO: Switch to user app stacks here. return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, null, null, bOptions, false, userId, null, null); } ... }
由上述程式碼片段可以看出幾點:ActivityManager繼承了IActivityManager.Stub,熟悉AIDL的同學應該一眼就能看出這是AIDL的特點;startActivity最終由mActivityStarter執行,然後順著程式碼一步步往下分析即可。
最終經過一系列方法呼叫: ActivityStarter#startActivityUnchecked ->
ActivityStackSupervisor#resumeFocusedStackTopActivityLocked->
ActivityStack#resumeTopActivityUncheckedLocked->
ActivityStack#resumeTopActivityInnerLocked->
ActivityStackSupervisor#startSpecificActivityLocked->
ApplicationThread#schedule*完成了Activity的啟動過程。(有興趣的同學可以從ActivityManagerService類開始一步步分析下去。)
其中,我們只需要分析核心類 ActivityStack 、 ActivityStackSupervisor 、 ApplicationThread 即可。
首先看 ActivityStack#resumeTopActivityInnerLocked 方法
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { ··· // We need to start pausing the current activity so the top one can be resumed... final boolean dontWaitForPause = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0; boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, dontWaitForPause); if (mResumedActivity != null) { if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Pausing " + mResumedActivity); pausing |= startPausingLocked(userLeaving, false, next, dontWaitForPause); } ··· mStackSupervisor.startSpecificActivityLocked(next, true, true); ··· }
由上述程式碼片段可知,Activity啟動之前,必須先pause當前Activity,這裡最終也會呼叫到ApplicationThread裡面的方法,具體就不細說了。
然後再看 ActivityStackSupervisor#startSpecificActivityLocked 方法,最終發現同樣是呼叫到了 app.thread 中的排程方法,這也是整個過程的第二次AIDL跨程序呼叫。
public final class ActivityStackSupervisor implements DisplayListener { ... void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { // Is this activity's application already running? ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); r.task.stack.setLaunchTime(r); if (app != null && app.thread != null) { try { if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0 || !"android".equals(r.info.packageName)) { // Don't add this if it is a platform component that is marked // to run in multiple processes, because this is actually // part of the framework so doesn't make sense to track as a // separate apk in the process. app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode, mService.mProcessStats); } realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { Slog.w(TAG, "Exception when starting activity " + r.intent.getComponent().flattenToShortString(), e); } // If a dead object exception was thrown -- fall through to // restart the application. } mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true); } final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { ... app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo); ... } ... }
最後看 ApplicationThread#scheduleLaunchActivity 方法(注意ApplicationThread是ActivityThread的內部類哦)
// we use token to identify this activity without having to send the // activity itself back to the activity manager. (matters more with ipc) @Override public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { ... sendMessage(H.LAUNCH_ACTIVITY, r); }
這裡就是採用message機制的地方,通過message機制通知Handler H(也是ActivityThread的內部類)完成了Activity的啟動(大家可以思考下為什麼這裡用了message機制,筆者認為message機制主要是可以確保應用層Activity啟動的過程都是在主執行緒中完成的)
後續的程式碼片段如下:
Handler H: public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; } ... } private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. Activity a = performLaunchActivity(r, customIntent); ... handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason); ... } //建立Activity並通過Instrumentation呼叫Activity的onCreate方法 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); ... mInstrumentation.callActivityOnCreate(activity, r.state); .... } final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ... r = performResumeActivity(token, clearHide, reason); ... View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ... if (a.mVisibleFromClient && !a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); } }
由上面的程式碼片段可知Activity啟動順序為onCreate->onResume->DecorView繪製。
綜上所述可以得知Activity啟動生命週期為:
Activity source onPause->Activity target onCreate->Activity target onResume->頁面繪製(onStop的呼叫週期沒有找到就不列了)
最初的問題1,2也就有了答案了。
至於問題3,首先要明確的是,Android的桌面其實也是一個Activity,應用啟動其實也是startActivity的過程,唯一的區別在於此時應用程序還未建立,回到 ActivityStackSupervisor#startSpecificActivityLocked 方法可以發現,應用啟動的分支就在此處,當應用程序不存在時,會建立一個新的程序。
public final class ActivityStackSupervisor implements DisplayListener { ... void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { // Is this activity's application already running? ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); r.task.stack.setLaunchTime(r); if (app != null && app.thread != null) { try { if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0 || !"android".equals(r.info.packageName)) { // Don't add this if it is a platform component that is marked // to run in multiple processes, because this is actually // part of the framework so doesn't make sense to track as a // separate apk in the process. app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode, mService.mProcessStats); } realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { Slog.w(TAG, "Exception when starting activity " + r.intent.getComponent().flattenToShortString(), e); } // If a dead object exception was thrown -- fall through to // restart the application. } mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true); } }
除了應用程序的建立,應用啟動過程的區別還包括ActivityThread的建立、向ActivityManagerService繫結新建的ApplicationThread、建立UI Thread等操作,然後就是MainActivity的啟動過程與上述情況一至,具體流程就不詳細列舉了。
總結
通過上述framework層程式碼的分析,我們大致瞭解了Activity啟動的過程,其中筆者最重要的還是最開始的流程圖,我們主要需要了解整體的工作原理,對於程式碼細節沒必要死摳。
整體看下來,AIDL跨程序通訊貫穿了整個Acitivity的生命週期,這其實也是Android系統整體的一個設計思路:通過一系列framework層的ManagerService來實現和管理各種應用功能,相當於C/S架構,這些service相當於服務端,對於我們應用開發者只暴露基礎的介面,具體實現封裝在framework層可以隨時升級替換,應用層通過AIDL向“服務端”發起請求獲取各種基礎功能服務。