APP的啟動流程梳理
前言
整整一週,利用晚上下班時間梳理了桌面點選APP到開啟的流程,可以說看似簡簡單單的一個操作,裡邊的過程相當複雜,各種程序間通訊等等,下面我們一起看看整個流程是怎麼樣的。
正文
我們先通過一張圖來看整個流程的一個概覽:
startActivity
桌面點選app到開啟,涉及了三個程序,分別如下:
Launcher程序:它是一個Activity,可以把桌面看成是一個app,裡邊有多個啊、其他app的入口,當點選app圖示時,就會去啟動對應的app,並且跳轉至頁面。
SystemServer程序:在Android系統中有著重要的作用,由Zygote程序fork出來,許多重要的服務,都是在此程序開啟的,例如ActivityManagerService、InputManagerService和WindowManagerService等等。
APP程序:我們要啟動的app的程序。
startActivity
首先我們從startActivity開始,startActivity最終會呼叫startActivityForResult:
java
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);
//...
} else {
//...
}
}
在這裡我們先了解一下Instrumentation,每個Activity都會持有一個Instrumentation引用,整個程序只會有一個Instrumentation的例項,它主要是完成對Application和Activity初始化和生命週期的工具類。
我們可以看到,裡邊呼叫了mInstrumentation的execStartActivity方法,其中的核心程式碼如下:
java
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
//...
try {
//...
int result = ActivityTaskManager.getService().startActivity(whoThread,
who.getBasePackageName(), who.getAttributionTag(), 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;
}
ActivityTaskManager.getService()返回的是一個ActivityManagerProxy,它就是用來與AMS進行通訊的,AMS是執行在system_server程序中的,這一次的呼叫,實際是通過Binder的方式,呼叫到了AMS的startActivity方法。
fork建立新程序
呼叫了ActivityManagerService的startActivity方法後,經過一系列的呼叫,來到了startProcesslocked方法,然後會通過Process.start()方法,這次使用的是socket通訊方式向zygote程序傳送一個建立新程序的請求,也就是請求Zygote去建立App程序。
Zygote程序監聽到有建立新程序的請求後,便會fork新的程序,並返回對應的pid。新程序建立後,然後會執行ActivityThread的main()方法。
這裡說一下Zygote程序:由init程序fork出來的,當建立app程序時,都是由zygote程序fork而來的。
不知你會不會有兩個疑問:
為什麼SystemServer程序與Zegote程序通訊不是使用Binder而是使用Socket?下面整理了網友的幾點回答。
- zegote比serviceManager先啟動,這點從先後順序看,沒有serviceManager可以註冊,沒法用Binder。
- 假設它們誰先啟動這個順序不確定,但是如果serviceManager先啟動,但沒法保證它先初始化完。
- 在安全性上,socket的所有者是root,group是system,只有系統許可權使用者才能進行讀寫。
為什麼APP程序需要由Zegote程序fork出來?
我們知道每個APP都執行在獨立的Dalvik虛擬機器中,如果每啟動一個APP就得去單獨啟動跟初始化,那麼是比較耗時的。Zegote程序會把已經載入好的虛擬機器程式碼和記憶體資訊共享,通過它fork會起到一個預載入作用,加快了app的啟動。
繫結Application
建立執行緒後,便會執行ActivityThread的main函式,main函式裡邊會啟動主執行緒的Looper。在我們初學Java的時候可以知道,main函式是一個應用程式的入口。
main函式裡邊會呼叫ActivityThread的attach方法。
java
//由於在main方法裡邊呼叫傳進來的system為false,所以我們只看第一個分支。
private void attach(boolean system, long startSeq) {
//...
if (!system) {
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
} else {
//...
}
}
attach方法中,會遠端呼叫ActivityManagerService的attachApplication方法,ActivityManagerService最後會通過遠端呼叫ActivityThread的bindApplication,bindApplication會發送一個BIND_APPLICATION的訊息,
java
public final void bindApplication(String processName, ApplicationInfo appInfo,
ProviderInfoList providerList, ComponentName instrumentationName,/*省略n個引數*/) {
//...
sendMessage(H.BIND_APPLICATION, data);
}
接收到此訊息後,通過handleBindApplication方法進行處理,然後呼叫LoadedApk的makeApplication方法,由Instrumentation載入Application例項出來,
```java public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { if (mApplication != null) { return mApplication; }
//...
Application app = null;
//...
try {
final java.lang.ClassLoader cl = getClassLoader();
//...
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
//...
}
//... mApplication = app;
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
//...
}
}
return app;
} ```
newApplication方法裡邊會去加載出Application物件,並且呼叫它的attach方法。
java
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
建立完之後,回到ActivityThread的handleBindApplication方法,會通過Instrumentation去呼叫Application的onCreate方法。
java
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
//...
}
到這裡Application的建立就完成了。
啟動Activity
經過一連串的呼叫,最後會向H傳送一個訊息,這裡的H是一個Handler,由於版本不同,所以這裡會有不一樣,它們最終都會呼叫到handleLaunchActivity方法:
java
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
//...
final Activity a = performLaunchActivity(r, customIntent);
//...
return a;
}
performLaunchActivity方法裡邊會建立Activity,並且走onCreate
```java private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { //...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
//... } catch (Exception e) { //... }
try {
//...設定一些引數等,還有attach進Application
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
//...
}
r.setState(ON_CREATE);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
//...
}
return activity;
} ```
可以看到,又有Instrumentation的身影,此方法裡邊通過mInstrumentation去建立Activity,然後設定引數、校驗等操作後,再繼續呼叫callActivityOnCreate方法,裡邊會去呼叫Activity的onCreate方法,至此,Activity被建立好了。
接下來就是繼續通過Handler,然後去呼叫Activity的onStart、onResume,到這裡,Activity就可見了。
小結
最後我們回顧一下整個流程:
- 在桌面點選app圖示,Launcher響應點選事件,然後經過呼叫,通過Binder的方式告訴在system_server程序中的ActivityManagerService去startActivity
- ActivityManagerService收到呼叫後,便會去請求建立一個新的程序,它通過Socket的方式,告訴Zygote程序去啟動一個新的程序。
- 新的程序啟動後,會執行ActivityThread的main方法,這是程式的入口,並且會開始Looper。
- 在main中,會去請求ActivityManagerService進行attach Application,再經過一系列的呼叫,會回到app程序,建立Application,並且讓Application進行attach。
- Application建立繫結完,便開始建立Activity,由AMS告訴APP程序去scheduleLaunchActivity,APP程序會發送一個Handler的訊息,收到這個訊息後由Instrumentation去建立Activity,接著繼續去呼叫Activity的onCreate、onStart和onResume的生命週期,至此,從桌面點選APP的圖示到APP啟動至可見已完成。
結語
到這裡我們便把APP的啟動流程過了一遍,看似很簡單的一個操作,實際上系統幫我們做了很多的事情。當然在原始碼上不同版本會存在不一樣,但它們的流程基本是一樣的,只是一些方法或者細節做了改變。
我正在參與掘金技術社群創作者簽約計劃招募活動,點選連結報名投稿。