Android runtime機制(二)zygote程序的啟動流程
在前一章裡介紹了Android runtime的Init程序的啟動,在Init程序裡,我們知道了runtime環境是如何搭建的和系統屬性設定在什麼時候設定的,並瞭解了使用配置檔案Init.rc來啟動系統Service和其他操作處理。
在這一章裡,我們將繼續跟進zygote程序的啟動過程。在zygote程序啟動過程中,虛擬機器的建立、jni functions註冊及java層ZygoteInit是我們需要關注和理解的,這有助於我們開發更加高效的Android程式。
本文主要以android-7.1.2_r11原始碼作為參考
主要程式碼路徑:
\frameworks\base\cmds\app_process\app_main.cpp
\frameworks\base\core\jni\AndroidRuntime.cpp
\frameworks\base\include\android_runtime\AndroidRuntime.h
\libnativehelper\JniInvocation.cpp
\libnativehelper\include\nativehelper\JniInvocation.h
\frameworks\base\core\java\android\os\ZygoteInit.java
zygote程序的啟動
在《Android runtime機制(一)init程序》篇中我們知道,zygote程序是在Init程序中通過執行如下命令啟動的
/system/bin/app_process64//64位系統使用
/system/bin/app_process32//32位系統使用
執行上述命令後,程式執行到了app_main.c的main()函式。
AndroidRuntime
在介紹zygote程序的啟動流程之前,我們先來看看兩個重要的類AppRuntime 和AndroidRuntime。
這兩個類是Android runtime執行環境的介面類,在Android runtime中起著至關重要的作用。
下面是這兩個類之間的關係:
class AppRuntime : public AndroidRuntime
從上面的類繼承關係中可以看到,AppRuntime類繼承自AndroidRuntime。從後續處理中,在Android 建立執行時環境的時候,主要是通過AppRuntime例項操作。
AndroidRuntime類
AndroidRuntime類是AppRuntime的父類,基本承載著Android runtime的絕大部分操作處理。
下面是AndroidRuntime類的宣告,從其宣告中,我們可以看到Android runtime的幾個重要的介面:
公共介面
- registerNativeMethods函式。JNI程式設計時,應用程式向虛擬機器註冊本地方法時使用。JNI程式設計時非常重要的一個函式介面。
- getRuntime函式。提供給外部獲取當前程序AndroidRuntime類例項的介面。
- getJavaVM函式。提供給外部獲取當前程序android虛擬機器的介面。
- getJNIEnv函式。提供給外部獲取當前程序JNI環境的介面。
私有介面
- startReg函式。系統級服務的JNI註冊函式。
- startVm函式。建立並啟動Android虛擬機器。
- mJavaVM。指向虛擬機器的指標,用以訪問Android虛擬機器。
具體請參考AndroidRuntime類的宣告:\frameworks\base\core\jni\AndroidRuntime.h
AppRuntime類
AppRuntime類繼承自AndroidRuntime類,過載了AndroidRuntime類的onVmCreated()、onStarted()、onZygoteInit()和onExit()函式,是zygote程序處理時實際runtime入口。
Android runtime類的功能
從上面AppRuntime類和AndroidRuntime類的實現宣告中可知,AndroidRumtime主要實現如下功能:
- 建立並啟動虛擬機器
- 註冊JNI服務
- 提供外部訪問虛擬機器的介面
- 提供外部訪問JNI環境的介面,並提供JNI程式開發的功能支援。
zygote程序的功能處理
zygote程序的入口處理函式是app_main.cpp中的main()函式。Android run time環境的建立和啟動,主要是通過AndroidRuntime來實現的。
zygote程序主要處理:
- app_main.cpp中的main()處理:解析傳入引數,例項化AppRuntime,啟動Android runtime執行時環境等
- Android虛擬機器的建立
- 向Android虛擬機器註冊Android native處理函式
- java層Zygote初始化處理
下面我們按照程式執行流程來具體梳理一下整個zygote程序的啟動處理過程。
app_main.cpp中的main()處理
1. 例項化AndroidRuntime類
///通過執行system/bin/app_process命令,根據service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server傳入引數
//argc是傳入引數的引數數目:argc=5
//argv是傳入的引數值:
// argv[0]="/system/bin/app_process64"
// argv[1]="-Xzygote"
// argv[2]="/system/bin"
// argv[3]="--zygote"
// argv[4]="--start-system-server"
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
2. 解析啟動引數
- 忽略命令列引數:”/system/bin/app_process64”
// Process command line arguments
// ignore argv[0]
//執行此語句塊後:
//argc=4
//"/system/bin/app_process64"被忽略,此時argv剩下的值為:
// argv[1]="-Xzygote"
// argv[2]="/system/bin"
// argv[3]="--zygote"
// argv[4]="--start-system-server"
argc--;
argv++;
- 把以“-”打頭的引數傳遞給虛擬機器,對於zygote,是把”-Xzygote”傳入虛擬機器
//執行此語句塊後:
//argc=3
//"-Xzygote"被傳遞給虛擬機器,被忽略,此時argv剩下的值為:
// argv[2]="/system/bin"
// argv[3]="--zygote"
// argv[4]="--start-system-server"
int i;
for (i = 0; i < argc; i++) {
//只有第一個引數"-Xzygote"滿足,第二個引數是"/system/bin",不是以"-"打頭的,所以退出for迴圈。
if (argv[i][0] != '-') {
break;
}
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip --.
break;
}
runtime.addOption(strdup(argv[i]));
}
- 解析剩餘傳入的引數,根據上一個語句塊的處理,此時剩餘引數值為:”/system/bin”、”–zygote”和”–start-system-server”
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
//根據引數"--zygote",表示當前程序是zygote程序,zygote=true,並設定nicename為zygote64(64位)或zygote。
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
//static const char ZYGOTE_NICE_NAME[] = "zygote64";或
//static const char ZYGOTE_NICE_NAME[] = "zygote";
niceName = ZYGOTE_NICE_NAME;
}
//根據引數"--start-system-server",需要啟動System Server。
else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg);
break;
} else {
--i;
break;
}
}
3. 變更zygote程序名為zygote或zygote64
//niceName在引數解析時被設定。此處變更了app_process啟動的程序名為zygote。
if (!niceName.isEmpty()) {
runtime.setArgv0(niceName.string());
set_process_name(niceName.string());
}
4. 呼叫AndroidRuntime.start()來建立和啟動Android 執行時環境
//zygote在引數解析時被設定為true。
//"com.android.internal.os.ZygoteInit"是Android Java執行時環境的初始化類。
//zygote=true。
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
return 10;
}
Android虛擬機器的建立
Android虛擬機器的建立是在AndroidRuntime.start()中處理的。
先初始化JniInvocation,然後通過startVm()->JNI_CreateJavaVM()來建立Android虛擬機器。
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
1. JniInvocation
JniInvocation,是一個外部和虛擬機器之間的一箇中間層,是外部訪問虛擬機器的API介面,允許外部動態的呼叫虛擬機器內部的實現。其主要實現如下功能:
- 指定載入的虛擬機器。現在Android系統中有兩種虛擬機器:dalvik和art。現在已基本使用art虛擬機器了。
- 封裝虛擬機器的物件介面
程式碼路徑:
\libnativehelper\JniInvocation.cpp
\libnativehelper\include\nativehelper\JniInvocation.h
JniInvocation的主要介面
//獲取預設的虛擬機器初始化引數
jint (*JNI_GetDefaultJavaVMInitArgs_)(void*);
//建立虛擬機器
jint (*JNI_CreateJavaVM_)(JavaVM**, JNIEnv**, void*);
//獲取已建立的虛擬機器物件
jint (*JNI_GetCreatedJavaVMs_)(JavaVM**, jsize, jsize*);
虛擬機器庫的載入
在JniInvocation的Init()函式首先會載入虛擬機器庫檔案。
虛擬機器庫檔案有兩種獲取方式:
- 使用者指定
- 系統預設
使用者可以通過persist.sys.dalvik.vm.lib.2屬性設定虛擬機器的庫檔案,預設的庫檔案是libart.so。
介面的初始化
在虛擬機器庫檔案載入成功後,需要對JniInvocation的介面進行初始化,提供虛擬機器介面功能供外部來訪問虛擬機器。
在init()函式裡,有如下處理:
//初始化JNI_GetDefaultJavaVMInitArgs_,呼叫JNI_GetDefaultJavaVMInitArgs_實際上就是呼叫虛擬機器庫函式JNI_GetDefaultJavaVMInitArgs。
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
"JNI_GetDefaultJavaVMInitArgs")) {
return false;
}
//初始化JNI_CreateJavaVM_,呼叫JNI_CreateJavaVM_實際上就是呼叫虛擬機器庫函式JNI_CreateJavaVM。
if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
"JNI_CreateJavaVM")) {
return false;
}
//初始化JNI_GetCreatedJavaVMs_,呼叫JNI_GetCreatedJavaVMs_實際上就是呼叫虛擬機器庫函式JNI_GetCreatedJavaVMs。
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
"JNI_GetCreatedJavaVMs")) {
return false;
}
函式JNI_CreateJavaVM()、JNI_GetCreatedJavaVMs()、JNI_GetDefaultJavaVMInitArgs()定義在art\runtime\java_vm_ext.cc下,是art虛擬機器的內部處理函式。
2. startVm的處理
在JniInvocation初始化之後,虛擬機器庫檔案被載入且介面函式也已被初始化。從JniInvocation的初始化中,我們知道,Android N的預設虛擬機器已是ART虛擬機器。
startVm()函式就是用來建立ART虛擬機器的。整個的處理過程就是配置虛擬機器的屬性然後呼叫虛擬機器庫函式JNI_CreateJavaVM()來建立。
在虛擬機器屬性配置過程中,有如下幾點值得我們注意:
- 虛擬機器的heapstartsize和heapsize分別被設定為4M和16M。這表明在Java的程序空間中,記憶體堆疊只有16M的空間
parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m");
parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");
- 執行模式的配置。執行模式是在屬性”dalvik.vm.execution-mode”裡配置的
property_get("dalvik.vm.execution-mode", propBuf, "");
if (strcmp(propBuf, "int:portable") == 0) {
executionMode = kEMIntPortable;
} else if (strcmp(propBuf, "int:fast") == 0) {
executionMode = kEMIntFast;
} else if (strcmp(propBuf, "int:jit") == 0) {
executionMode = kEMJitCompiler;
}
- NativeBridge的配置,在屬性”ro.dalvik.vm.native.bridge”裡設定。如果沒有被設定,則NativeBridge被disabled
// Native bridge library. "0" means that native bridge is disabled.
property_get("ro.dalvik.vm.native.bridge", propBuf, "");
if (propBuf[0] == '\0') {
ALOGW("ro.dalvik.vm.native.bridge is not expected to be empty");
} else if (strcmp(propBuf, "0") != 0) {
snprintf(nativeBridgeLibrary, sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX,
"-XX:NativeBridge=%s", propBuf);
addOption(nativeBridgeLibrary);
}
還有其他一些屬性配置,具體請參考int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)的實現。
向Android虛擬機器註冊Android native處理函式
在建立Android虛擬機器成功後,Android 執行環境也已準備就緒並執行。在Android系統服務執行的過程中,需要Java層和C/C++層的相互訪問。在Android系統中,虛擬機器提供JNI機制來實現Java層和C/C++層程式碼的相互訪問。
JNI機制的實現,主要依賴於虛擬機器的native method註冊機制,使Java類的方法和C/C++函式關聯起來。在Java類中呼叫相應方法的時候能直接尋找到對應的native函式並呼叫之。
為了相關服務能正常執行,在zygote中就需要對相關服務的JNI功能進行初始化,以使在相關服務執行的時候能夠使用底層提供的功能。
此階段就是來處理系統級的JNI的native函式註冊的。使用者級別的native函式的註冊是通過System.loadLibrary(libraryname)中處理,此部分將在JNI處理中介紹。
下面是AndroidRuntime::start()中的處理,直接呼叫AndroidRuntime::startReg(env)函式進行註冊。
/*
* Register android functions.
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
在AndroidRuntime::startReg(env)函式中,到底是如何註冊JNI native functions的呢?註冊了哪些JNI native functions呢?我們來看一下該函式的具體處理:
- 設定Android執行緒函式。Android所有的執行緒都是通過函式javaCreateThreadEtc()建立,並attach到虛擬機器中
/*
* This hook causes all future threads created in this process to be
* attached to the JavaVM. (This needs to go away in favor of JNI
* Attach calls.)
*/
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
- 註冊native funcitons。每一個native functions都是通過虛擬機器的RegisterNativeMethods()函式介面進行註冊的
/*
* Every "register" function calls one or more things that return
* a local reference (e.g. FindClass). Because we haven't really
* started the VM yet, they're all getting stored in the base frame
* and never released. Use Push/Pop to manage the storage.
*/
env->PushLocalFrame(200);
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
在zygote程序中註冊的native functions。請大家參考AndroidRuntime.cpp中的 static const RegJNIRec gRegJNI[]定義,gRegJNI[]是系統級JNI native functions列表。註冊native functions詳細處理,請參照JNI篇介紹,這裡暫時做一個大致介紹:
- 在虛擬機器中查詢當前java類是否已經載入,如果已載入,則直接把當前native function 與java類的方法關聯。如果當前java類沒有載入,則先載入該java類,然後再把native function 與java類的方法關聯。
- 在此註冊過程中,我們可以看到,註冊中涉及的java類已全部載入到虛擬機器中,並與其他程序共享,因為Android java層中,其他的程序都是fork zygote程序的。
java層Zygote初始化處理
在完成虛擬機器的建立及JNI native functions的註冊後,程式就會執行到Android Java層上。
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
//classname="com.android.internal.os.ZygoteInit"
char* slashClassName = toSlashClassName(className);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
//呼叫com.android.internal.os.ZygoteInit的main()方法。
env->CallStaticVoidMethod(startClass, startMeth, strArray);
}
}
下面我們來具體梳理一下Zygote Java層的處理過程
1. com.android.internal.os.ZygoteInit類的main()方法
檔案目錄:\frameworks\base\core\java\com\android\internal\os\ZygoteInit.java。
在ZygoteInit類的main()方法裡,主要實現如下功能:
- 保證在Zygote程序中沒有建立執行緒
- 建立zygote socket並監聽
- preload Android資源
- GC初始化並啟動
- 啟動System server程序
- 執行zygote程序的select loop
保證在Zygote程序中沒有建立執行緒
這個處理保證在Zygote Init處理的時候沒有執行緒被建立,也就是說,在Zygote程序中是沒有執行緒存在的。
// Mark zygote start. This ensures that thread creation will throw
// an error.
ZygoteHooks.startZygoteNoThreadCreation();
同時,在Zygote init處理完成後,關閉保護功能。
ZygoteHooks.stopZygoteNoThreadCreation();
建立zygote socket並監聽
zygote socket是應用程式通過Zygote程序來建立應用程序的一個通道,其主要被用在zygote select loop中來進行應用程式程序的建立處理。
//zygote socketName = "ANDROID_SOCKET_zygote"
String socketName = "zygote";
registerZygoteSocket(socketName);
preload Android資源
在zygote init中,Android的相關資源被預載入到Zygote程序空間中。這些資源在後續所有的應用程式程序中都是共享的,因為Android Java層程序都是Zygote的子程序。
//Android資源預載入的入口
preload();
下面是preload()方法的具體實現。
- preloadClasses:載入class類到虛擬機器中,需要載入的類是由/system/etc/preloaded-classes檔案指定。它是由相關preloaded-classes檔案生成,如:
\frameworks\base\preloaded-classes
\frameworks\base\compiled-classes-phone
虛擬機器會通過classloader把preloaded-classes檔案中指定的類都載入到虛擬機器中,方便應用程式的呼叫處理。此處會涉及到手機記憶體空間和手機開機效能問題,手機效能優化方面可以進一步深入研究。 - preloadResources:載入系統資源
- preloadOpenGL: 載入顯示資源
- preloadSharedLibraries: 載入共享庫,包含android.so、compiler_rt.so和jnigraphics.so
- preloadTextResources:載入語言庫
static void preload() {
Log.d(TAG, "begin preload");
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "BeginIcuCachePinning");
beginIcuCachePinning();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClasses");
preloadClasses();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadResources");
preloadResources();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
preloadOpenGL();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
preloadSharedLibraries();
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
endIcuCachePinning();
warmUpJcaProviders();
Log.d(TAG, "end preload");
}
GC初始化並啟動
// Do an initial gc to clean up after startup
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PostZygoteInitGC");
gcAndFinalize();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
啟動System server程序
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
在startSystemServer()方法裡,通過Zygote.forkSystemServer() native函式呼叫來建立system server 程序。從建立中可以看到,system server程序是zygote程序的子程序。
/* Hardcoded command line to start the system server */
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
在system_server程序建立之後,如果執行的是子程序,即system_server程序,則通過命令執行到com.android.server.SystemServer類的main()方法。
//執行在子程序system_server程序
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
//跳轉到com.android.server.SystemServer的main()方法繼續處理。
//具體實現請參考handleSystemServerProcess()方法的實現。
handleSystemServerProcess(parsedArgs);
}
執行zygote程序的select loop
zygote select loop是在zygote程序中執行,通過zygote socket接受相關命令來建立zygote程序的子程序。
下面是select loop 在main()方法的入口:
Log.i(TAG, "Accepting command socket connections");
runSelectLoop(abiList);
在runSelectLoop()方法中
- 監聽socket,通過Os.poll函式來等待POLLIN事件的到來。
- 通過ZygoteConnection來讀取socket傳過來的command並建立程序。
- zygote程序在執行select loop後,zygote程序就進入無限迴圈,一直等待socket的command,並做處理。
//zygote程序在此進入無限迴圈
while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
//zygote程序被阻塞,直至以下條件達到時退出:
//a file descriptor becomes ready;
//the call is interrupted by a signal handler; or
//the timeout expires.
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
//呼叫ZygoteConnection的runOnce()方法來fork程序。
boolean done = peers.get(i).runOnce();
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
至此,zygote程序啟動處理完成,最後一直停留在select loop中執行。
總結及程式設計相關事項
上面是Zygote程序的啟動過程。從Zygote程序的啟動過程中,我們可以獲取以下知識點,能在我們的程式設計中給予一定的幫助。
- 每個虛擬機器的記憶體堆疊只有16M,所以在Java處理中要特別注意記憶體的管理和使用。
- JNI的native function註冊。如果想讓自己的JNI處理在虛擬機器中供其他程序呼叫,則可以把註冊函式新增到gRegJNI[]數組裡。但是這樣會增加系統的開機時間及記憶體的開銷。
- 虛擬機器的屬性配置大部分都是依據property屬性的值,所以在系統級的優化方面可以根據自己的需求來修改配置虛擬機器的屬性,從而生成一個適合自己專案的虛擬機器。
- 在Java層的ZygoteInit類中類及資源的載入,會佔用大量的開機時間和記憶體開銷,在開機時間優化及系統記憶體優化方面可以做進一步研究。
- Java程序都是Zygote程序的子程序,這個是Java程序的一個基本概念,有助於程式開發中效能及記憶體優化等方案的討論。
- zygote程序的啟動有許多好的程式設計技法和演算法,如引數的解析、native function的註冊、JniInvocation的介面定義和實現、socket的處理、通過命令來執行特定類的方法等,值得借鑑和參考。