1. 程式人生 > >(轉)那兩年煉就的Android內功修養

(轉)那兩年煉就的Android內功修養

http://blog.csdn.net/luoshengyang/article/details/8923485

  經過兩年的時間,終於完成對Android系統的研究了。Android是一個博大精深的系統,老羅不敢說自己精通了(事實上最討厭的就是說自己精通神馬神馬的了,或者說企業說要招聘精通神馬神馬的人才),但是至少可以說打通了整個Android系統,從最上面的應用層,一直到最下面的Linux核心,煉就的是一種內功修養。這篇文章和大家一起分享這兩年研究Android系統的歷程,以此感謝大家一直以來的支援和鼓勵。        以下是本文的提綱:

        1. 理念

        2. 里程碑

        3. 看過的書

        4. 研究過的內容

        5. 將來要做的事情

        它們涵蓋了老羅這兩年一直想要和大家分享的內容。好了,不說廢話了,直入主題。

        一. 理念

        這裡說的理念是說應該帶什麼樣的心態去研究一個系統。古人說書中自的顏如玉,書中自有黃金屋,我想說程式碼裡也有顏如玉和黃金屋,所以老羅希望大家都能“Read The Fucking Source Code”。再者,對於優秀的開源專案來說,不去讀一下它的原始碼,簡直就是暴殄天物啊。那麼,讀程式碼有什麼好處呢?太多了,除了可以學到別人的優秀程式碼、架構之外,最重要的是,我們能從中找到答案,從而可以解決自己專案上的燃眉之急。

        我們在專案中碰到問題的時候,通常第一反應都是到網上去搜索答案。但是有時候有些問題,網路並不能給出滿意的答案。這時候就千萬不要忘了你所擁有的一個大招——從程式碼中找答案!當然,從程式碼中找答案說起來是輕鬆,但是等到直正去找時,可能就會發現雲裡霧裡,根本不知道那些程式碼在說什麼東東,甚至連自己想要看的原始碼檔案都不知道在哪裡。這就要求平時就要養成讀程式碼的習慣,不要臨時抱佛腳。有時候臨時抱佛腳是能解決問題,但是千萬不能抱著這種僥倖心裡,掌握一門技術還是需要踏踏實實地一步一步走。

        胡克其實在牛頓之前,就發現了萬有引力定律,並且推匯出了正確的公式。但由於數學不好,他只能勉強解釋行星繞日的圓周運動,而沒有認識到支配天體執行的力量是“萬有”的。後來數學狂人牛頓用微積分圓滿地解決了胡克的問題,並且把他提出的力學三條基本定律推廣到了星系空間,改變了自從亞里士多德以來公認的天地不一的舊觀點,被科學界奉為偉大的發現。胡克大怒,指責牛頓剽竊了他的成果。牛頓尖酸刻薄的回敬:是啊,我他媽還真是站在巨人的肩膀上呢!

        我們有理由相信像牛頓、喬布斯之類的狂人,不用站在巨人的肩膀上也能取得矚目的成就。但是,我們不是牛頓,也不是喬布斯,所以在看程式碼之前,還是找一些前人總結的資料來看看吧。拿Android系統來說,你在至少得懂點Linux核心基礎吧!所以在看Android原始碼之前,先找些Linux核心的經典書籍來看看吧,騷年!後面老羅會推薦一些書籍給大家。

        另外,我們知道,現在的網際網路產品,講究的是快速迭代。Android系統自第一個版本釋出以來,到現在已經經歷了很多版本呢?那麼我們應該如何去選擇版本來閱讀呢?一般來說,就是選擇最新的版本來閱讀了。不過隨著又有新版本的原始碼的釋出,我們所看的原始碼就會變成舊版本。這時候心裡就會比較糾結:是應該繼續看舊的程式碼,還是去追新版本的程式碼呢?就當是看連續劇,一下子跳到前面去,可能就不知道講什麼了。其實版本就算更新得再快,基礎的東西也是不會輕易變化的。我們看程式碼時,要抱著的一個目的就是弄懂它的骨架和脈絡。畢竟對於一個系統來說,它是有很多細節的,我們無法在短時間把它們都完全吃透。但是主要我們掌握了它的骨架和脈絡,以後無論是要了解它的什麼細節,都可以很輕輕地找到相關的原始檔,並且可以很容易進入主題。

        坦白說,對於Android系統,很多細節我也不瞭解。所以有時候你們可以看到,在部落格文章後面的評論上,有些同學問的一些比較具體的問題,我是沒有回覆的。一來是我不懂,二來是我也沒有時間去幫這些同學去扒程式碼來看。這也是在文章一開頭,我就說自己沒有精通Android系統的原因。但是請相信,主要你熟悉Android系統的程式碼,並且有出現問題的現場,順藤摸瓜跟著程式碼走下去,並且多一點耐心和細心,是可以解決問題的!

        關於Android版本的問題,相信大家都知道我現在的文章都是基於2.3來寫的。很多同學都說我out了,現在都4.2了,甚至4.3或者5.0都要出來了,還在看2.3。我想說的是,主要你掌握了它的骨架和脈絡,無論版本上怎麼變化,原理都是一樣的,這就是以不變應萬變之道。因此,我就一直堅持研究2.3,這樣可以使得前前後後研究的東西更連貫一致,避免分散了自己的精力。如果還有疑問的話,後面我講到Android的UI架構時,就會簡單對比一下4.2和2.3的不同,其實就會發現,基本原理還是一樣的!

        說到Android系統的骨架和脈絡,也有同學抱怨我的文章裡面太多程式碼細節了,他們希望我可以抽象一下,用高度概括的語言或者影象來勾勒出每一個模組的輪廓。我想說的是,如果你不看程式碼,不瞭解細節,即使我能夠用概括的語言或者影象來勾勒出這樣的輪廓出來,也許這個輪廓只有我才能看得懂。

        我在真正開始看Android系統的原始碼之前,也是有這樣的想法,希望能有一張圖來清楚地告訴我Android系統的輪廓,例如,HAL為什麼要將驅動劃分成使用者空間和核心空間兩部分,為什麼說Binder是所有IPC機制效率最高的。我確實是從網上得到抽象的資料來解釋這兩個問題,但是這些資料對我來說,還是太抽象了,以至於我有似懂非懂的感覺,實際上就是不懂!就是因為這樣,激發了我要從程式碼中找答案的念頭!現在當我回過頭來這些所謂抽象的輪廓時,我就清楚地知道它講的是什麼了。

        所以古人云“天將降大任於斯人也,必先苦其心志,勞其筋骨,餓其體膚”是有道理的,因為只有親身經歷過一些磨難後得到的東西才是真實的!

        好了,關於理念的問題,就完了,這裡再做一下總結:

        1. 從程式碼中找答案——Read The Fucking Source Code。

        2. 以不變應萬變——堅持看一個版本的程式碼直至理清它的骨架和脈絡。

        二. 里程碑

        研究Android 2.3期間,主要是經歷了以下五個時間點,如圖1所示:


圖1 研究Android 2.3的里程碑

         從2011年06月21日第一篇部落格文章開始,到2013年06月03日結束對Android 2.3的研究,一共是差不多兩年的時間,一個從無到有的過程。其中,最痛苦的莫過於是2011年12月下旬到2012年06月12日這6個多月的時間裡面,整理了2011年12月下旬前的所有部落格文章,形成了《Android系統原始碼情景分析》一書,並且最終在2012年10月下旬正式上市。

        總的來說,就是在兩年的時間裡面,獲得了以下的兩個產出: 

        1. 《老羅的Android之旅》部落格專欄93篇文章,1857224次訪問,4156條評論,13440積分,排名154。

        2. 《Android系統原始碼情景分析》一書3大篇16章,830頁,1570000字。

        以上產出除了能幫助到廣大的網友之外,也讓自己理清了Android系統的骨架和脈絡。這些骨架和脈絡接下來再總結。2013年06月03日之後,將何去何從?接下來老羅也會簡單說明。

        三. 看過的書 

        在2011年06月21日開始寫部落格之前,其實已經看過不少的書。在2011年06月21日之後,也一邊寫部落格一邊看過不少的書。這個書單很長,下面我主要分類列出一些主要的和經典的。

        語言:

        《深度探索C++物件模型》,對應的英文版是《Inside C+++ Object Model》

        程式編譯、連結、載入:

        《連結器和載入器》,對應的英文版是《Linker and Loader》

        《程式設計師的自我修養:連結、裝載和庫》

        作業系統:

        《Linux核心設計與實現》,對應的英文版是《Linux Kernel Development》

        《深入理解Linux核心》,對應的英文版是《Understanding the Linux Kernel》

        《深入Linux核心架構》,對應的英文版是《Professional Linux Kernel Architecture》

        《Linux核心原始碼情景分析》

         網路:

        《Linux網路體系結構:Linux核心中網路協議的設計與實現》,對應的英文版是《The Linux Networking Architecture: Design and Implementation of Network Protocols in the Linux Kernel》

        《深入理解LINUX網路技術內幕》,對應的英文版是《 Understanding Linux Network Internals》

        裝置驅動:

        《Linux裝置驅動程式》,對應的英文版是《Linux Device Drivers》

        《精通Linux裝置驅動程式開發》,對應的英文版是《Essential Linux Device Drivers》

        虛擬機器:

        《Java SE 7虛擬機器規範》

        《深入Java虛擬機器》,對應的英文版是《Inside the Java Virtual Machine》

        《Oracle JRockit: The Definitive Guide》

        嵌入式:

        《嵌入式Linux開發》,對應的英文版是《Embedded Linux Primer》

        《構建嵌入式Linux系統》,對應的英文版是《Building Embedded Linux Systems》

        ARM體系架構:

        《ARM嵌入式系統開發:軟體設計與優化》,對應的英文版是《ARM System Developer's Guide: Designing and Optimizing System Software》

        綜合:

       《深入理解計算機系統》,對應的英文版是《Computer Systems: A Programmer's Perspective》

        上面介紹的這些書,都是屬於進階級別的,所以要求要有一定的語言基礎以及作業系統基礎。此外,對於看書,老羅有一些觀點,供大家參考:

        1. 書不是要用的時候才去看的,要養成經常看書、終身學習的習慣。

        2. 不要只看與目前自己工作相關的書,IT技術日新月異,三五年河東,三五年河西。

        3. 書看得多了,就會越看越快,學習新的東西時也越容易進入狀態。

        對於Android應用開發,力推官方文件:

        四. 研究過的內容

        整個部落格的內容看似鬆散,實際上都是有組織有計劃的,目標是打通整個Android系統,從最上面的應用層,到最下面的Linux核心層。簡單來說,部落格的所有文章可以劃分為“三橫三縱”,如圖2所示:


圖2 Android系統研究之“三橫三縱”

        接下來,老羅就分別描述這三條橫線和縱線,並且給出對應的部落格文章連結。

        1. 準備 -- Preparation -- 橫線

        主要就是:

       (1)通過閱讀相關的書籍來了解Linux核心和Android應用基礎知識

       (2)搭建好Android原始碼環境

       (3)Android系統有很多C++程式碼,這些C++程式碼用到了很多智慧指標,因此有必要了解一下Android系統在C/C++ Runtime Framework中提供的智慧指標

         2. 專用驅動 -- Proprietary Drivers -- 橫線

         這些專用驅動就是指Logger、Binder和Ashmem,它們整個Android系統的基石:

        (1)Logger

        (2)Binder

        (3)Ashmem

        3. 硬體抽象層 -- HAL -- 縱線

        硬體抽層象最適合用作Android系統的學習入口,它從下到上涉及到了Android系統的各個層次:

        4. 應用程式元件 -- Application Component -- 縱線

        應用程式元件是Android系統的核心,為開發者提供了貼心的服務。應用程式元件有四種,分別是Activity、Service、Broadcast Receiver和Content Provider。圍繞應用程式元件,又有應用程式程序、訊息迴圈和安裝三個相關模組。

       (1)Activity

       (2)Service

       (3)Broadcast Receiver

       (4)Content Provider

       (5)程序

       (6)訊息迴圈

       (7)安裝

        5. 使用者介面架構 -- UI -- 縱線

        大家對老羅現在還在寫Android 2.3的UI架構意見最大,認為已經過時了。老羅認為持有這種觀點的人,都是沒有經過認真思考的。老羅承認,從Android 4.0開始,UI部分發生了比較大的變化。但是請注意,這些變化都是在Android  2.3的UI架構基礎之上進行的,也就是說,Android  2.3的UI架構並沒有過時。你不能說Android 4.0在Android  2.3之上增加了一些feature,就說Android  2.3過時了。

        下面這張是從Android官網拿過來的最新UI渲染流程圖,也就是4.2的UI渲染流程圖


圖2 Android 4.2的UI渲染流程

        從這張圖可以看出關於Android的UI架構的三條主線:

      (1)每一個Window的Surface都怎樣渲染的?不管怎麼樣,最後渲染出來的都是一個Buffer,交給SurfaceFlinger合成到Display上。

      (2)SurfaceFlinger是怎樣合成每一個Window的Surface的?

      (3)WindowManamgerService是怎麼樣管理Window的? 

        第(1)和第(2)兩個點在2.3和4.2之間有變化,主要是因為增加了GPU的支援,具體就表現為Window的Surface在渲染的時候使用了GPU,而SurfaceFlinger在合成每一個Window的Surface的時候,也使用了GPU或者Overlay和Blitter這些硬體加速,但是主體流程都沒有變,也就是說,Window的Surface渲染好之後,最終依然是交給SurfaceFlinger來合成。此外,雖然我還沒有開始看4.2的程式碼,但是可以看得出,4.2裡面的HWComposer,只不過是封裝和抽象了2.3就有的Overlay和Blitter,而SurfaceTexture的作用與2.3的SurfaceComposerClient、SurfaceControl也是類似的。第(3)點基本上就沒有什麼變化,除非以後要支援多視窗。

        通過上述對比,只想強調一點:Android 2.3的UI架構並沒有過時,是值得去研究的,並且在2.3的基礎上去研究4.2的UI架構,會更有幫助。

        仁者見仁,智者見智,Android 2.3的UI架構的說明就到此為止,接下來它的分析路線,都是圍繞上述三個點來進行的。

        首先是以開機動畫為切入點,瞭解Linux核心裡面的驅動:

        FB驅動抽象了顯示卡,上面的使用者空間程式就是通過它來顯示UI的。

        HAL層的Gralloc模組對FB驅動進行了封裝,以方便SurfaceFlinger對它進行訪問:

        SurfaceFlinger負責合成各個應用程式視窗的UI,也就是將各個視窗的UI合成,並且通過FB顯示在螢幕上。在對SurfaceFlinger進行分析之前,我們首先了解應用程式是如何使用的它的:

        萬事俱備,可以開始分析SurfaceFlinger了:

        SurfaceFlinger操作的物件是應用程式視窗,因此,我們要掌握應用程式視窗的組成:

        應用程式視窗是由WindowManagerService進行管理的,並且也是WindowManagerService負責提供視窗資訊給SurfaceFlinger的,因此,我們最後分析WindowManagerService:

        上述內容都研究清楚之後,Android系統的UI架構的骨架就清晰了。但是前面所研究的應用程式視窗還是太抽象了,我們有必要研究一下那些組成應用程式視窗內容的UI控制元件是怎麼實現的,以TextView和SurfaceView為代表:

        最後,分析Android系統的UI架構,怎能不提它的資源管理框架?它有效地分離了程式碼和UI:

        分析這裡,Android系統的UI架構就分析完成了,看出什麼門道來沒有?是的,我們以開機動畫為切入點,從Linux核心空間的FB驅動,一直分析到使用者空間中HAL層模組Gralloc、C/C++ Runtime Framework層的SurfaceFlinger、Java Runtime Framework層的WindowMangerService、Window、Widget,以及資源管理框架,從下到上,披荊斬棘。

        6. Dalvik虛擬機器 -- 橫線

        Android系統的應用程式及部分應用程式框架是使用Java語言開發的,它們執行在Dalvik虛擬機器之上,還有另外一部分應用唾棄框架在使用C/C++語言開發的。使用Java語言開發的應用程式框架老羅稱之為Java Runtime Framework,而使用C/C++語言開發的應用程式框架老羅稱之為C/C++ Runtime Framework,它們被Dalvik虛擬機器一分為二。通過前面的學習,其實我們都已經瞭解Android系統的Java Runtime Framework和C/C++ Runtime Framework,因此,我們最後將注意力集中在Dalvik虛擬機器上:

        學習完成“三橫三縱”這六條主線之後,我們就可以自豪地說,從上到下地把Android系統打通,並且將它的骨架和脈絡也理清了!

        對於“準備”、“專用驅動”、“HAL”、“應用程式元件”這四條主線,老羅極力推薦大家看《Android系統原始碼情況分析》一書,內容比部落格文章要系統、詳細很多,不說其它的,就單單是講Binder程序間通訊機制的那一章,就物超所值。在《Android系統原始碼情景分析》一書中,老羅最引以為豪的就是講Binder程序間通訊機制的第5章,網上或者其它書上絕對是找不到這麼詳盡的分析資料。

        五. 將來要做的事情

        接下來要做的主要是三件事情:

        1. 繼續研究Android系統

        本來以為前段時間的Google I/O會發布Android 4.3或者5.0,然後老羅就以最新發布的版本為藍本來進行研究。既然沒有釋出新版本,那麼就只有以現在的最新發布版本4.2為藍本進行研究了。如前所述,4.2與2.3相比,最大的不同之處是預設增加了GPU支援,因此,老羅在接下來的一段時間裡,將著重研究4.2的GPU支援。

        2. 停止部落格更新

        這兩年投入在部落格上的精力太多了,部落格上的文章基本上熬夜熬出來的。大多數時候,一個話題要花一個星期左右的時間來看程式碼,然後再花四個星期左右的時間將文章寫出來。本來是這樣計劃的,依靠《Android系統原始碼情景分析》一書的銷量,可以在經濟上得到一定的回報,然後可以繼續在部落格上投入,直至把4.x版本的GPU支援寫完,最後再整理出一本關於Android系統UI架構的書。但是最近詢問了一下書的銷量,差強人意,達不到預期目標。由於沒有形成良性迴圈,因此沒有辦法,只好停止部落格更新。老羅需要把精力投入在其它事情上,希望大家諒解!

        3. 仍然會持續地進行一些小分享

        主要是一些隨筆分享,這些分享主要會發布在微博或者QQ群上面,那裡也方便一些和大家進行討論。此外,老羅也樂意和大家進行一些線下分享,主要針對企業或者單位組織的沙龍、活動和會議等,同時也可以單獨地針對企業內部進行分享。不過前提當然是舉辦方對《老羅的Android之旅》專欄或者《Android系統原始碼情景分析》一書的內容感興趣,並且邀請老羅去參加。

        如果需要邀請老羅去參加分享,可以通過微博或者郵箱和老羅聯絡,非常感謝大家兩年以來的支援!

        郵箱:[email protected]