Android面試題(31)-App啟動流程
先貼個連結,總結的挺全面
在看這篇文章之前,希望先看完我的之前的部落格 android面試(6)-Binder機制,因為關於App啟動流程設計很多Binder通訊;
先將“三個程序”,“六個大類”進行介紹:
三個程序:
Launcher程序:整個App啟動流程的起點,負責接收使用者點選螢幕事件,它其實就是一個Activity,裡面實現了點選事件,長按事件,觸控等事件,可以這麼理解,把Launcher想象成一個總的Activity,螢幕上各種App的Icon就是這個Activity的button,當點選Icon時,會從Launcher跳轉到其他頁面;
SystemServer程序:這個程序在整個的Android程序中是非常重要的一個,地位和Zygote等同,它是屬於Application Framework層的,Android中的所有服務,例如AMS, WindowsManager, PackageManagerService等等都是由這個SystemServer fork出來的。所以它的地位可見一斑。
App程序:你要啟動的App所執行的程序;
六個大類:
ActivityManagerService:(AMS)AMS是Android中最核心的服務之一,主要負責系統中四大元件的啟動、切換、排程及應用程序的管理和排程等工作,其職責與作業系統中的程序管理和排程模組相類似,因此它在Android中非常重要,它本身也是一個Binder的實現類。
Instrumentation:監控應用程式和系統的互動;
ActivityThread:應用的入口類,通過呼叫main方法,開啟訊息迴圈佇列。ActivityThread所在的執行緒被稱為主執行緒;
ApplicationThread:ApplicationThread提供Binder通訊介面,AMS則通過代理呼叫此App程序的本地方法
ActivityManagerProxy:AMS服務在當前程序的代理類,負責與AMS通訊。
ApplicationThreadProxy:ApplicationThread在AMS服務中的代理類,負責與ApplicationThread通訊。
可以說,啟動的流程就是通過這六個大類在這三個程序之間不斷通訊的過程;
我先簡單的梳理一下app的啟動的步驟:
(1)啟動的起點發生在Launcher活動中,啟動一個app說簡單點就是啟動一個Activity,那麼我們說過所有元件的啟動,切換,排程都由AMS來負責的,所以第一步就是Launcher響應了使用者的點選事件,然後通知AMS
(2)AMS得到Launcher的通知,就需要響應這個通知,主要就是新建一個Task去準備啟動Activity,並且告訴Launcher你可以休息了(Paused);
(3)Launcher得到AMS讓自己“休息”的訊息,那麼就直接掛起,並告訴AMS我已經Paused了;
(4)AMS知道了Launcher已經掛起之後,就可以放心的為新的Activity準備啟動工作了,首先,APP肯定需要一個新的程序去進行執行,所以需要建立一個新程序,這個過程是需要Zygote參與的,AMS通過Socket去和Zygote協商,如果需要建立程序,那麼就會fork自身,建立一個執行緒,新的程序會匯入ActivityThread類,這就是每一個應用程式都有一個ActivityThread與之對應的原因;
(5)程序建立好了,通過呼叫上述的ActivityThread的main方法,這是應用程式的入口,在這裡開啟訊息迴圈佇列,這也是主執行緒預設繫結Looper的原因;
(6)這時候,App還沒有啟動完,要永遠記住,四大組建的啟動都需要AMS去啟動,將上述的應用程序資訊註冊到AMS中,AMS再在堆疊頂部取得要啟動的Activity,通過一系列鏈式呼叫去完成App啟動;
下面這張圖很好的描述了上面的六大步:
接下來看看詳細看看這幾個步驟:
(1)Launcher響應使用者點選,通知AMS
Launcher呼叫startActivity()方法,而startActivity()最終也是呼叫startActivityForResult();
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
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);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
......
}
在其中呼叫了Instrumentation的execstartActivity方法:
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
.....
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManager.getService()
.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;
}
這裡呼叫了ActivityManager.getService方法返回的就是ActivityManagerProxy,用於與AMS通訊,這樣通過這些一系列的方法呼叫,就把Launcher程序和AMS程序進行通訊了一次;
(2)AMS響應Launcher程序請求
主要是通過ActivityStackSupervisor:startActivityUncheckedLocked去給Activity申請Task的;此方法經過intent的標誌值設定,通過findTaskLocked函式來查詢存不存這樣的Task,這裡返回的結果是null,即intentActivity為null,因此,需要建立一個新的Task來啟動這個Activity。現在處理堆疊頂端的Activity是Launcher,與我們即將要啟動的MainActivity不是同一個Activity,建立了一個新的Task裡面來啟動這個Activity。
經過棧頂檢測,則需要將Launcher推入Paused狀態,才可以啟動新的Activity。後續則呼叫至ActivityStack:startPausingLocked;此方法裡有prev.app.thread是一個ApplicationThread物件的遠端介面,通過呼叫這個遠端介面的schedulePauseActivity來通知Launcher進入Paused狀態。至此,AMS對Launcher的請求已經響應,這是我們發現又通過Binder通訊回撥至Launcher程序;
(3)Launcher程序掛起Launcher,再次通知AMS
這部分Launcher的ActivityThread處理頁面Paused並且再次通過ActivityManagerProxy通知AMS。
(4)AMS建立新的程序
建立新程序的時候,AMS會儲存一個ProcessRecord資訊,如果應用程式中的AndroidManifest.xml配置檔案中,我們沒有指定Application標籤的process屬性,系統就會預設使用package的名稱。每一個應用程式都有自己的uid,因此,這裡uid + process的組合就可以為每一個應用程式建立一個ProcessRecord。
這裡主要是呼叫Process:start介面來建立一個新的程序,新的程序會匯入android.app.ActivityThread類,並且執行它的main函式,這就是每一個應用程式都有一個ActivityThread例項來對應的原因。
先判斷是否有相應的 ProcessRecord,如果不存在,就需要新建程序,這個程序就是相應的應用程序。ActivityManagerService 通過 Socket 通訊的方式和 Zygote 進行協商,Zygote 在其監聽的 /dev/socket/zygote socket 中發現有需要建立程序的請求後,會 fork 自身,並返回相應的 Process Id。這個 Process 會進行相應的初始化,使得其具備與系統服務進行 IPC 通訊的能力;
(5)應用程序初始化
我們來看Activity的main函式,這裡綁定了主執行緒的Looper,並進入訊息迴圈,大家應該知道,整個Android系統是訊息驅動的,這也是為什麼主執行緒預設繫結Looper的原因:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
attach函式最終呼叫了ActivityManagerService的遠端介面ActivityManagerProxy的attachApplication函式,傳入的引數是mAppThread,這是一個ApplicationThread型別的Binder物件,它的作用是AMS與應用程序進行程序間通訊的。
(6)在AMS中註冊應用程序,啟動啟動棧頂頁面
前面我們提到了AMS負責系統中四大元件的啟動、切換、排程及應用程序的管理和排程等工作,通過上一個流程我們知道應用程序建立後通過Binder驅動與AMS產生互動,此時AMS則將應用程序建立後的資訊進行了一次註冊,如果拿Windows系統程式註冊到的登錄檔來理解這個過程,可能會更形象一些。
mMainStack.topRunningActivityLocked(null)從堆疊頂端取出要啟動的Activity,並在realStartActivityLockedhan函式中通過ApplicationThreadProxy調回App程序啟動頁面。
此時在App程序,我們可以看到,經過一些列的呼叫鏈最終呼叫至MainActivity:onCreate函式,之後會呼叫至onResume,而後會通知AMS該MainActivity已經處於resume狀態。至此,整個啟動流程告一段落。
上面講的是activity的啟動流程,下面要說的是,android系統的啟動流程,通俗來說,按下電源鍵,開機的過程系統做了些什麼:
1.當系統載入程式啟動Linux核心,核心會載入各種資料結構,和驅動程式,載入完畢之後,Android系統開始啟動並載入第一個使用者級別的程序:init(system/core/init/Init.c)
我們來看一看這個Init.c程式碼,看main函式
int main(int argc, char **argv)
{
...
//執行Linux指令
mkdir("/dev", 0755);
mkdir("/proc", 0755);
mkdir("/sys", 0755);
...
//解析執行init.rc配置檔案
init_parse_config_file("/init.rc");
...
}
主要建立了一些目錄,並且載入執行了一個init.rc的檔案,我們來看看這個init.rc
2.在system\core\rootdir\Init.rc中定義好的指令都會開始執行,其中執行了很多bin指令,啟動系統服務
//啟動孵化器程序,此程序是Android系統啟動關鍵服務的一個母程序
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 666
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
裡面啟動了一個app_process,我們看看這是什麼:
3.在appprocess資料夾下找到appmain.cpp,檢視main函式,發現以下程式碼
int main(int argc, const char* const argv[])
{
...
//啟動一個系統服務:ZygoteInit
runtime.start("com.android.internal.os.ZygoteInit",startSystemServer);
...
}
仍然是為啟動一個系統服務做準備,我們繼續跟下去看看:
4.在ZygoteInit.java中,檢視main方法
public static void main(String argv[]) {
...
//載入Android系統需要的類
preloadClasses();
...
if (argv[1].equals("true")) {
//呼叫方法啟動一個系統服務
startSystemServer();
}
...
}
在這裡主要是先載入了android系統所需要的類,然後啟動系統服務,繼續跟下去:
5.startSystemServer()方法的方法體
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003",
"--capabilities=130104352,130104352",
"--runtime-init",
"--nice-name=system_server",
"com.android.server.SystemServer",
};
...
//分叉啟動上面字串陣列定義的服務
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, debugFlags, null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
6.SystemServer服務被啟動
public static void main(String[] args) {
...
//載入動態連結庫
System.loadLibrary("android_servers");
//執行連結庫裡的init1方法
init1(args);
...
}
7.動態連結庫檔案和java類包名相同,找到com_android_server_SystemServer.cpp檔案
在com_android_server_SystemServer.cpp檔案中,找到了
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
//給init1方法對映一個指標,呼叫system_init方法
{ "init1", "([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 },
};
8.android_server_SystemServer_init1方法體中呼叫了system_init(),system_init()沒有方法體
在system_init.cpp檔案中找到system_init()方法,方法體中 //執行了SystemServer.java的init2方法 runtime->callStatic("com/android/server/SystemServer", "init2");
回到SystemServer.java,在init2的方法體中
//啟動一個服務執行緒
Thread thr = new ServerThread();
thr.start();
9.在ServerThread的run方法中
//準備訊息輪詢器
Looper.prepare();
...
//啟動大量的系統服務並把其逐一新增至ServiceManager
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
...
//呼叫systemReady,準備建立第一個activity
((ActivityManagerService)ActivityManagerNative.getDefault())
.systemReady(new Runnable(){
...
});
10.在ActivityManagerService.java中,有systemReady方法,方法體裡找到
//檢測任務棧中有沒有activity,如果沒有,建立Launcher
mMainStack.resumeTopActivityLocked(null);
11.在ActivityStack.java中,方法resumeTopActivityLocked
// Find the first activity that is not finishing.
ActivityRecord next = topRunningActivityLocked(null);
...
if (next == null) {
// There are no more activities! Let's just start up the
// Launcher...
if (mMainStack) {
return mService.startHomeActivityLocked();
}
}
...