本文作者
作者:codelang
連結:
https://juejin.im/post/5e8090ae6fb9a03c3d7372a2
本文由作者授權釋出。
之前提到:
總覺得 ViewBinding 和 DataBinding 能力非常類似,于是又找了一些和 DataBinding 的差別文章來看,恩...還是沒有看到太本質的差別,後面可能會詳細的對比一波,有比較了解的也歡迎留言,先看看這篇 ViewBinding 實戰吧。
碼農西哥,公衆号:鴻洋ViewBinding 實戰,遞進優雅的寫波代碼
今天終于看到一篇帶大家從本質來了解 ViewBinding 了。
如果你完全沒了解過 View Binding,可以先看下面這篇介紹:
AS 3.6 Canary 中推出新技術 視圖綁定 View Binding
今天我們來深入的了解 ViewBinding 的本質,看看他是怎麼生成 ActivityMainBinding 這種檔案的。
1 使用
ViewBinding 目前隻支援 as3.6,使用方法很簡單,僅僅隻需要添加如下代碼:
make project 之後,會在對應的 module 路徑:
app/build/generated/data_binding_base_class_source_out/${buildTypes}/out/${包名}/databinding
生成 ViewBinding 檔案,為什麼我會說 對應的 module ?因為 viewBinding 隻對目前設定了 enabled = true 的 module 才會進行處理。
然後來看下處理後的檔案:
我們來看看這個檔案有哪些資訊:
- R.layout.activity_main 布局檔案
- 布局檔案中的 view 控件和 view id
- 布局檔案的 rootView 和類型
接下來,我們會通過源碼的方式來跟蹤到,這些資訊是怎麼産生的。
具體使用可以參考文章:
[譯]深入研究ViewBinding 在 include, merge, adapter, fragment, activity 中使用
https://juejin.im/post/5e4806f3e51d4526c550a2ef
2 準備
由于我們并沒有依賴其他 plugin 就可以使用,是以能被直接識别隻能是 classpath 依賴的 gradle 了:
classpath 'com.android.tools.build:gradle:3.6.1'
既然 make project 之後就可以看到 ViewBinding 的生成類,那麼,我們可以根據 make project 的 build 資訊檢視做了哪些 task:
沒有找到 ViewBinding,但找到了 dataBinding,但可以肯定的是,這個 dataBinding 就是生成 ViewBinding 的 task(因為沒有其他的 task 帶有 binding)。
然後我們可以去 maven 倉庫找一下 gradle:3.6.1 ,驚喜的是,gradle:3.6.1 的依賴項有 18 個,第一個就是 Data Binding Compiler Common:
https://mvnrepository.com/artifact/com.android.tools.build/gradle/3.6.1
然後我們進去找到對應的 compiler 3.6.1 版本,通過 gradle 依賴,我們就能看到源碼了:
https://mvnrepository.com/artifact/androidx.databinding/databinding-compiler-common/3.6.1
compile group: 'androidx.databinding', name: 'databinding-compiler-common', version: '3.6.1'
可以看到,ViewBinding 是屬于 dataBinding 庫裡面的一個小功能。
3 階段一:收集元素
由于我們僅僅隻是檢視 dataBinding compiler,是以,對于 gradle 調用 compiler 的哪個部分進行聯結,我們是檢視不到的,但這也不影響我們跟蹤源碼。
我們直接來看 :
LayoutXmlProcessor.java
我們直接來看 全量編譯檔案處理 :
①、判斷目前的檔案夾的檔案名 startWith 是否是 layout
②、會建立一個檔案輸出目錄, 輸出目錄為 new File(input.getRootOutputFolder(),file path); 這個 file path 做了與輸入目錄的 relativize 化,其實,可以了解為,這個輸出目錄為 輸出目錄 + file 檔案名 。
③、判斷 layout 下面的檔案名 endWith 是否是 .xml
④、處理 xml 檔案,這個地方也會建立一個輸出目錄,跟 ② 的方式一樣,最終,這個方法會
調用到 processSingleFile 方法
然後我們來看下 processSingleFile 方法:
①、這個地方會拿着 xml 檔案的路徑和輸出路徑進行解析
②、将解析結果緩存起來
然後來看下 xml 的解析 parseXml
LayoutFileParser.java
parseOriginalXml:
①、是否是 databinding,這個的判斷依據是,根元素是否是 layout , 擷取 data 和 rootView
②、isViewBindingEnable 就是 gradle 設定的 enable = true,根元素就是就是他的 rootView,這個地方要注意的是 data = null,data 資料隻有 databinding 才會有的元素,viewBinding 是不會去解析的
③、解析表達式,這裡面會循環周遊元素,解析 view 的 id、tag、include、fragment 等等 xml 相關的元素,并且還有 databinding 相關的 @={ 的表達式,最後将結果緩存起來,源碼我就補貼了,太多,影響文章
4 階段二:寫 Layout 檔案
LayoutXmlProcessor.java
①、周遊之前收集到的所有 LayoutFileBundle,寫入 xmlOutDir 路徑
②、生成 LayoutFileBundle 的檔案名,這個檔案名最終生成為:
layout.getFileName() + '-' + layout.getDirectory() + ".xml
例如 activity_main.xml,生成的 fileName 為 activity_main-layout.xml
③、将 LayoutFileBundle 轉換 xml ,寫入檔案
由于我們是直接跟蹤的 databinding compiler 庫,是以無法跟蹤到 gradle 是什麼聯結 compiler 庫的,是以,xmlOutDir 我是未知的,也不知道他存到了哪,但沒有關系,我們既然知道了生成的檔案名規則,我們可以全局搜尋該檔案,最終,我們在該目錄中搜尋到:
app/build/intermediates/data_binding_layout_info_type_merge/debug/out/activity_main-layout.xml
檔案内容如下:
這份 xml 描述了原始 layout 的相關資訊,對于 include 和 merge 是怎麼關聯 tag 的,讀者可以自行運作檢視
5 階段三:寫 ViewBinding 類
BaseDataBinder.java
可以看到,最後又去讀之前生成的 layout xml,這個地方為什麼會又寫又讀,而不是直接利用之前 layout 的緩存?我想可能是因為解耦,他們都是獨立的 task。
然後來看是如何生成 Binding 類的:
①、toViewBinder 是 BaseLayoutModel 的拓展函數,他會将 LayoutFileBundle 包裝成 ViewBinder 類傳回
②、toJavaFile 是 ViewBinder 的拓展函數,該拓展函數在 ViewBinderGenerateSource 類中
ViewBinderGenerateSource.java
這個地方就貼 typeSpec 方法了,具體的,大家可以自己去看源碼,從 typeSpec 中,我們就可以看到點生成的 ViewBinding 類包含了哪些東西,rootView 字段,inflater 、bind 方法。
總結
文章已經盡量保持源碼簡短,隻貼核心部分。本來還想絮絮叨叨一下,算了,就到這。
最後推薦一下我做的網站,玩Android: wanandroid.com ,包含詳盡的知識體系、好用的工具,還有本公衆号文章合集,歡迎體驗和收藏!
推薦閱讀:
代碼潔癖症的我,學習Lint學到心态爆炸 一段代碼引發的問題,== , equals包含多少知識 關于Handler 的這 15 個問題,你都清楚嗎?
掃一掃 關注我的公衆号
如果你想要跟大家分享你的文章,歡迎投稿~
┏(^0^)┛明天見!