一圖摸清Android應(yīng)用進(jìn)程的啟動~
大綱:
- 簡要回顧
- AMS發(fā)送socket請求
- Zygote處理socket請求
- 啟動binder線程池
- 總結(jié)
- 細(xì)節(jié)補(bǔ)充
- 參考資料
本文約2.5k字寺鸥,閱讀大約11分鐘丈甸。
Android源碼基于8.0。
簡要回顧
先回顧一下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)程會啟動各項(xiàng)系統(tǒng)服務(wù)液斜,其中就包含了AMS累贤,AMS會啟動Launcher桌面叠穆,此時就可以等待用戶點(diǎn)擊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桌面點(diǎn)擊圖標(biāo)啟動一個應(yīng)用的組件如Activity時,如果Activity所在的進(jìn)程不存在状勤,就會創(chuàng)建并啟動進(jìn)程鞋怀。
點(diǎ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句柄闺金,來實(shí)現(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來說槽棍,
- AMS向Zygote發(fā)起啟動應(yīng)用的socket請求,Zygote收到請求fork出進(jìn)程,返回進(jìn)程的pid給AMS刹泄;
- 應(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ī)實(shí)例,所以由他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膀篮,
//ZygoteConnection.java
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有兩個宏定義值得注意一下秉剑,感興趣可以看 一次Binder通信最大可以傳輸多大的數(shù)據(jù) 這篇文章泛豪,
//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é)成以下步驟:
- 點(diǎn)擊Launcher桌面的App圖標(biāo)
- AMS發(fā)起socket請求
- Zygote進(jìn)程接收請求并處理參數(shù)
- Zygote進(jìn)程fork出應(yīng)用進(jìn)程荠雕,應(yīng)用進(jìn)程繼承得到虛擬機(jī)實(shí)例
- 應(yīng)用進(jìn)程啟動binder線程池、運(yùn)行ActivityThread類的main函數(shù)驶赏、啟動Looper循環(huán)
完整流程圖:
可見binder用得還是非常多的炸卑,下篇就補(bǔ)一補(bǔ)binder吧~
系列文章:
細(xì)節(jié)補(bǔ)充
- 拋異常清空堆棧幀:Zygote不是直接執(zhí)行ActivityThread的main函數(shù)的,而是通過拋出一個異常進(jìn)行捕獲煤傍,捕獲后再執(zhí)行盖文,這樣可以清除初始化過程產(chǎn)生的調(diào)用堆棧,讓ActivityThread的main函數(shù)看起來像個應(yīng)用程序進(jìn)程的入口函數(shù)蚯姆。
參考資料
- 書籍 - Android進(jìn)階解密
- 博客 - 一次Binder通信最大可以傳輸多大的數(shù)據(jù)