簡要回顧
先回顧一下Android系統(tǒng)的啟動過程:
init進(jìn)程fork出Zygote進(jìn)程后,Zygote進(jìn)程會創(chuàng)建一個服務(wù)端socket,等待AMS發(fā)起socket請求。
同時,由Zygote進(jìn)程fork出的SystemServer進(jìn)程會啟動各項系統(tǒng)服務(wù),其中就包含了AMS,AMS會啟動Launcher桌面,此時就可以等待用戶點擊App圖標(biāo)來啟動應(yīng)用進(jìn)程了。

然后看下系統(tǒng)服務(wù)的啟動,不管是由init進(jìn)程啟動的獨(dú)立進(jìn)程的系統(tǒng)服務(wù)如SurfaceFlinger,還是由SystemServer進(jìn)程啟動的非獨(dú)立進(jìn)程的系統(tǒng)服務(wù)如AMS,都是在ServiceManager進(jìn)程中完成注冊和獲取的,在跨進(jìn)程通信上使用了Android的binder機(jī)制。

ServiceManager進(jìn)程本身也是一個系統(tǒng)服務(wù),經(jīng)過啟動進(jìn)程、啟動binder機(jī)制、發(fā)布自己和等待請求4個步驟,就可以處理其他系統(tǒng)服務(wù)的獲取和注冊需求了。
AMS發(fā)送socket請求
Android應(yīng)用進(jìn)程的啟動是被動式的,在Launcher桌面點擊圖標(biāo)啟動一個應(yīng)用的組件如Activity時,如果Activity所在的進(jìn)程不存在,就會創(chuàng)建并啟動進(jìn)程。
點擊App圖標(biāo)后經(jīng)過層層調(diào)用會來到ActivityStackSupervisor的startSpecificActivityLocked方法。
//ActivityStackSupervisor.JAVA
final ActivityManagerService mService;
void startSpecificActivityLocked(...) {
//查找Activity所在的進(jìn)程,ProcessRecord是用來封裝進(jìn)程信息的數(shù)據(jù)結(jié)構(gòu)
ProcessRecord app = mService.getProcessRecordLocked(...);
//如果進(jìn)程已啟動,并且binder句柄IApplicationThread也拿到了,那就直接啟動Activity
if (app != null && app.thread != null) {
realStartActivityLocked(r, app, andResume, checkConfig);
return;
}
//否則,讓AMS啟動進(jìn)程
mService.startProcessLocked(...);
}
app.thread并不是線程,而是一個binder句柄。應(yīng)用進(jìn)程使用AMS需要拿到AMS的句柄IActivityManager,而系統(tǒng)需要通知應(yīng)用和管理應(yīng)用的生命周期,所以也需要持有應(yīng)用進(jìn)程的binder句柄IApplicationThread。
也就是說,他們互相持有彼此的binder句柄,來實現(xiàn)雙向通信。

那IApplicationThread句柄是怎么傳給AMS的呢?Zygote進(jìn)程收到socket請求后會處理請求參數(shù),執(zhí)行ActivityThread的入口函數(shù)main。
//ActivityThread.java
public static void main(String[] args) {
//創(chuàng)建主線程的looper
Looper.prepareMainLooper();
//ActivityThread并不是線程,只是普通的java對象
ActivityThread thread = new ActivityThread();
//告訴AMS,應(yīng)用已經(jīng)啟動好了
thread.attach(false);
//運(yùn)行l(wèi)ooper,啟動消息循環(huán)
Looper.loop();
}
private void attach(boolean system) {
//獲取AMS的binder句柄IActivityManager
final IActivityManager mgr = ActivityManager.getService();
//告訴AMS應(yīng)用進(jìn)程已經(jīng)啟動,并傳入應(yīng)用進(jìn)程自己的binder句柄IApplicationThread
mgr.attachApplication(mAppThread);
}
所以對于AMS來說:
1.AMS向Zygote發(fā)起啟動應(yīng)用的socket請求,Zygote收到請求fork出進(jìn)程,返回進(jìn)程的pid給AMS;
2.應(yīng)用進(jìn)程啟動好后,執(zhí)行入口main函數(shù),通過attachApplication方法告訴AMS已經(jīng)啟動,同時傳入應(yīng)用進(jìn)程的binder句柄IApplicationThread。
完成這兩步,應(yīng)用進(jìn)程的啟動過程才算完成。
下面看AMS的startProcessLocked啟動應(yīng)用進(jìn)程時都做了些什么。
//ActivityManagerService.java
final ProcessRecord startProcessLocked(...){
ProcessRecord app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
//如果進(jìn)程信息不為空,并且已經(jīng)拿到了Zygote進(jìn)程返回的應(yīng)用進(jìn)程pid
//說明AMS已經(jīng)請求過了,并且Zygote已經(jīng)響應(yīng)請求然后fork出進(jìn)程了
if (app != null && app.pid > 0) {
//但是app.thread還是空,說明應(yīng)用進(jìn)程還沒來得及注冊自己的binder句柄給AMS
//即此時進(jìn)程正在啟動,那就直接返回,避免重復(fù)創(chuàng)建
if (app.thread == null) {
return app;
}
}
//調(diào)用重載方法
startProcessLocked(...);
}
之所以要判斷app.thread,是為了避免當(dāng)應(yīng)用進(jìn)程正在啟動的時候,假如又有另一個組件需要啟動,導(dǎo)致重復(fù)拉起(創(chuàng)建)應(yīng)用進(jìn)程。
繼續(xù)看重載方法startProcessLocked:
//ActivityManagerService.java
private final void startProcessLocked(...){
//應(yīng)用進(jìn)程的主線程的類名
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
ProcessStartResult startResult = Process.start(entryPoint, ...);
}
//Process.java
public static final ProcessStartResult start(...){
return zygoteProcess.start(...);
}
來到ZygoteProcess。
//ZygoteProcess.java
public final Process.ProcessStartResult start(...){
return startViaZygote(...);
}
private Process.ProcessStartResult startViaZygote(...){
ArrayList<String> argsForZygote = new ArrayList<String>();
//...處理各種參數(shù)
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
其中:
- openZygoteSocketIfNeeded打開本地socket
- zygoteSendArgsAndGetResult發(fā)送請求參數(shù),其中帶上了ActivityThread類名
- return返回的數(shù)據(jù)結(jié)構(gòu)ProcessStartResult中會有pid字段
梳理一下:

注意:Zygote進(jìn)程啟動時已經(jīng)創(chuàng)建好了虛擬機(jī)實例,所以由他fork出的應(yīng)用進(jìn)程可以直接繼承過來用而無需創(chuàng)建。
下面來看Zygote是如何處理socket請求的。
Zygote處理socket請求
從 圖解Android系統(tǒng)的啟動 一文可知,在ZygoteInit的main函數(shù)中,會創(chuàng)建服務(wù)端socket。
//ZygoteInit.java
public static void main(String argv[]) {
//Server類,封裝了socket
ZygoteServer zygoteServer = new ZygoteServer();
//創(chuàng)建服務(wù)端socket,名字為socketName即zygote
zygoteServer.registerServerSocket(socketName);
//進(jìn)入死循環(huán),等待AMS發(fā)請求過來
zygoteServer.runSelectLoop(abiList);
}
看到ZygoteServer。
//ZygoteServer.java
void registerServerSocket(String socketName) {
int fileDesc;
//socket真正的名字被加了個前綴,即 "ANDROID_SOCKET_" + "zygote"
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
//創(chuàng)建文件描述符fd
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
//創(chuàng)建LocalServerSocket對象
mServerSocket = new LocalServerSocket(fd);
}
void runSelectLoop(String abiList){
//進(jìn)入死循環(huán)
while (true) {
for (int i = pollFds.length - 1; i >= 0; --i) {
if (i == 0) {
//...
} else {
//得到一個連接對象ZygoteConnection,調(diào)用他的runOnce
boolean done = peers.get(i).runOnce(this);
}
}
}
}
來到ZygoteConnection的runOnce。
boolean runOnce(ZygoteServer zygoteServer){
//讀取socket請求的參數(shù)列表
String args[] = readArgumentList();
//創(chuàng)建應(yīng)用進(jìn)程
int pid = Zygote.forkAndSpecialize(...);
if (pid == 0) {
//如果是應(yīng)用進(jìn)程(Zygote fork出來的子進(jìn)程),處理請求參數(shù)
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
return true;
} else {
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
}
handleChildProc方法調(diào)用了ZygoteInit的zygoteInit方法,里邊主要做了3件事:
- 啟動binder線程池(后面分析)
- 讀取請求參數(shù)拿到ActivityThread類并執(zhí)行他的main函數(shù),執(zhí)行thread.attach告知AMS并回傳自己的binder句柄
- 執(zhí)行Looper.loop()啟動消息循環(huán)(代碼前面有)
這樣應(yīng)用進(jìn)程就啟動起來了。梳理一下:

下面看下binder線程池是怎么啟動的。
啟動binder線程池
Zygote的跨進(jìn)程通信沒有使用binder,而是socket,所以應(yīng)用進(jìn)程的binder機(jī)制不是繼承而來,而是進(jìn)程創(chuàng)建后自己啟動的。
前邊可知,Zygote收到socket請求后會得到一個ZygoteConnection,他的runOnce會調(diào)用handleChildProc。
//ZygoteConnection.java
private void handleChildProc(...){
ZygoteInit.zygoteInit(...);
}
//ZygoteInit.java
public static final void zygoteInit(...){
RuntimeInit.commonInit();
//進(jìn)入native層
ZygoteInit.nativeZygoteInit();
RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
來到AndroidRuntime.cpp:
//AndroidRuntime.cpp
static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz){
gCurRuntime->onZygoteInit();
}
來到app_main.cpp:
//app_main.cpp
virtual void onZygoteInit() {
//獲取單例
sp<ProcessState> proc = ProcessState::self();
//在這里啟動了binder線程池
proc->startThreadPool();
}
看下ProcessState.cpp:
//ProcessState.cpp
sp<ProcessState> ProcessState::self()
{
//單例模式,返回ProcessState對象
if (gProcess != NULL) {
return gProcess;
}
gProcess = new ProcessState("/dev/binder");
return gProcess;
}
//ProcessState構(gòu)造函數(shù)
ProcessState::ProcessState(const char *driver)
: mDriverName(String8(driver))
, mDriverFD(open_driver(driver)) //打開binder驅(qū)動
,//...
{
if (mDriverFD >= 0) {
//mmap是一種內(nèi)存映射文件的方法,把mDriverFD映射到當(dāng)前的內(nèi)存空間
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ,
MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
}
}
//啟動了binder線程池
void ProcessState::startThreadPool()
{
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
spawnPooledThread(true);
}
}
void ProcessState::spawnPooledThread(bool isMain)
{
if (mThreadPoolStarted) {
//創(chuàng)建線程名字"Binder:${pid}_${自增數(shù)字}"
String8 name = makeBinderThreadName();
sp<Thread> t = new PoolThread(isMain);
//運(yùn)行binder線程
t->run(name.string());
}
}
ProcessState有兩個宏定義值得注意一下:
//ProcessState.cpp
//一次Binder通信最大可以傳輸?shù)拇笮∈?1MB-4KB*2
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
//binder驅(qū)動的文件描述符fd被限制了最大線程數(shù)15
#define DEFAULT_MAX_BINDER_THREADS 15
我們看下binder線程PoolThread長啥樣:
class PoolThread : public Thread {
public:
explicit PoolThread(bool isMain)
: mIsMain(isMain){}
protected:
virtual bool threadLoop()
{ //把binder線程注冊進(jìn)binder驅(qū)動程序的線程池中
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
}
const bool mIsMain;
};
來到IPCThreadState.cpp:
//IPCThreadState.cpp
void IPCThreadState::joinThreadPool(bool isMain)
{
//向binder驅(qū)動寫數(shù)據(jù):進(jìn)入死循環(huán)
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
status_t result;
do {
//進(jìn)入死循環(huán),等待指令的到來
result = getAndExecuteCommand();
} while (result != -ECONNREFUSED && result != -EBADF);
//向binder驅(qū)動寫數(shù)據(jù):退出死循環(huán)
mOut.writeInt32(BC_EXIT_LOOPER);
}
status_t IPCThreadState::getAndExecuteCommand()
{
//從binder驅(qū)動讀數(shù)據(jù),得到指令
cmd = mIn.readInt32();
//執(zhí)行指令
result = executeCommand(cmd);
return result;
}
梳理一下binder的啟動過程:
- 打開binder驅(qū)動
- 映射內(nèi)存,分配緩沖區(qū)
- 運(yùn)行binder線程,進(jìn)入死循環(huán),等待指令
總結(jié)
綜上,Android應(yīng)用進(jìn)程的啟動可以總結(jié)成以下步驟:
- 點擊Launcher桌面的App圖標(biāo)
- AMS發(fā)起socket請求
- Zygote進(jìn)程接收請求并處理參數(shù)
- Zygote進(jìn)程fork出應(yīng)用進(jìn)程,應(yīng)用進(jìn)程繼承得到虛擬機(jī)實例
- 應(yīng)用進(jìn)程啟動binder線程池、運(yùn)行ActivityThread類的main函數(shù)、啟動Looper循環(huán)
完整流程圖:

面試前的知識梳理,儲備提升
自己的知識準(zhǔn)備得怎么樣,這直接決定了你能否順利通過一面和二面,所以在面試前來一個知識梳理,看需不需要提升自己的知識儲備是很有必要的。
關(guān)于知識梳理,這里再分享一下我面試這段時間的復(fù)習(xí)路線:(以下體系的復(fù)習(xí)資料是我從各路大佬收集整理好的)
- 架構(gòu)師筑基必備技能:深入Java泛型+注解深入淺出+并發(fā)編程+數(shù)據(jù)傳輸與序列化+Java虛擬機(jī)原理+反射與類加載+動態(tài)代理+高效IO
- Android高級UI與FrameWork源碼:高級UI晉升+Framework內(nèi)核解析+Android組件內(nèi)核+數(shù)據(jù)持久化
- 360°全方面性能調(diào)優(yōu):設(shè)計思想與代碼質(zhì)量優(yōu)化+程序性能優(yōu)化+開發(fā)效率優(yōu)化
- 解讀開源框架設(shè)計思想:熱修復(fù)設(shè)計+插件化框架解讀+組件化框架設(shè)計+圖片加載框架+網(wǎng)絡(luò)訪問框架設(shè)計+RXJava響應(yīng)式編程框架設(shè)計+IOC架構(gòu)設(shè)計+Android架構(gòu)組件Jetpack
- NDK模塊開發(fā):NDK基礎(chǔ)知識體系+底層圖片處理+音視頻開發(fā)
- 微信小程序:小程序介紹+UI開發(fā)+API操作+微信對接
- Hybrid 開發(fā)與Flutter:html5項目實戰(zhàn)+Flutter進(jìn)階

知識梳理完之后,就需要進(jìn)行查漏補(bǔ)缺,所以針對這些知識點,我手頭上也準(zhǔn)備了不少的電子書和筆記,這些筆記將各個知識點進(jìn)行了完美的總結(jié)。

《507頁Android開發(fā)相關(guān)源碼解析》

《379頁Android開發(fā)面試寶典》
3.項目復(fù)盤
實際上,面試的一二輪所問到的技術(shù)問題,很多都是圍繞著你的項目展開,因此在面試前最后要做好的一件事情就是項目復(fù)盤。關(guān)于項目復(fù)盤,我個人的思路如下,可供參考:
- 你在這個項目中承擔(dān)了什么樣的角色?
- 這個項目的背景是什么,如果是技術(shù)項目,為什么要做?
- 有哪些技術(shù)難點,是怎么解決的,是否還有更好的方案?
- 你認(rèn)為項目中是否有可以改進(jìn)的點?
- 這個項目解決了什么問題,最好用數(shù)據(jù)說話,這個數(shù)據(jù)又是怎么得出來的?
提前把思路捋一捋,上面這些問題好好思考或準(zhǔn)備一下,做到心中有譜以后,自然能夠面試官聊得融洽,保持一個好的心態(tài),通過的幾率就會更大一些。
以上文章中的資料,均可以免費(fèi)分享給大家來學(xué)習(xí),
資料太多,全部展示會影響篇幅,暫時就先列舉這些部分截圖;