android徹底元件化番外篇-gradle3.0.0

格竹子發表於2017-11-06

最近Google正式推出AS3.0版本,同時gradle外掛也升級為3.0.0,目前各大開源庫都在做gradle3.0.0的相容,我也把得到開源的元件化方案AndroidComponent進行了升級,結論是:DDComponent在gradle3.0上是沒有相容問題的,可以直接使用。關於如何遷移到gradle3.0.0,請參見官方遷移指南

雖然沒有相容問題,但在升級的過程中也收穫了意外之喜,那就是發現gradle3.0.0對程式碼隔離的支援越來越好。為什麼對“程式碼隔離”這麼關注呢?大家可以回顧前兩篇文章Android徹底元件化方案實踐Android徹底元件化demo釋出,在這兩篇文章中提到的DDComponent元件化方案,被我冠以“徹底”二字,雖然有些說大話,但主要是為了強調DDComponent與之前其他元件化方案的不同之處就在於,DDComponent實現了元件之間的絕對隔離,不同元件之間在程式碼開發階段是完全不可見的,是一種徹底解耦的思想。為了實現這種隔離,我人為在編譯和執行期做了一次判斷和區分,既在編譯期間(開發期間)元件之間沒有任何依賴關係,但在打包和執行時,再偷偷新增依賴。具體可以參見前兩篇文章和github原始碼

不得不說,當時這種實現是迫不得已,我本來想直接使用gradle提供的功能來做這種隔離,其實gradle也的確提供一個類似的功能,那就是apk依賴語法,其作用就是保證依賴庫只在執行期間對外可見,但在編譯期間是不可見的。按說這已經滿足我的要求了,但是遇到了一個坑:在gradle2.+版本,apk依賴只能是jar,不能是aar,但是我們的元件因為含有各種資源,輸出產物就是aar!所以最終選擇了放棄apk這種語法。

而在最新的gradle3.0.0上,apk被替換為runtimeOnly語法,其作用還是一樣的,但是我發現runtimeOnly可以新增aar依賴!這的確讓我很興奮,這不就是我夢寐以求的功能嗎?有了這個尚方寶劍,元件化的方案就可以做的更薄了啊。於是我對在得到app上進行了實驗,結論是:runtimeOnly的確可以解決一些問題,但是還不夠。下面我從程式碼隔離、資源隔離和除錯切換(單獨和整合)三個方便仔細闡述,也順便再講一下DDComponent所能實現的功能。

一、程式碼隔離

在講程式碼隔離之前,先大致看一下gradle3.0.0對新增依賴的語法變化。

首先compile被廢棄了,而是分成了兩個:implementation和api,其中api與之前的compile功能基本一致,不再贅述;implementation就比較高階了,其作用就是,使用implementation新增的依賴不會再編譯期間被其他元件引用到,但在執行期間是完全可見的。這也是一種程式碼隔離。舉個例子,

元件A依賴lib1,既A implementation lib1
元件B依賴元件A,既B api A
複製程式碼

在gradle3.0.0之前,B是完全可以引用到lib1裡面的類的,但是現在B在編譯期間就做不到了,只能在執行期可以。這種思想有點類似於“下屬的下屬不是你的下屬”的思想。但是這種隔離在元件之間是不起作用的,在上面的例子中A的所有類對B還是完全可見的,也就是沒有做任何隔離的。不過implementation的確是一種有效減少編譯時間的方式,還是上面的例子,lib1發生了變化,現在只需要編譯A就可以了,而在之前B有可能也使用到了lib1,所以需要同時編譯B和A。按照官方建議,大部分情況下都應該使用implementation來進行新增依賴。

此外還有兩種變化,原來的apk語法被runtimeOnly取代,provided被compileOnly取代,其作用還是沒變。上文也講了,runtimeOnly有個極大的改動就是可以支援aar了,但是compileOnly還是隻能支援jar!

先做一個小結,目前gradle3.0.0的四種語法的功能和程式碼隔離效果見下圖:

四種語法的功能和程式碼隔離效果

從上圖可以看出,在程式碼隔離效果上,runtimeOnly的效果是最好的!但是就可以直接使用了嗎,答案是否定的

二、資源隔離

在前面的文章中,一直在強調程式碼隔離,其實元件之間的完全隔離還有一層就是資源隔離,否則還是容易造成元件之間的耦合。這個在文章的“單獨除錯”章節中提到了一句,就是每個元件都需要指定一個資源字首resourcePrefix,以避免整合後資源名衝突的問題。也就是說,一個徹底的元件化不僅要做到程式碼不能直接引用,資源也是不能引用的!

但是runtimeOnly目前還做到資源隔離,我在DDComponent的開源庫上做了試驗,app通過runtimeOnly引用sharecomponent元件,雖然sharecomponent的程式碼是不可見了,但是資源還是可以被app直接使用的並能成功執行。

從這一點上看,直接替換成runtimeOnly是不行的,為了達到這種效果,目前還是需要像DDComponent一樣,人為的加一層控制,所以從元件化方案的角度上看並沒有變的更薄,不過幸好DDComponent已經很簡單了,有一定的gradle基礎的人可以比較容易的理解。

三、除錯切換

除了上面說的資源隔離導致不能直接用runtimeOnly之外,還有一個使用上的問題需要解決,這也是DDComponent中compbuild外掛提供的一個功能:自動切換單獨除錯和整合除錯。在單獨除錯時,元件是一個application工程,其輸出產物是apk檔案,而在整合除錯時,被依賴的元件是一個library工程,其輸出產物是aar檔案。對於runtimeOnly來說,對aar和jar是支援的,但是不能支援apk,所以如果想在單獨除錯和整合除錯之間切換的話,需要人工修改runalone配置並修改build.gradle配置檔案,然後還需要sync之後才能生效,這種修改是相當繁瑣的。

在DDComponent中,這個問題的解決是通過“智慧”識別當前要除錯的元件來解決的,對於要除錯的元件將其設定為application工程,而將其依賴的其他元件默默修改為library工程,這種修改是即時生效的,對開發者是完全透明的。開發者直接點選AS的run功能區就可以隨意的除錯任意元件。AS的run功能區的圖如下:

隨意的除錯切換

四、總結

綜上所述,我們對DDComponent和gradle3.0.0做幾點總結: (1)升級到gradle3.0.0之後,可以繼續使用DDComponent,不需要專門做相容 (2)gradle3.0.0提供了implementation和runtimeOnly兩種語法,它們都能實現一定程度的程式碼隔離效果,建議大家在今後優先使用 (3)implementation和runtimeOnly目前還在資源隔離和除錯切換上還能滿足元件化的要求,所以還是需要使用DDComponent提供的完全隔離和隨意切換功能。

在DDComponent的原始碼中我增加了gradle3.0.0分支,依賴語法做了相應的替換,歡迎大家繼續支援“得到”app出品的元件化方案,原始碼地址:https://github.com/mqzhangw/AndroidComponent

相關文章