分析Android中應(yīng)用的啟動流程
前言
在我們開始之前,希望您能最好已經(jīng)滿足以下條件:
1、有一份編譯后的Android源碼(親自動手實踐才會有更深入的理解)
2、對Binder機制有一定的了解
本文啟動流程分析基于Android 5.1的源碼。為什么是5.1的源碼呢?因為手邊編譯完的代碼只有這個版本…另外,用什么版本的源碼并不重要,大體的流程并無本質(zhì)上的區(qū)別,僅僅是實現(xiàn)細節(jié)的調(diào)整,找一個你熟悉的版本就好。
1、啟動時序圖
作為一個輕微強迫癥的人,整理的時序圖,相信大家按圖索驥,一定能搞明白整個啟動流程:

說明:為了讓大家更清楚的理解整個過程,將時序圖中劃分為三個部分:Launcher進程、System進程、App進程,其中有涉及共用的類以L / A進行區(qū)分表示跟哪個進程有關(guān),便于理解。
2、關(guān)鍵類說明
整個啟動流程因為會涉及到多次Binder通信,這里先簡要說明一下幾個類的用途,方便大家理解整個交互流程:
1、ActivityManagerService:AMS是Android中最核心的服務(wù)之一,主要負責(zé)系統(tǒng)中四大組件的啟動、切換、調(diào)度及應(yīng)用進程的管理和調(diào)度等工作,其職責(zé)與操作系統(tǒng)中的進程管理和調(diào)度模塊相類似,因此它在Android中非常重要,它本身也是一個Binder的實現(xiàn)類。
2、Instrumentation:顧名思義,它用來監(jiān)控應(yīng)用程序和系統(tǒng)的交互。
3、ActivityThread:應(yīng)用的入口類,系統(tǒng)通過調(diào)用main函數(shù),開啟消息循環(huán)隊列。ActivityThread所在線程被稱為應(yīng)用的主線程(UI線程)。
4、ApplicationThread:ApplicationThread提供Binder通訊接口,AMS則通過代理調(diào)用此App進程的本地方法。
5、ActivityManagerProxy:AMS服務(wù)在當(dāng)前進程的代理類,負責(zé)與AMS通信。
6、ApplicationThreadProxy:ApplicationThread在AMS服務(wù)中的代理類,負責(zé)與ApplicationThread通信。
3、流程分析
首先交代下整個流程分析的場景:用戶點擊Launcher上的應(yīng)用圖標到該應(yīng)用主界面啟動展示在用戶眼前。
這整個過程涉及到跨進程通信,所以我們將其劃分為時序圖中所展示三個進程:Launcher進程、System進程、App進程。為了不貼過長的代碼又能說清楚進程間交互的流程,這里簡述幾個重要的交互點。
從時序圖上大家也可以看到調(diào)用鏈相當(dāng)長,對應(yīng)的代碼量也比較大,而且時序圖只是分析了這個一個場景下的流程。道阻且長,行則將至!
3.1 Launcher響應(yīng)用戶點擊,通知AMS
Launcher做為應(yīng)用的入口,還是有必要交代一下的,我們來看看Launcher的代碼片段,Launcher使用的是packages/apps/Launcher3的的源碼。
public class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
...
/**
* 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;
}
...
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
onClickAppShortcut(v);
} else if (tag instanceof FolderInfo) {
...
} else if (v == mAllAppsButton) {
onClickAllAppsButton(v);
} else if (tag instanceof AppInfo) {
startAppShortcutOrInfoActivity(v);
} else if (tag instanceof LauncherAppWidgetInfo) {
...
}
}
private void startAppShortcutOrInfoActivity(View v) {
...
boolean success = startActivitySafely(v, intent, tag);
...
}
boolean startActivitySafely(View v, Intent intent, Object tag) {
...
try {
success = startActivity(v, intent, tag);
} catch (ActivityNotFoundException e) {
...
}
return success;
}
boolean startActivity(View v, Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
...
if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);
} else {
...
}
return true;
} catch (SecurityException e) {
...
}
return false;
}
}
通過starActicity輾轉(zhuǎn)調(diào)用到Activity:startActivityForResult而后則調(diào)用至Instrumentation:execStartActivity,代碼片段如下:
public class Instrumentation {
...
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
...
try {
...
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
...
} catch (RemoteException e) {
}
return null;
}
...
}
這里的ActivityManagerNative.getDefault返回ActivityManagerService的遠程接口,即ActivityManagerProxy接口,有人可能會問了為什么會是ActivityManagerProxy,這就涉及到Binder通信了,這里不再展開。通過Binder驅(qū)動程序,ActivityManagerProxy與AMS服務(wù)通信,則實現(xiàn)了跨進程到System進程。
3.2 AMS響應(yīng)Launcher進程請求
從上面的流程我們知道,此時AMS應(yīng)該處理Launcher進程發(fā)來的請求,請參看時序圖及源碼,此時我們來看ActivityStackSupervisor:startActivityUncheckedLocked方法,目測這個方法已經(jīng)超過600行代碼,來看一些關(guān)鍵代碼片段:
public final class ActivityStackSupervisor implements DisplayListener {
...
final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, Bundle options, TaskRecord inTask) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
...
final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
int launchFlags = intent.getFlags();
...
// We'll invoke onUserLeaving before onPause only if the launching
// activity did not explicitly state that this is an automated launch.
mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
...
ActivityRecord notTop =
(launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
// If the onlyIfNeeded flag is set, then we can do this if the activity
// being launched is the same as the one making the call... or, as
// a special case, if we do not know the caller then we count the
// current top activity as the caller.
if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
...
}
...
// If the caller is not coming from another activity, but has given us an
// explicit task into which they would like us to launch the new activity,
// then let's see about doing that.
if (sourceRecord == null && inTask != null && inTask.stack != null) {
final Intent baseIntent = inTask.getBaseIntent();
final ActivityRecord root = inTask.getRootActivity();
...
// If this task is empty, then we are adding the first activity -- it
// determines the root, and must be launching as a NEW_TASK.
if (launchSingleInstance || launchSingleTask) {
...
}
...
}
...
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;
}
}
...
// We may want to try to place the new activity in to an existing task. We always
// do this if the target activity is singleTask or singleInstance; we will also do
// this if NEW_TASK has been requested, and there is not an additional qualifier telling
// us to still place it in a new task: multi task, always doc mode, or being asked to
// launch this as a new task behind the current one.
if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| launchSingleInstance || launchSingleTask) {
// If bring to front is requested, and no result is requested and we have not
// been given an explicit task to launch in to, and
// we can find a task that was started with this same
// component, then instead of launching bring that one to the front.
if (inTask == null && r.resultTo == null) {
// See if there is a task to bring to the front. If this is
// a SINGLE_INSTANCE activity, there can be one and only one
// instance of it in the history, and it is always in its own
// unique task, so we do a special search.
ActivityRecord intentActivity = !launchSingleInstance ?
findTaskLocked(r) : findActivityLocked(intent, r.info);
if (intentActivity != null) {
...
}
}
}
...
if (r.packageName != null) {
// If the activity being launched is the same as the one currently
// at the top, then we need to check if it should only be launched
// once.
ActivityStack topStack = getFocusedStack();
ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
if (top != null && r.resultTo == null) {
if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
...
}
}
} else{
...
}
boolean newTask = false;
boolean keepCurTransition = false;
TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
sourceRecord.task : null;
// Should this be considered a new task?
if (r.resultTo == null && inTask == null && !addingToTask
&& (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
...
if (reuseTask == null) {
r.setTask(targetStack.createTaskRecord(getNextTaskId(),
newTaskInfo != null ? newTaskInfo : r.info,
newTaskIntent != null ? newTaskIntent : intent,
voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
taskToAffiliate);
...
} else {
r.setTask(reuseTask, taskToAffiliate);
}
...
} else if (sourceRecord != null) {
} else if (!addingToTask &&
(launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
} else if (inTask != null){
} else {
}
...
targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
...
return ActivityManager.START_SUCCESS;
}
...
}
函數(shù)經(jīng)過intent的標志值設(shè)置,通過findTaskLocked函數(shù)來查找存不存這樣的Task,這里返回的結(jié)果是null,即intentActivity為null,因此,需要創(chuàng)建一個新的Task來啟動這個Activity?,F(xiàn)在處理堆棧頂端的Activity是Launcher,與我們即將要啟動的MainActivity不是同一個Activity,創(chuàng)建了一個新的Task里面來啟動這個Activity。
經(jīng)過棧頂檢測,則需要將Launcher推入Paused狀態(tài),才可以啟動新的Activity。后續(xù)則調(diào)用至ActivityStack:startPausingLocked,我們來看一下這個函數(shù):
final class ActivityStack {
...
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
boolean dontWait) {
if (mPausingActivity != null) {
...
}
ActivityRecord prev = mResumedActivity;
if (prev == null) {
...
}
...
mResumedActivity = null;
mPausingActivity = prev;
mLastPausedActivity = prev;
mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
|| (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
prev.state = ActivityState.PAUSING;
...
if (prev.app != null && prev.app.thread != null) {
try {
...
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
userLeaving, prev.configChangeFlags, dontWait);
} catch (Exception e) {
...
}
} else {
...
}
...
}
...
}
這里的prev.app.thread是一個ApplicationThread對象的遠程接口,通過調(diào)用這個遠程接口的schedulePauseActivity來通知Launcher進入Paused狀態(tài)。至此,AMS對Launcher的請求已經(jīng)響應(yīng),這是我們發(fā)現(xiàn)又通過Binder通信回調(diào)至Launcher進程。
3.3 Launcher進程掛起Launcher,再次通知AMS
這個流程相對會簡單一些,我們來看ActivityThread:
public final class ActivityThread {
...
private void handlePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
...
performPauseActivity(token, finished, r.isPreHoneycomb());
// Make sure any pending writes are now committed.
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
// Tell the activity manager we have paused.
if (!dontReport) {
try {
ActivityManagerNative.getDefault().activityPaused(token);
} catch (RemoteException ex) {
}
}
...
}
}
...
}
這部分Launcher的ActivityThread處理頁面Paused并且再次通過ActivityManagerProxy通知AMS。
3.4 AMS創(chuàng)建新的進程
創(chuàng)建新進程的時候,AMS會保存一個ProcessRecord信息,如果應(yīng)用程序中的AndroidManifest.xml配置文件中,我們沒有指定Application標簽的process屬性,系統(tǒng)就會默認使用package的名稱。每一個應(yīng)用程序都有自己的uid,因此,這里uid + process的組合就可以為每一個應(yīng)用程序創(chuàng)建一個ProcessRecord。
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
...
private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
...
try {
...
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
boolean isActivityProcess = (entryPoint == null);
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
...
} catch () {
...
}
}
...
}
這里主要是調(diào)用Process:start接口來創(chuàng)建一個新的進程,新的進程會導(dǎo)入android.app.ActivityThread類,并且執(zhí)行它的main函數(shù),這就是每一個應(yīng)用程序都有一個ActivityThread實例來對應(yīng)的原因。
3.5 應(yīng)用進程初始化
我們來看Activity的main函數(shù),這里綁定了主線程的Looper,并進入消息循環(huán),大家應(yīng)該知道,整個Android系統(tǒng)是消息驅(qū)動的,這也是為什么主線程默認綁定Looper的原因:
public final class ActivityThread {
...
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
...
Looper.loop();
...
}
private void attach(boolean system) {
...
if (!system) {
...
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
} else {
...
}
...
}
...
}
attach函數(shù)最終調(diào)用了ActivityManagerService的遠程接口ActivityManagerProxy的attachApplication函數(shù),傳入的參數(shù)是mAppThread,這是一個ApplicationThread類型的Binder對象,它的作用是AMS與應(yīng)用進程進行進程間通信的。
3.6 在AMS中注冊應(yīng)用進程,啟動啟動棧頂頁面
前面我們提到了AMS負責(zé)系統(tǒng)中四大組件的啟動、切換、調(diào)度及應(yīng)用進程的管理和調(diào)度等工作,通過上一個流程我們知道應(yīng)用進程創(chuàng)建后通過Binder驅(qū)動與AMS產(chǎn)生交互,此時AMS則將應(yīng)用進程創(chuàng)建后的信息進行了一次注冊,如果拿Windows系統(tǒng)程序注冊到的注冊表來理解這個過程,可能會更形象一些。
mMainStack.topRunningActivityLocked(null)從堆棧頂端取出要啟動的Activity,并在realStartActivityLockedhan函數(shù)中通過ApplicationThreadProxy調(diào)回App進程啟動頁面。
public final class ActivityStackSupervisor implements DisplayListener {
...
final boolean realStartActivityLocked(ActivityRecord r,
ProcessRecord app, boolean andResume, boolean checkConfig)
throws RemoteException {
...
r.app = app;
...
try {
...
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
r.compat, r.launchedFromPackage, r.task.voiceInteractor, app.repProcState,
r.icicle, r.persistentState, results, newIntents, !andResume,
mService.isNextTransitionForward(), profilerInfo);
...
} catch (RemoteException e) {
...
}
...
}
...
}
此時在App進程,我們可以看到,經(jīng)過一些列的調(diào)用鏈最終調(diào)用至MainActivity:onCreate函數(shù),之后會調(diào)用至onResume,而后會通知AMS該MainActivity已經(jīng)處于resume狀態(tài)。至此,整個啟動流程告一段落。
4、總結(jié)
通過上述流程,相信大家可以有了一個基本的認知,這里我們忽略細節(jié)簡化流程,單純從進程角度來看下圖: launch_app_sim

圖上所畫這里就不在贅述,Activity啟動后至Resume狀態(tài),此時可交互。以上就是分析Android中應(yīng)用啟動流程的全部內(nèi)容了,如何有疑問歡迎大家指正交流。
- Android 啟動activity的4種方式及打開其他應(yīng)用的activity的坑
- Android應(yīng)用啟動另外一個apk應(yīng)用的方法
- Android優(yōu)化應(yīng)用啟動速度
- Android使用Intent啟動其他非系統(tǒng)應(yīng)用程序的方法
- android應(yīng)用實現(xiàn)開機自動啟動方法
- 解析android創(chuàng)建快捷方式會啟動兩個應(yīng)用的問題
- 解析Android應(yīng)用啟動后自動創(chuàng)建桌面快捷方式的實現(xiàn)方法
- Android筆記之:App應(yīng)用之啟動界面SplashActivity的使用
- Android Intent啟動別的應(yīng)用實現(xiàn)方法
- Android應(yīng)用框架之應(yīng)用啟動過程詳解
相關(guān)文章
android 網(wǎng)絡(luò)編程之網(wǎng)絡(luò)通信幾種方式實例分享
這篇文章主要介紹了android 網(wǎng)絡(luò)編程之網(wǎng)絡(luò)通信幾種方式,有需要的朋友可以參考一下2013-12-12
Android基準配置文件Baseline?Profile方案提升啟動速度
這篇文章主要為大家介紹了Android基準配置文件Baseline?Profile方案提升啟動速度示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02
詳解Android App中創(chuàng)建ViewPager組件的方法
這篇文章主要介紹了詳解Android App中創(chuàng)建ViewPager組件的方法,ViewPager最基本的功能就是可以使視圖滑動,需要的朋友可以參考下2016-03-03
Android實現(xiàn)檢測手機搖晃的監(jiān)聽器
本文給大家分享一段代碼實現(xiàn)檢測手機搖晃的監(jiān)聽器,代碼簡單易懂,非常不錯,感興趣的朋友參考下吧2016-12-12
Android開發(fā)中Activity的生命周期及加載模式詳解
這篇文章主要介紹了Android開發(fā)中Activity的生命周期及加載模式詳解的相關(guān)資料,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-05-05
Flutter開發(fā)之Widget自定義總結(jié)
這篇文章主要給大家介紹了關(guān)于Flutter開發(fā)中Widget自定義的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
解決Android Studio導(dǎo)入項目非常慢的辦法
在使用Android studio的時候常常遇到這樣的問題,從其他地方導(dǎo)入項目,Android studio呈現(xiàn)非常慢的現(xiàn)象!當(dāng)遇到這種情況時,可以看看是下面這篇文章,再按照方法來解決!2016-09-09

