編輯:關於Android編程
由於Android系統是基於Linux的,所以在Android系統存在兩個不一樣的空間,Android空間(Java空間)以及Native空間。系統啟動的時候當然是Native空間,所以必須有一個進程來打開Android空間。同時,盡管Android的設計者在淡化進程的概念,強化Activity,
Service,
BroadcastReceiver等等組件的概念,但從Linux的視角來看,每一個應用都是寄生在一個進程上的,那麼創建進程也同樣需要從Native空間去創建。在Android世界中Zygote就擔任了這個角色,所以所有應用程序進程的父進程都是Zygote。Zygote的意思是受精卵,所以從名字上就能看出來它的作用。今天就來討論一下Zygote的作用。
1.Zygote的啟動
在上一篇博客介紹init進程啟動的時候講到了init.rc文件的解析,其中就有關於Zygote的啟動:
#zygote service
service zygote /system/bin/app_process -Xzygote/system/bin –zygote \
--start-system-server
socketzygote stream 666 #socket關鍵字表示OPTION
onrestart write /sys/android_power/request_state wake #onrestart也是OPTION
onrestart write /sys/power/state on
onrestart restart media
從中可以看到zygote的啟動是通過/system/bin/app_process進程啟動的。所以zygote最初的名字叫“app_process”,這個名字是在Android.mk文件中被指定的,但app_process在運行過程中,通過Linux下的pctrl系統調用將自己的名字換成了“zygote”,所以我們通過ps命令看到的進程名是“zygote”。接下來看看app_process的啟動:
<code><code><code>[-->App_main.cpp] int main(int argc, const char* const argv[]) { /* Zygote進程由init通過fork而來,我們回顧一下init.rc中設置的啟動參數: -Xzygote/system/bin --zygote --start-system-server */ mArgC= argc; mArgV= argv; mArgLen = 0; for(int i=0; i<argc; 0="" marglen="" appruntime="" int="" i="runtime.addVmArguments(argc," if="" system="" bin="" runtime.mparentdir="argv[i++];" arg="argv[i++];" true="" bool="" startsystemserver="(i" :="" ......="" pre="">
在這個函數中一共做了兩件重要的事: - 創建Android運行時環境,AppRuntime - 啟動SystemServer SystemServer是管理Android所有服務的部件,這一部分放到下一篇博客講,重點來講一下AppRumtime的創建。 從上面這幅圖可以看到,AppRuntime繼承自AndroidRumtime,所以在app_process進程中調用的runtime.start其實是調用的AndroidRumtime的start函數:
void AndroidRuntime::start(const char*className, const bool startSystemServer) { //className的值是"com.android.internal.os.ZygoteInit" //startSystemServer的值是true char*slashClassName = NULL; char*cp; JNIEnv* env; blockSigpipe();//處理SIGPIPE信號 ...... constchar* rootDir = getenv("ANDROID_ROOT"); if (rootDir == NULL) { //如果環境變量中沒有ANDROID_ROOT,則新增該變量,並設置值為“/system" rootDir = “/system"; ...... setenv("ANDROID_ROOT", rootDir, 1); } //① 創建虛擬機 if(startVm(&mJavaVM, &env) != 0) goto bail; //②注冊JNI函數 if(startReg(env) < 0) { goto bail; } jclass stringClass; jobjectArray strArray; jstring classNameStr; jstring startSystemServerStr; stringClass = env->FindClass("java/lang/String"); //創建一個有兩個元素的String數組,即Java代碼 String strArray[] = new String[2] strArray = env->NewObjectArray(2, stringClass, NULL); classNameStr = env->NewStringUTF(className); //設置第一個元素為"com.android.internal.os.ZygoteInit" env->SetObjectArrayElement(strArray, 0, classNameStr); startSystemServerStr = env->NewStringUTF(startSystemServer ? "true" : "false"); //設置第二個元素為"true",注意這兩個元素都是String類型,即字符串。 env->SetObjectArrayElement(strArray, 1, startSystemServerStr); jclass startClass; jmethodID startMeth; slashClassName = strdup(className); /* 將字符串“com.android.internal.os.ZygoteInit”中的“. ”換成“/”, 這樣就變成了“com/android/internal/os/ZygoteInit”,這個名字符合JNI規范, 我們可將其簡稱為ZygoteInit類。 */ for(cp = slashClassName; *cp != '\0'; cp++) if(*cp == '.') *cp = '/'; startClass = env->FindClass(slashClassName); ...... //找到ZygoteInit類的static main函數的jMethodId。 startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V"); ...... /* ③通過JNI調用Java函數,注意調用的函數是main,所屬的類是 com.android.internal.os.ZygoteInit,傳遞的參數是 “com.android.internal.os.ZygoteInit true”, 調用ZygoteInit的main函數後,Zygote便進入了Java世界! 也就是說,Zygote是開創Android系統中Java世界的盤古。 */ env->CallStaticVoidMethod(startClass,startMeth, strArray); //Zygote退出,在正常情況下,Zygote不需要退出。 if(mJavaVM->DetachCurrentThread() != JNI_OK) LOGW("Warning: unable to detach main thread\n"); if(mJavaVM->DestroyJavaVM() != 0) LOGW("Warning: VM did not shut down cleanly\n"); bail: free(slashClassName); }
上面這段代碼主要做了三件事:
- startVM:注冊Java虛擬機,並設置虛擬機相關參數
-
startReg:注冊JNI函數,因為後續Java世界用到的一些函數是采用native方式來實現的,所以才必須提前注冊這些函數。
- 調用ZygoteInit.main正式進入Java世界
接下來重點講一下第三點。首先來看看源碼:
[-->ZygoteInit.java]
public static void main(String argv[]) {
try {
SamplingProfilerIntegration.start();
//①注冊Zygote用的socket
registerZygoteSocket();
//②預加載類和資源
preloadClasses();
preloadResources();
......
// 強制一次垃圾收集
gc();
//我們傳入的參數滿足if分支
if (argv[1].equals("true")) {
startSystemServer();//③啟動system_server進程
}else if (!argv[1].equals("false")) {
thrownew RuntimeException(argv[0] + USAGE_STRING);
}
// ZYGOTE_FORK_MODE被定義為false,所以滿足else的條件
if(ZYGOTE_FORK_MODE) {
runForkMode();
}else {
runSelectLoopMode();//④zygote調用這個函數
}
closeServerSocket();//關閉socket
}catch (MethodAndArgsCaller caller) {
caller.run();//⑤很重要的caller run函數,以後分析
}catch (RuntimeException ex) {
closeServerSocket();
throw ex;
}
......
}
在這個函數中主要做了四件事:
- 創建IPC通信Socket
- 預加載類和資源
- 啟動SystemServer
- 監聽其他進程發出來的請求
1.1 創建IPC通信接口Socket——registerZygoteSocket
Zygote和其他進程之間的IPC並不是通過binder機制,而是通過一個localsocket來完成的,所以要首先創建一個socket:
[-->ZygoteInit.java]
private static void registerZygoteSocket() {
if(sServerSocket == null) {
intfileDesc;
try{
//從環境變量中獲取Socket的fd,還記得第3章init中介紹的zygote是如何啟動的嗎?
//這個環境變量由execv傳入。
String env = System.getenv(ANDROID_SOCKET_ENV);
fileDesc = Integer.parseInt(env);
}
try{
//創建服務端Socket,這個Socket將listen並accept Client
sServerSocket= new LocalServerSocket(createFileDescriptor(fileDesc));
}
}
}
1.2 預加載類和資源
這部分就不細講了,邏輯比較簡單,就是提前加載好一些運行時經常會用到的類和資源,提升系統運行的效率,但這同時會導致系統在啟動的時候時間過長,所以這是一個取捨的問題。
1.3 啟動SystemServer——startSystemServer
這是Zygote非常重要的一個功能,因為Android啟動的所有Service(ActivityManagerService,WindowManagerService,PackageManagerService)都是通過SystemServer來啟動的。
private static boolean startSystemServer()
throws MethodAndArgsCaller, RuntimeException {
//設置參數
String args[] = {
"--setuid=1000",//uid和gid等設置
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,
3001,3002,3003",
"--capabilities=130104352,130104352",
"--runtime-init",
"--nice-name=system_server", //進程名,叫system_server
"com.android.server.SystemServer", //啟動的類名
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
//把上面字符串數組參數轉換成Arguments對象。具體內容請讀者自行分析。
parsedArgs = new ZygoteConnection.Arguments(args);
int debugFlags = parsedArgs.debugFlags;
//fork一個子進程,看來,這個子進程就是system_server進程。
pid = Zygote.forkSystemServer(
parsedArgs.uid,parsedArgs.gid,
parsedArgs.gids,debugFlags, null);
}catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
if(pid == 0) {
//① system_server進程的工作
handleSystemServerProcess(parsedArgs);
}
//zygote返回true
return true;
}
邏輯還是比較簡單的,就是通過fork創建出SystemServer進程,具體SystemServer啟動如何工作下一篇博客會詳細介紹。
1.4 監聽其他進程發出來的請求——runSelectLoopMode
registerZygoteSocket中注冊了一個用於IPC的Socket,不過那時還沒有地方用到它。它的用途將在這個runSelectLoopMode中體現出來,請看下面的代碼:
[-->ZygoteInit.java]
private static void runSelectLoopMode()
throws MethodAndArgsCaller {
ArrayList fds = new ArrayList();
ArrayList peers = new ArrayList();
FileDescriptor[] fdArray = new FileDescriptor[4];
//sServerSocket是我們先前在registerZygoteSocket建立的Socket
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
int loopCount = GC_LOOP_COUNT;
while (true) {
int index;
try {
fdArray = fds.toArray(fdArray);
/*
selectReadable內部調用select,使用多路復用I/O模型。
當有客戶端連接或有數據時,則selectReadable就會返回。
*/
index = selectReadable(fdArray);
}
else if (index == 0) {
//如有一個客戶端連接上,請注意客戶端在Zygote的代表是ZygoteConnection
ZygoteConnection newPeer = acceptCommandPeer();
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done;
//客戶端發送了請求,peers.get返回的是ZygoteConnection
//後續處理將交給ZygoteConnection的runOnce函數完成。
done = peers.get(index).runOnce();
}
}
runSelectLoopMode比較簡單,就是:
處理客戶連接和客戶請求。其中客戶在Zygote中用ZygoteConnection對象來表示; 客戶的請求由ZygoteConnection的runOnce來處理。
2.處理請求
Zygote在啟動過程中的最後一步是進入runSelectLoopMode來監聽socket,接收請求。比如當ActivityManagerService在接收到一個打開Activity的請求,並發現該Activity所屬的應用並沒有啟動時就會創建一個進程,具體的過程可以參考我的另一篇博客:Android應用框架之應用啟動過程
XML對開發者來說十分的方便,不僅使用起來簡單,而且能夠及時調試,修改界面之後馬上能看到效果。Java設置布局不具有這個優勢。但是java卻可以動態對布局進行操作,這是x
在實現搜索功能的時候,比如藍牙搜索,附近熱點搜索等,通常我們需要一個比較友好的界面,以下通過自定義View來實現一個搜索界面。效果圖如下:當實現一個這樣的動畫的時候,思路
1、Activity簡介: Activity可以簡單理解為android手機應用程序中的每一個界面, 其有相應的實現Activity類的java類文件(相當於手機界面控
看一下Activity是怎麼通過View,Window等來用於自己的顯示的。上圖是Activity的Lifecycle。這裡只想說一下按back鍵和按home鍵退出,重新