閱讀Android源碼的一些姿勢
來自: https://segmentfault.com/a/1190000004426348
前面吐槽了 有沒有必要閱讀Android源碼 ,后面覺得只吐槽不太好,還是應該多少弄點干貨。
日常開發中怎么閱讀源碼
找到正確的源碼
IDE是日常經常用的東西,Eclipse就不說了,直接從Android Studio(基于IntelliJ Community版本改造)開始。
我們平時的Android項目,都是要依賴Android SDK里對應API Level的android.jar包(而且是以Provided的形式依賴),這樣才能使用Android提供的API。在IntelliJ中,當想要看具體類的源碼的時候,如果Android SDK里對應API Level的Source包有下載的話,IDE會打開對應的Source包;如果還沒有下載,IDE會把對應API Level的android.jar包反編譯成Java代碼,這個規則對于一些第三方的開源項目也一樣。推薦下載Source源碼,畢竟反編譯的Java代碼不可能完全和源碼的時候一樣,有時候反編譯出來的代碼的執行邏輯可能完全等價,但是可閱讀性下降了不好,而且也少了一些重要的注釋。
定位具體源碼的時候,可以通過“Ctrl+鼠標左鍵”來查看,也可以通過“雙擊Shift”,在查找框里輸入目標類的名字來定位。
如上圖,第一個類就是API23的NinePatchDrawable的源碼,第二個就是通過android.jar反編譯而來的,這里記得把“Include non-project items”勾上。
關于SDK自帶的源碼和隱藏API
Android SDK自帶的Source源碼包很小,并沒有包括所有的Android Framework的源碼,僅僅提供給應用開發參考用,一些比較少用的系統類的源碼并沒有給出,所以有時候你會看到如下。
public class BaseDexClassLoader extends ClassLoader { public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) { throw new RuntimeException("Stub!"); }protected Class<?> findClass(String name) throws ClassNotFoundException { throw new RuntimeException("Stub!"); } protected URL findResource(String name) { throw new RuntimeException("Stub!"); } ...
}</pre>
“RuntimeException("Stub!")”表示實際運行時的邏輯會由Android ROM里面相同的類代替執行。
此外,在IDE里看源碼的時候,有時候一些方法或者類會出現報紅(找不到)的情況,如下。
![]()
這是因為這些方法或者類是被Android SDK隱藏的,出于安全或者某些原因,這些API不能暴露給應用層的開發者,所以編譯完成的android.jar包里會把這些API隱藏掉,而我們的Android項目是依賴android.jar的,查看源碼的時候,IDE會自動去android.jar找對應的API,自然會找不到。當然,這些API在ROM中是實際存在的,有些開發者發現了一些可以修改系統行為的隱藏API,在應用層通過反射的方式強行調用這些API執行系統功能,這種手段也是一種HACK。
Google的AOSP項目
當你需要的源碼在Android SDK Source中找不到的時候,就有必要去 AOSP (Android Open Source Project)項目里面找了。
不過AOSP項目包括整個Android所有開源的東西,實在是太龐大了,對于一般開發者來說,我們只需要接觸Framework層次的東西就夠了,這里包括了base、build-tools、support包甚至Volley項目的源碼。
![]()
以base為例,進入base目錄,能看到base項目的git倉庫,左邊是其所有的分支。
![]()
進入“master/core/java/android/”路徑就能看到熟悉的Package目錄了,其他分支以及項目都類似。有必要的時候可以把整個AOSP項目Clone下來,大概20G左右,可以把項目導入到IDE里面,這樣就更方便查看源碼了,另外可以可是試著編譯自己的Android ROM,只需要部署能夠跑MakeFile命令的環境就好。
一些輔助閱讀的工具
Chrome擴展
Android SDK Search
![]()
相信平時到 Android開發者官網 查看API說明的人不少,這個擴展可以在API類名旁邊顯示一個跳轉鏈接,用于跳轉到AOSP中對應的類的源碼,方便查看源碼。
Source Insight
在這個工具幫助下,你才可以駕馭巨大數量的Android 源碼,你可以從容在Java,C++,C代碼間遨游,你可以很快找到你需要的繼承和調用關系。
V*N梯子
善用梯子是開發者基本的自我修養之一,如果你不想做“面向百度編程”的話。
我在一開始是使用免費的GoAgent和WallProxy,但是經常要更新,而且最近還要經常替換IP才能工作,所以后來我換成了收費的ShadowSocks,各個平臺都有客戶端,非常方便,我也在手機上常態用起了Google的全家桶,只不過最近老是更換服務器地址,而且部分服務器不穩定,非常擔心商家是否準備撈一筆然后跑路,23333。
我覺得比起折騰找免費的低速、不穩定的梯子,還是用一些穩定的收費的的梯子比較劃算。至于具體的用梯子的姿勢,請諸位自行搜索,這里隨便貼個介紹梯子使用總結。
推薦閱讀的源碼
AOSP項目這么龐大,就算是Framework部分也有夠看上一陣子的,所以推薦從常用的看起,由淺及深,同時向橫向和縱向深入閱讀。
開始
Handler-Message-Looper
Handler被稱為“異步提交器”,是Android開發入門教程必定談及的東西,這也是Activity等組件的工作機制需要用到的東西,是“數據驅動”框架的重要組成,作為閱讀源碼的入門最適合不過。
Activity和Service
作為經常使用到的組件,閱讀其源碼的花費和帶來的技術提高的性價比肯定是最高的,Service可以不看,但是Activity總不能少吧。
Fragment
還在認為Fragment是一個視圖嗎,還在認為FragmentActivity的界面有多個Fragment組成嗎,看看Fragment和FragmentManager吧,了解下生命周期的本質到底是什么。
View
想自定義高級的View類嗎,那總得知道onMeasure/onLayout/onDraw這些方法是怎么被調用的,了解LayoutParams是怎么工作的,知道調用requestLayout和Invalidate的時候有什么區別。
MotionEvent
在懂的怎么自定義高級的View后,只能向用戶顯示界面,還得知道怎么與用戶交互才能做出華麗的UI。所以必須知道TouchEvent的分發和攔截的工作機制,起碼也得知道其特點,才不會一直在困擾“為什么無法監聽用戶的觸摸事件”、“View之間的觸摸事件沖突了”或者“View的滑動與點擊事件沖突了”之類的問題。
LayoutInflator
布局渲染器也是開發Android UI的時候經常用到的,不過LayoutInflator實例的創建方式有好幾種,你至少得知道其之間的區別。還有,LayoutInflator在渲染指定布局的時候,有container和attachToRoot等參數,閱讀源碼后很快能了解其區別。
SurfaceView和TextureView
閱讀完View的工作機制后,就能理解為什么View在繪制復雜的UI效果時效率這么低,這時候就需要SurfaceView和TextureView了。理解雙緩沖對UI更新效率的幫助,了解SurfaceView在視圖疊加的時候的缺陷,了解TextureView在Android Lollipop之前的內容竄臺BUG,才能用正確姿勢使用這倆。
AsyncTask
異步任務也是Android開發經常遇到的問題,相比自己從Thread和Handler寫起,被稱為“異步任務大師”的AsyncTask類自然更受到許多小伙伴的喜歡。不過AsyncTask在早期的Android版本中差別甚大,需要做大量的適配工作,而且特別容易引起異步任務引用著組件的實例導致內存泄露從而引發OOM問題,所以不推薦直接使用AsyncTask類,不過強烈推薦閱讀AsyncTask的源碼學習Google優秀的異步任務設計理念。此外,如果真的要使用AsyncTask,不要直接使用系統提供的AsyncTask類,AsyncTask本身就是一個單一的Java類,沒有耦合其他系統類,推薦自己從最新的Android版本中復制一份AsyncTask類的代碼,自己維護,在項目中當做Support包一樣使用,以規避其兼容性問題。
Volley
這個強烈推薦,是Google官方的異步任務框架,沒有隨Android發布,需要自己在Framework里下載代碼。Volley的中文意思就是“并發”,閱讀其源碼能讓你見識到原來異步任務框架也能寫得這么低耦合和高擴擴展,其用“生產者-消費者”模式來處理異步請求的框架會讓人拍案叫絕。此外,Volley框架是用于處理Http任務和Image加載任務,但是其優秀的異步控制思想也能運用與File、Sqlite等耗時任務的處理,當你能夠自己寫出類似Volley框架的代碼時,說明你的Android技術已經有所突破。
android.util.*
“android.util.*” 包名下有許多優秀的實用類,大多是作為Java自帶類的補充,比如數據結構類的SparseArray、ArrayMap、ArraySet,用于加密的Base64,用于處理屏幕分辨率自適應的DisplayMetrics和TypedValue,用于時間換算的TimeUtils,以及用于內存緩存的LruCache,熟悉這些類對Android開發非常有幫助,也會讓代碼顯得成熟。
進階
Context
閱讀Context源碼能幫助我們了解其工作機制,了解Google是怎么在Java代碼上添加Android特性的,了解Android是怎么保存和獲取res資源的,了解ContextWrapper和Activity這些Context有什么區別,了解Context設計的裝飾者模式(Description Pattern)。
ClassLoader
類加載器ClassLoader是Android虛擬機工作的基礎,了解其“雙親代理模式”能讓你更好的了解系統的類和你寫的類是怎么工作的。Multi-Dex和ART模式也和ClassLoader的工作機制息息相關。
Binder
Binder是Android上RPC(Remote Procedure Call Protocol)的實現,Android系統許多功能就是居于Binder實現的,平時應用層對Binder的使用大多是在于和Service通訊的時候,不過,當我們需要使用AIDL功能的時候,就需要接觸到Binder了。(推薦閱讀原理即可,反正C++驅動層我是看不下去了)
WMS,AMS,PMS,NMS,IMS等系統Service
SystemServer是Android的Framework層工作的核心,Android系統啟動過程包含從Linux內核加載到Home應用程序啟動的整個過程。SystemServer是Zygnote孵化的第一個進程,這個進程會啟動許多Framework層功能需要用到的線程,比如用于管理窗口的WindowManagerService,用于管理Activity的ActivityManagerService,用于管理APK包信息的PackageManagerService,用于管理網絡的NetworkManager,用于處理用戶觸摸的InputManagerService等,這些系統Service提供了APP運行時需要的大多系統功能,大多使用“stub-server”的模式進行交互,而且有大量的JNI的調用。這部分的源碼比較適合從事ROM開發的人閱讀,應用層的開發基本不會用到,但是這方面的只是能讓我們對Android Framework層的工作機制有個大抵的認識。(非常慚愧,這部分我自己看了幾次,還是沒能產生融會貫通的感覺,整體的認識還是比較模糊,希望繼續跟著老羅的博客,撿撿肉吃)
第三方開源項目
EventBus
Android上的一個“訂閱者-發布者”模式的實現框架,非常適合業務多而且經常變動的項目,能夠有效預防“接口爆炸”,現在基本上中型以上的項目都會采用類似的框架。
OTTO
同上,只不過實現的具體方案不一樣,而且OTTO相比EventBus來,比較小巧,代碼也比較簡練,非常適合處女座的開發者食用。
RxJava
相比起上面兩個,RxJava可以說是把異步的思想發揮到了極致,RxJava的興起代表了Android開發中響應式編程的崛起,同樣非常適合業務多而且經常變動的項目,只不過相比傳統的基于接口的開發方式,RxJava框架的開發方式會有點難以適應,特別是團隊開發的時候。
Guava
這個其實也是Google自己開源的,提供了許多優秀的Java工具類,比如“one to one mapping”的Bimap,有時候一些工具類Android或Java自帶的庫沒有提供,或許我們可以先參考Guava的。
以上是我自己個人推薦閱讀的源碼,不過每個開發者自身的興趣和側重點都不一樣,有興趣的參考著看就是。同時,如果有一些有趣的系統類,隨時歡迎推薦給我。
站在巨人的肩膀上閱讀
學習一個系統最好的方法就是“Read The Fucking Source Code”,壞消息是AOSP項目是在太龐大太難消化了,好消息就是現在已經有不少先驅,我們或許可以站在他們的肩膀上閱讀。
項目介紹, 代碼下載, 環境搭建, 刷機方法, Eclipse配置都在這里,這是一切的基礎。
Android官方Issue列表,記錄一些系統BUG,別掉坑里了。
此老羅非彼老羅,羅升陽老師的博客非常有營養,基本可以作為指引你開始閱讀AOSP源碼的教程。你可以按照博客的時間順序一篇篇挑需要的看。但這個系列的博客有些問題:早期的博客是基于舊版本的Android;
大量的代碼流程追蹤。讀文章時你一定要清楚你在看的東西在整個系統處于什么樣的位置。
![]()
同時推薦老羅的這本書,平時看博客就可以,無聊的時候,比如在動車上可以把這本書翻翻。(非常優秀的書,不過據本人描述,這本書稿費還抵不會出版費)
鄧凡平老師也是為Android大牛, 博客同樣很有營養。但是不像羅升陽老師的那么系統, 更多的是一些技術點的深入探討。
閱讀時的姿勢
現在的問題是:當你拿到一份幾G的源碼,該從哪里開始呢?
著作權歸作者所有。
商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
</div>
宏觀上看,Android源碼分為功能實現上的縱向,和功能拓展上的橫向。
在閱讀源碼時需要把握好著兩個思路。譬如你需要研究音頻系統的實現原理,縱向:你需要從一個音樂的開始播放追蹤,一路下來,你發現解碼庫的調用,共享內存的創建和使用,路由的切換,音頻輸入設備的開啟,音頻流的開始。譬如你要看音頻系統包括哪些內容,橫向:通過Framework的接口,你會發現,音頻系統主要包括:放音,錄音,路由切換,音效處理等。
Android的功能模塊絕大部分是C/S架構
你心里一定需要有這個層級關系,你需要思路清晰地找到Server的位置,它才是你需要攻破的城,上面的libraries是不是很親切的樣子?看完它長成啥樣后,然后你才能發現HAL和Kernel一層層地剝離。很多研究源碼的同學兜兜轉轉,始終在JAVA層上,這是不科學的,要知道libraries才是它的精髓啊。
Android的底層是Linux Kernel。
在理解上面兩點之后,還是需要對Kernel部分有個簡單的理解,起碼你要熟悉kernel的基礎協議吧!你要能看懂電路圖吧!你要熟悉設備的開啟和關閉吧!你要熟悉調寄存器了吧!這方面的書太多了,我建議根據實例去閱讀,它并不復雜,不需要一本本厚書來鋪墊。在libraries和kernel間,可能還會有個HAL的東東,其實它是對kernel層的封裝,方便各個硬件的接口統一。這樣,如果我換個硬件,不用跑了長得很復雜的libraries里面改了,kernel調試好了后,改改HAL就好了。
好了,你現在是不是躍躍欲試準備去找個突破口準備進攻了,但是好像每個寶庫的入口都挺難找了我大概在三個月前閱讀完Android UI系統的源碼,這是Android最復雜的部分,我要簡單說下過程。我需要先找寶庫入口,我要研究UI,首先要找什么和UI有親戚關系吧!View大神跳出來了,沿著它往下找找看,發現它在貼圖在畫各種形狀,但是它在哪里畫呢,馬良也要紙吧?很明顯它就是某個寶藏,但是世人只是向我們描述了它有多美,卻無人知在哪里?我們需要找一張地圖羅。開發Android的同學逃不掉Activity吧!它有個setcontentview的方法,從這個名字看好像它是把view和activity結合的地方。趕緊看它的實現和被調用,然后我們就發現了Window,ViewRoot和WindowManager的身影,沿著WM和WMS我們就驚喜會發現了Surface,以及draw的函數,它居然在一個DeCorView上畫東西哈。借助Source Insight, UI Java層的橫向靜態圖呼之欲出了。完成這個靜態UML,我覺得我可以開始功能實現上追蹤了,這部分主要是C++的代碼(這也是我堅定勸阻的放棄Eclipse的原因),我沿著draw函數,看到了各個層級的關系,SurfaceSession的控制和事務處理,SharedBuffer讀寫控制,彪悍的SurfaceFlinger主宰一切,OpenGL ES的神筆馬良。FrameBuffer和FrameBufferDevice的圖像輸出,LCD設備打開后,開始接收FBD發過來的一幀幀圖像,神奇吧。
參考文獻
</div>本文由用戶 wdd119 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!