Android Application啓動流程分析

譯者注:
原文分成兩個部分, 鏈接如下:
http://multi-core-dump.blogspot.com/2010/04/android-application-launch.html
http://multi-core-dump.blogspot.com/2010/04/android-application-launch-part-2.html
本文合二爲一, 章節標題由譯者加註.

作者曾經在高通的Android性能組工作, 主要工作是優化Android Application的啓動時間.

1, App基礎理論

要想優化App啓動時間, 第一步就是了解App啓動進程的工作原理. 有幾個基礎理論:

Android Application與其他移動平臺有兩個重大不同點:

  1. 每個Android App都在一個獨立空間裏, 意味着其運行在一個單獨的進程中, 擁有自己的VM, 被系統分配一個唯一的user ID.
  2. Android App由很多不同組件組成, 這些組件還可以啓動其他App的組件. 因此, Android App並沒有一個類似程序入口的main()方法.

Android Application組件包括:

  • Activities: 前臺界面, 直接面向User, 提供UI和操作.
  • Services: 後臺任務.
  • Broadcast Receivers: 廣播接收者.
  • Contexnt Providers: 數據提供者.

Android進程與Linux進程一樣. 默認情況下, 每個apk運行在自己的Linux進程中. 另外, 默認一個進程裏面只有一個線程---主線程. 這個主線程中有一個Looper實例, 通過調用Looper.loop()從Message隊列裏面取出Message來做相應的處理.

那麼, 這個進程何時啓動的呢?
簡單的說, 進程在其需要的時候被啓動. 任意時候, 當用戶或者其他組件調取你的apk中的任意組件時, 如果你的apk沒有運行, 系統會爲其創建一個新的進程並啓動. 通常, 這個進程會持續運行直到被系統殺死. 關鍵是: 進程是在被需要的時候才創建的.

舉個例子, 如果你點擊email中的超鏈接, 會在瀏覽器裏面打開一個網頁. Email App和瀏覽器App是兩個不同的App, 運行在不同的進程中. 這次點擊事件促使Android系統去創建了一個新的進程來實例化瀏覽器的組件.

首先, 讓我們快速看下Android啓動流程. 與衆多基於Linux內核的系統類似, 啓動系統時, bootloader啓動內核和init進程. init進程分裂出更多名爲"daemons(守護進程)"的底層的Linux進程, 諸如android debug deamon, USB deamon等. 這些守護進程處理底層硬件相關的接口.

隨後, init進程會啓動一個非常有意思的進程---"Zygote". 顧名思義, 這是一個Android平臺的非常基礎的進程. 這個進程初始化了第一個VM, 並且預加載了framework和衆多App所需要的通用資源. 然後它開啓一個Socket接口來監聽請求, 根據請求孵化出新的VM來管理新的App進程. 一旦收到新的請求, Zygote會基於自身預先加載的VM來孵化出一個新的VM創建一個新的進程.

啓動Zygote之後, init進程會啓動runtime進程. Zygote會孵化出一個超級管理進程---System Server. SystemServer會啓動所有系統核心服務, 例如Activity Manager Service, 硬件相關的Service等. 到此, 系統準備好啓動它的第一個App進程---Home進程了.

2, 啓動App流程

用戶點擊Home上的一個App圖標, 啓動一個應用時:


app launch

Click事件會調用startActivity(Intent), 會通過Binder IPC機制, 最終調用到ActivityManagerService. 該Service會執行如下操作:

  • 第一步通過PackageManager的resolveIntent()收集這個intent對象的指向信息.
  • 指向信息被存儲在一個intent對象中.
  • 下面重要的一步是通過grantUriPermissionLocked()方法來驗證用戶是否有足夠的權限去調用該intent對象指向的Activity.
  • 如果有權限, ActivityManagerService會檢查並在新的task中啓動目標activity.
  • 現在, 是時候檢查這個進程的ProcessRecord是否存在了.

如果ProcessRecord是null, ActivityManagerService會創建新的進程來實例化目標activity.

以桌面啓動一個應用Activity爲例,onClick事件後,會調用startActivityForResult(Intent, int)

public void startActivityForResult(Intent intent, int requestCode) {
if (mParent == null) {
//Activity啓動執行交由Instrumentation對象去處理
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode);
//mMainThread 在attach方法中被設置,當ActivityThread PerformLauchActivity,調用attach把ActivityThread.this傳送過來
//mMainThread.getApplicationThread()它是一個進程通信服務端存根對象,提供了很多操作ActivityThread的方法,它繼承了ApplicationThreadNative 
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;
}
} else {
mParent.startActivityFromChild(this, intent, requestCode);
}
}
Instrumentation.execStartActivity
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode) {
......
try {
//ActivityManagerNative.getDefault()實際返回的是一個ActivityManagerProxy對象
int result = ActivityManagerNative.getDefault()
.startActivity
(whoThread, intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
null, 0, token, target != null ? target.mEmbeddedID : null,
requestCode, false, false);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
}
return null;
}

ActivityManagerProxy是實現IActivityManager接口的一個進程通信代理對象,在該方法ActivityManagerProxy.startActivity中,它只負責
準備相關的數據發送到system_process進程去處理startActivity:

public int startActivity(IApplicationThread caller, Intent intent,
String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
IBinder resultTo, String resultWho,
int requestCode, boolean onlyIfNeeded,
boolean debug) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeTypedArray(grantedUriPermissions, 0);
data.writeInt(grantedMode);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(onlyIfNeeded ? 1 : 0);
data.writeInt(debug ? 1 : 0);
//mRemote是一個BinderProxy對象,transact方法本地化實現
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}

到此前面3步都是在Laucher2進程中執行,調用mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);後,系統轉到
system_process進程中執行,根據我前面講的進程通信機制,入口函數是ActivityManagerNative.onTransact方法,在system_process進程中
ActivityManagerNative被繼承,實際執行的是ActivityManagerService.onTransact方法,調用堆棧如下:

ActivityManagerService(ActivityManagerNative).onTransact(int, Parcel, Parcel, int) line: 129 
ActivityManagerService.onTransact(int, Parcel, Parcel, int) line: 1481 
ActivityManagerService(Binder).execTransact(int, int, int, int) line: 288 
NativeStart.run() line: not available [native method]

在ActivityManagerService(ActivityManagerNative).onTransact中根據前面的參數START_ACTIVITY_TRANSACTION,執行對應的case代碼:

case START_ACTIVITY_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
Uri[] grantedUriPermissions = data.createTypedArray(Uri.CREATOR);
int grantedMode = data.readInt();
IBinder resultTo = data.readStrongBinder();
String resultWho = data.readString(); 
int requestCode = data.readInt();
boolean onlyIfNeeded = data.readInt() != 0;
boolean debug = data.readInt() != 0;
//執行對應Stub的IActivityManager接口方法
int result = startActivity(app, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug);
reply.writeNoException();
reply.writeInt(result);
return true;
}

前面紅色的startActivity方法實際是執行的ActivityManagerService中的startActivity,


2.1 創建進程

ActivityManagerService調用startProcessLocked()方法來創建新的進程, 該方法會通過前面講到的socket通道傳遞參數給Zygote進程. Zygote孵化自身, 並調用ZygoteInit.main()方法來實例化ActivityThread對象並最終返回新進程的pid.

ActivityThread隨後依次調用Looper.prepareLoop()和Looper.loop()來開啓消息循環.

流程圖如下:


process creation

接下來的執行很複雜,要想搞清楚這些細節還需要一些時間,我我們跳過這些,繼續沿着主幹道前行:

ActivityManagerService.startProcessLocked(ProcessRecord, String, String) line: 2043 
ActivityManagerService.startProcessLocked(String, ApplicationInfo, boolean, int, String, ComponentName, boolean) line: 1982 
ActivityManagerService.startSpecificActivityLocked(HistoryRecord, boolean, boolean) line: 1908 
ActivityManagerService.resumeTopActivityLocked(HistoryRecord) line: 2855 
ActivityManagerService.completePauseLocked() line: 2237 
ActivityManagerService.activityPaused(IBinder, Bundle, boolean) line: 5963 
ActivityManagerService.activityPaused(IBinder, Bundle) line: 5941 
ActivityManagerService(ActivityManagerNative).onTransact(int, Parcel, Parcel, int) line: 387 
ActivityManagerService.onTransact(int, Parcel, Parcel, int) line: 1481 
ActivityManagerService(Binder).execTransact(int, int, int, int) line: 288 
SystemServer.init1(String[]) line: not available [native method] 
SystemServer.main(String[]) line: 582 
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method] 
Method.invoke(Object, Object...) line: 521 
ZygoteInit$MethodAndArgsCaller.run() line: 868 
ZygoteInit.main(String[]) line: 626 
NativeStart.main(String[]) line: not available [native method]

在用例進程onPause後,通過ActivityManagerProxy.activityPaused執行相關操作,這也是一個用例進程到system_process進程的遠程調用,原來的用例進程需要進棧,並啓動一個新的Activity在屏幕最前端,我們只關注新Activity的啓動,首先關注新Activity應用進程的創建,看上面的調用堆棧,在函數startProcessLocked:

//app記錄的是一個要啓動ActivityThread進程的信息,hostingType=activity,hostingNameStr="com.iaiai.activity/.IaiaiActivity"
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
......
//Process.start是一個靜態方法,它將啓動一個新的進程,新進程的的入口main方法爲android.app.ActivityThread.main
int pid = Process.start("android.app.ActivityThread",
mSimpleProcessManagement ? app.processName : null, uid, uid,
gids, debugFlags, null); 
......


現在,我們終於看到了一個新的應用進程的創建,別急,在啓動主Activity我們還有很多工作要做,我我們可以想象到得,一個新的應用肯定要建立閉環的消息循環,然後它要把的一個ApplicationThreadProxy代理對象傳遞給system_process進程,這樣system_process進程就可以通過ApplicationThreadProxy代理對象來控制我們的應用進程了,比如它把廣播消息轉給應用進程,關閉應用進程等

先看新進程的main函數:

public static final void main(String[] args) {
SamplingProfilerIntegration.start();

Process.setArgV0("<pre-initialized>");
//建立looper消息循環隊列
Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);
//開始主線程消息循環
Looper.loop();

if (Process.supportsProcesses()) {
throw new RuntimeException("Main thread loop unexpectedly exited");
}

thread.detach();
String name = (thread.mInitialApplication != null)
? thread.mInitialApplication.getPackageName()
: "<unknown>";
Slog.i(TAG, "Main thread of " + name + " is now exiting");
}

在main函數中建立了閉環的消息循環,這個是一般ui程序做法,很容易理解,但是後續應用的啓動工作是如何進程的,關注我上面標註的紅色代碼,這裏創建了一個ActivityThread對象,ActivityThread構造時初始化了該應用進程的一些基本成員,最重要的我們關注

final Looper mLooper = Looper.myLooper();
final H mH = new H();//消息處理handler
在這裏,我們建立了消息處理器,它將負責處理main線程中Looper消息循環中的消息。

還用一個成員對象值得我們關注,那就是ApplicationThread對象,在ActivityThread對象被創建時,它也被構造了,我前面已經提到過了,它繼承了ApplicationThreadNative類,熟悉進程通信代理機制的朋友就清楚了,ApplicationThread就是一個通信代理存根實現類,我們可以看它的實現方法,都是調用queueOrSendMessage方法,派發消息交給ActivityThread的mH去處理,那麼我們很清楚了,ActivityThread代理存根對象,它負責執行來自遠程的調用,這些遠程的調用大部分來自system_process,所以,system_process很容易通過ApplicationThread的客戶端代理對象控制ActivityThread,事實就是如此,後面我們可以很好地看到這一點,

繼續看thread.attach(false)函數,參數標識是否系統進程,系統進程的入口函數是systemMain,而不是main方法,

private final void attach(boolean system) {//system==false
sThreadLocal.set(this);//ActivityThread對象關聯到主線程
mSystemThread = system;
if (!system) {//是非系統進程
...
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
//把ApplicationThread mAppThread attach到系統進程system_process,以便system_process控制當前應用的ActivityThread
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
}
} else {
//系統進程要作的處理
...
}
//接收來自ViewRoot的ConfigurationChanged消息,派發給mH處理(H.CONFIGURATION_CHANGED),
//一旦配置發生變更,mH將執行H.CONFIGURATION_CHANGED
ViewRoot.addConfigCallback(new ComponentCallbacks() {
public void onConfigurationChanged(Configuration newConfig) {
synchronized (mPackages) {
// We need to apply this change to the resources
// immediately, because upon returning the view
// hierarchy will be informed about it.
if (applyConfigurationToResourcesLocked(newConfig)) {
// This actually changed the resources! Tell
// everyone about it.
if (mPendingConfiguration == null ||
mPendingConfiguration.isOtherSeqNewer(newConfig)) {
mPendingConfiguration = newConfig;

queueOrSendMessage(H.CONFIGURATION_CHANGED, newConfig);
}
}
}
}
public void onLowMemory() {
}
});
}


2.2 綁定Application

接下來要做的就是將進程和指定的Application綁定起來. 這個是通過上節的ActivityThread對象中調用bindApplication()方法完成的. 該方法發送一個BIND_APPLICATION的消息到消息隊列中, 最終通過handleBindApplication()方法處理該消息. 然後調用makeApplication()方法來加載App的classes到內存中.

流程如下:


bind application

再來看一下attach方法的調用堆棧:

ActivityManagerProxy.attachApplication(IApplicationThread) line: 1542 
ActivityThread.attach(boolean) line: 4555 
ActivityThread.main(String[]) line: 4632 
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method] 
Method.invoke(Object, Object...) line: 521 
ZygoteInit$MethodAndArgsCaller.run() line: 868 
ZygoteInit.main(String[]) line: 626 
NativeStart.main(String[]) line: not available [native method]

這裏你又會看到一個熟悉的身影,ActivityManagerProxy,是的,這裏又使用了進程通信,通知ActivityManagerService執行attachApplication
看一下ActivityManagerProxy.attachApplication方法的代碼:

public void attachApplication(IApplicationThread app) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
//參數IApplicationThread app通過進程通信傳送到system_process進程,而app是一個ApplicationThread對象,不要被它的名稱所迷惑
//這裏它只是一個對象,它繼承了ApplicationThreadNative,而ApplicationThreadNative是實現IApplicationThread接口的一個進程通信接口存根類,當它到達system_process,system_process得到的是它的一個代理類ActivityManagerProxy
data.writeStrongBinder(app.asBinder());
mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
再次回到了system_process進程,先看一下system接收到來自新的Activity的遠程調用堆棧:

ActivityManagerService.attachApplicationLocked(IApplicationThread, int) line: 5591 
ActivityManagerService.attachApplication(IApplicationThread) line: 5677 
ActivityManagerService(ActivityManagerNative).onTransact(int, Parcel, Parcel, int) line: 363 
ActivityManagerService.onTransact(int, Parcel, Parcel, int) line: 1481 
ActivityManagerService(Binder).execTransact(int, int, int, int) line: 288 
NativeStart.run() line: not available [native method]

我們看attachApplicationLocked的實現,由於函數比較長,而且我也沒有深入仔細看,所以我只列出我理解的關鍵部分代碼

//thread來用戶進程的ApplicationThread代理對象,pid是用戶進程的pid
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
......
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
//當用戶進程創建時有一個標識用戶進程的pid,它關聯了ProcessRecord記錄,現在根據pid或者該記錄
app = mPidsSelfLocked.get(pid);
}
} else if (mStartingProcesses.size() > 0) {
app = mStartingProcesses.remove(0);
app.setPid(pid);
} else {
app = null;
}
......
//設置app相關參數
app.thread = thread;//設置app的thread爲用戶進程代理對象ActivityManagerProxy
app.curAdj = app.setAdj = -100;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.forcingToForeground = null;
app.foregroundServices = false;
app.debugging = false;

......
thread.bindApplication(processName, app.instrumentationInfo != null
? app.instrumentationInfo : app.info, providers,
app.instrumentationClass, app.instrumentationProfileFile,
app.instrumentationArguments, app.instrumentationWatcher, testMode, 
isRestrictedBackupMode || !normalMode,
mConfiguration, getCommonServicesLocked());
updateLruProcessLocked(app, false, true);
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
......
HistoryRecord hr = topRunningActivityLocked(null);
if (hr != null && normalMode) {
if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
//realStartActivityLocked會調用thread.scheduleLaunchActivity
if (realStartActivityLocked(hr, app, true, true))
 {
didSomething = true;
}
}

這裏通過遠程調用後thread並不是一個ApplicationThread對象,而是其一個遠程代理對象ApplicationThreadProxy,通過thread,可以操作ApplicationThread對象調用bindApplication和scheduleLaunchActivity:

先看bindApplication:

ActivityThread$ApplicationThread.bindApplication(String, ApplicationInfo, List, ComponentName, String, Bundle, IInstrumentationWatcher, int, boolean, Configuration, Map) line: 1655 
ActivityThread$ApplicationThread(ApplicationThreadNative).onTransact(int, Parcel, Parcel, int) line: 251 
ActivityThread$ApplicationThread(Binder).execTransact(int, int, int, int) line: 288 
NativeStart.run() line: not available [native method]

public final void bindApplication(String processName,
ApplicationInfo appInfo, List<ProviderInfo> providers,
ComponentName instrumentationName, String profileFile,
Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
int debugMode, boolean isRestrictedBackupMode, Configuration config,
Map<String, IBinder> services) {
//獲取來自system_process遠程調用傳遞過來的相關參數
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}

AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.profileFile = profileFile;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.debugMode = debugMode;
data.restrictedBackupMode = isRestrictedBackupMode;
data.config = config;
//派發給ActivityThread.mH去處理執行H.BIND_APPLICATION
queueOrSendMessage(H.BIND_APPLICATION, data);
}

我們看mH是如何處理的,mH接收到H.BIND_APPLICATION消息執行的對應是handleBindApplication函數,handleBindApplication函數中做了大量初始化ActivityThread的操作:

初始化mConfiguration

設置進程名

本地語言設置

設置包名稱

設置應用程序根路徑

設置應用程序data路徑

設置activity的context


2.3 啓動Activity

經過前兩個步驟之後, 系統已經擁有了該application的進程. 後面的調用順序就是普通的從一個已經存在的進程中啓動一個新進程的activity了.

實際調用方法是realStartActivity(), 它會調用application線程對象中的sheduleLaunchActivity()發送一個LAUNCH_ACTIVITY消息到消息隊列中, 通過 handleLaunchActivity()來處理該消息.

假設點擊的是一個視頻瀏覽的App, 其流程如下:


start activity

接着是第二步,scheduleLaunchActivity,先看調用堆棧:
ActivityThread$ApplicationThread.scheduleLaunchActivity(Intent, IBinder, int, ActivityInfo, Bundle, List, List, boolean, boolean) line: 1526 
ActivityThread$ApplicationThread(ApplicationThreadNative).onTransact(int, Parcel, Parcel, int) line: 130 
ActivityThread$ApplicationThread(Binder).execTransact(int, int, int, int) line: 288 
NativeStart.run() line: not available [native method]

同樣,該函數操作也是交給ActivityThread.mH來處理,對應case消息爲:H.LAUNCH_ACTIVITY

交由函數handleLaunchActivity處理,在這個函數中,根據ActivityRecord對像,獲取要啓動的Activity類信息,然後創建一個Activity,執行Activity的生命週期函數:onCreate,onResume,至此ActivitThread的handleBindApplication和handleLaunchActivity完成Activity的啓動操作。

最後,我再總結一下從桌面啓動一個應用Activity的啓動過程,畫了一個時序圖,有助於理解,希望走過路過的朋友別忘了留個腳印,或者留個磚板,這個blog花費了我不少時間:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章