第三天,google說,伊甸園(linux世界)要被隔離,於是便創造瞭亞當(Adm)與夏娃(Eve),稱它為zygote和system_server
–xxx
第二天,init跑完瞭,它對於android系統,最重要的,就是啟動瞭zygote和system-server,誰是Adam誰是Eve?
從分析init.rc來看
1 service zygote /system/bin/app_process -Xzygote /system/bin –zygote –start-system-server
–start-system-server隻是個參數。
分析源碼
啊,原來夏娃(zygote)用自己的肋骨(fork)創造瞭亞當(system_server)!什麼?為什麼不是亞當創造夏娃!? android的世界,是無性繁殖的世界,zygote(夏娃)一開始就是個受精卵。。。。。。。
Eve誕生記(zygote啟動過程)
第一步,受精(改名)
從init.rc可以看出,啟動的是app_process程序,啟動後,再把自己的進程名改為zygote,就算是受精瞭。。
第二步,著床(交由AppRuntime啟動javaVM)
這時,會創建一個vm,算是android java世界的祖師爺,並且在這個vm啟動時賦予各種參數,比如我們都知道默認的情況下,一個應用程序加載的內存不能超過16mb,這個參數就是在這裡設置的,heapsize為16mb。這之後vm就啟動瞭,哦,應該叫dalvik。
在啟動後,還會為java的類註冊JNI函數,android世界是c與java互相交織的。
第三步,生長臍帶(初涉java世界)
註冊好各種JNI函數後,zygote的C層面就可以調用Java代碼瞭。
這裡第一次進入瞭java世界。它調用瞭 com.android.internal.os.ZygoteInit 的main方法。
java世界並不孤立
1、它首先註冊socket,使自己成為一個服務端,也就是IPC通信服務端。這就是android的偉大之處,巧妙利用瞭linux的所有特性。以後會講到zygote的真諦。
2、然後預加載類以及一些android資源。洋洋灑灑1k多個java類要加載,並且還都是加載時間大於1250微妙的類,android框架加載耗時的類都到這裡來加載瞭,這也正是android開機慢得原因,不過苦盡甘來嗎,開始累點能幹的都幹瞭,以後用起來就方便瞭,不是麼?當然,android的系統就像是量產車,各種性能不求最好隻求最穩定,廣大發燒友改rom就像改裝汽車一樣,需要精通從齒輪組到ECU的各種知識,方能達到硬件與軟件完美的結合以便發揮出最大功效,扯多瞭。。要想定制rom,減少開機時間,還得靠nb的水平與良好的洞察力。
3、啟動SystemServer。也就是system_server進程。
剛才說瞭,這個system_server就是Adm,夏娃的第一塊肉就這麼掉下來瞭,同樣利用linux的frok機制,從zygote進程分裂出瞭一個system_server進程。男女搭配幹活不累,亞當與夏娃共同勞動,為我們搭建美好的android世界。
後面會分析system_server都幹些什麼事。
4、建一個線程,轉入socket偵聽模式。每個apk在android中運行起來都是一個單獨的linux進程,這些進程,就是zygote分裂出來的。現在zygote偵聽的就是ActivityService通過system_server使用socket傳入的請求,用以分裂進程。線程之外的最後,關閉之前打開的socket偵聽。
現在夏娃該幹的事,基本上幹完瞭,就等著亞當再次讓她受精瞭。。。。。 -_-!!!!
這裡就順便描述下每個Activity分娩出來的過程吧!(Activity大致啟動過程)
step 1 凡人向神求仔:
啟動一個activity,也就是startActivity。這個activity依附於一個未啟動的進程,好比剛開機,打開一個android程序。
step 2 大神收到祈願:
ActvityManagerService收到startActvity,梳理一番各種參數,比如apk的報名,再將這個祈願通過送到伊甸園交由夏娃實施。
step3 伊甸園接到生仔請求:
亞當願意別人直接找夏娃麼?呵呵,各種service都是SystemServer啟動起來的,而SystemServer又掌管著Binder,自己肯定會首先處理這個通知的。SystemServer通過socket這個IPC機制,向zygote發送一個frok請求,這時從java世界回到瞭native世界,亞當(system_server)讓夏娃(zygote)受精瞭。。。。-_-!!! android的繁榮離不開這種造仔活動
step4 夏娃生仔:
又要分裂瞭,fork出瞭一個新進程,並把這個進程返還給Java世界,並且由ActivityManagerService管理它。這裡,就是讓這個進程調用ActivityThread,ActvityThread就是main,apk程序的main。
linuix中fork出來的子進程會繼承父進程的所有信息,就相當於一個拷貝,隻不過變成瞭另一個進程。既然繼承瞭所有信息,那麼dalvik也就繼承下來瞭,這就解釋瞭為什麼一個android程序都有一個vm進程。
Adam誕生記(SystemServer啟動過程)
上面提到zygote在java層啟動並fork瞭SystemServer,也就是夏娃身上掉下來的第一塊肉。
SystemServer首先會關閉因fork而從父進程繼承而來的socket。
第一步,Native層初始化
這裡會通過JNI調用native方法,又回到瞭Native世界。
首先,初始化Skia引擎,就是一個圖像引擎,封裝瞭畫圖的各種操作,這樣屏幕就能讓顯示東西瞭。
然後,啟動Binder,也就是android IPC的核心。
第二步,換名,並進行JAVA層初始化
換名瞭,這個vm進程就被正式命名為system_server瞭。
java層初始化,也就是調用com.android.server.SystemServer 的main。有趣的是,這裡通過反射,獲得SystemServer類的main方法後,不直接調用,而是拋出一個異常,這個異常包含瞭main這個Method。
再回頭看看zygote的java世界,啟動瞭system_server的代碼是在try中,catach中就是截獲上面所說的異常,並執行main的這個Method。
為什麼這麼做?
先來看個示例程序:
01 public class Method_test {
02
03 public static void main(String[] args) {
04 ClassA ca=new ClassA();
05 ca.start();
06 }
07
08 }
09
10 class ClassA{
11
12 public ClassA() {
13 }
14 public void start(){
15 System.out.println("開始調用方法");
16 Method_A();
17 }
18
19 public void Method_A(){
20 System.out.println("方法A被調用");
21 Method_B();
22
23 }
24 public void Method_B(){
25 System.out.println("方法B被調用");
26 Method_C();
27 }
28 public void Method_C(){
29 System.out.println("方法C被調用");
30 Method_final();
31 }
32
33 public void Method_final(){
34 System.out.println("最終方法被調用");
35
36 }
37 }
上面這段代碼模擬一個一個場景,方法A調用B,B又調用瞭C。。。 A()->B()->C()->final(),final裡面可能又有更復雜的內容,而且final之後基本上不做什麼瞭。真實情況下,A()與final()之間還有更多的調用層次。
上面的調用棧情況是這樣的:
這樣的話,在一些資源稀缺的平臺上,,,,的確不怎麼好,我們希望執行final的時候,前面調用的層次能消失,以前用過的變量什麼也消失,還我們一個清凈的世界,而且執行完瞭也不用再層層跳出瞭。
這時,try catch就有大作用瞭,看代碼:
01 public class Method_test {
02
03 public static void main(String[] args) {
04 ClassA ca=new ClassA();
05 ca.start();
06 }
07
08 }
09
10 class ClassA{
11
12 public ClassA() {
13 }
14 public void start(){
15 System.out.println("開始調用方法");
16 try{
17 Method_A();
18 }catch (RuntimeException e) {
19 //捕獲異常後再執行目標方法
20 Method_final();
21 }
22 }
23
24 public void Method_A(){
25 System.out.println("方法A被調用");
26 Method_B();
27
28 }
29 public void Method_B(){
30 System.out.println("方法B被調用");
31 Method_C();
32 }
33 public void Method_C(){
34 System.out.println("方法C被調用");
35 //這裡拋出一個異常
36 throw new RuntimeException();
37 }
38
39 public void Method_final(){
40 System.out.println("最終方法被調用");
41
42 }
43 }
與之前相比,把final()的調用換到瞭start()裡,直接讓c()拋出個異常,catch中執行final()。
調用棧如下:
Android的設計思想真猛啊,通過這麼分析,又學到一個技巧。。。。
SystemService->main
1、加載native庫並調用init1()
這裡創建瞭幾個native服務,ServiceManger、SurfaceFlinger等,並把當前線程加入到Binder的通信大軍中。
2、通過JNI調用JAVA世界的init2()
在上面的init1()中,最後會調用java世界的init2()。
這裡就是啟動各種服務瞭,什麼EntropyService、PowerManagerService、BatteryService、WindowManager等等。
還會把地獄犬召喚出來,也就是WatchDog(看門狗),來時刻盯著一些重要的Service,防止他們墮落。看門狗的職責就是盯著一些重要的service,萬一他們掛瞭,就把亞當殺死,然後讓init把它再原地滿血復活。
最後,就進入瞭消息循環,負責android世界中跨線程訪問的調度,google常曰 子線程要通過handler來更新UI線程,那麼handler中發送的消息就是靠這裡來分發的。
這裡嚴重推薦 鄧凡平 前輩所著的《深入理解Anroid 卷I》,我自己感覺這是我見過的涉及框架的最好的一本書,主要就是通俗易懂啊(其實還是自己的水平有限)
水平有限,錯誤之處請指正,多謝!
作者 黑暗伯爵