1. 程式人生 > >Android元件化之終極方案

Android元件化之終極方案

Fragment或View如何支援元件化

距離 Android元件化方案 釋出已經半年有餘,雖說這個方案已經能夠解決一些專案的需求,但是依然不夠完美。很多開發者也在部落格和GitHub中留言甚至發郵件問我,Fragment怎麼辦? 目前市面上APP的風格還是類似於微信介面的比較多,好幾個Fragment擺在主介面中,然後點選NavigationBar上的圖示顯示不同的Fragment。但是很顯然Android元件化方案
並不適合這種情況。剛開始我給大家想了一個不是那麼優雅的折中方案,這個方案是將我們應用的MainActivity移動到“app殼工程”中,因為“app殼工程”的本身就肩負著管理和組裝業務元件的功能,因此這個MainActivity自然也就拿到了分散到其他業務元件中的Fragment,就像下圖這樣:

偷懶的Fragment元件化工程模型

這個方案雖說也是可行的,但是顯然沒有達到我們期望的結果,我們理想的“app殼工程”是不應該跟業務有關的,他應該負責管理和組裝其他元件,並將這些業務元件包裝成一個可以釋出到應用市場的APP,也就是說我們希望“app殼工程”不要和任何業務相關,不要耦合其他元件中的程式碼,我想我可以隨意的替換那個空殼工程,而不會影響到我的APP打包,顯然這個偷懶的方案是做不到這一點的。因此我必須解決的問題是:一個業務元件如何在不依賴其他業務元件的情況下拿到這些業務元件中的Fragment或者其他View?

假設小A收到一個邀請函,邀請他要去參加一個網際網路技術會議,而這個會議在一個叫”XX大酒店“中舉行,但是小A之前並沒有聽過這個酒店,那麼他怎麼才能找到這個酒店並參加會議呢?大多數同學都會習慣性的開啟百度地圖,然後輸入“XX大酒店”,百度地圖就會幫我找到這個酒店。但是大家有沒有想過為什麼百度地圖能找到這個酒店呢?這時候肯定有人會說:這不是廢話嗎,百度地圖都不知道還有誰知道? 這讓我想起08年那時候還沒有智慧手機,我想去蘭州的一個大廈,但是我問了周圍很多路人都沒有人知道這個大廈在哪裡。而現在我們去一個地方從問路人變成了問百度地圖,那麼又回到哪句話,百度地圖是怎麼知道這些地方呢?有兩種可能:一種是有人告訴百度地圖某個地點在那裡(那些小商店就是這樣做的),另一種是百度地圖派人去城市裡晃悠把城市的所有顯著的地標都記錄下來。他們的關係就像下圖表示的這樣:

這裡寫圖片描述

其實在 Android元件化方案 中已經有類似功能的元件:Common元件,還有另外一個就ARouter了。但是鑑於ARouter是開源庫,我們不方便去修改,那麼我們就在Common元件中做手腳。如果我想讓Common元件知道D元件中的DFragment,我們需要怎麼做呢?首先將CFragment和DFragment新增到Common元件中去,B元件想要獲取DFragment,直接就去Common元件查詢就行。

這裡寫圖片描述

這時候你一定很激動,彷彿發現了什麼絕世祕密一樣,你恨不得立馬就寫個Demo測試下這個方案。當你擼起袖子開幹後發現, What? 怎麼才能把DFragment新增到BaseApplication啊?我們都知道Application啟動後會回撥onCreate()方法,貌似我們可以在Application啟動的時候在onCreate方法中把Fragment新增到BaseApplication中去。這時候你腦海肯定會付出那個黑人問號的表情,總不能讓D元件去依賴Common吧?這關係太特麼亂了。

但是經過前面的鋪墊,其實大家都發現了點什麼,那就是:只要我們能在業務元件中知道Application的生命週期,那麼我們就可以在Application onCreate 時將業務元件中的Fragment新增到Common元件中!那麼這個時候我們就需要解決:如何才能讓業務元件知道Application的生命週期呢?問題分析到這裡,我們看看下面的類圖:

這裡寫圖片描述

首先我們Common元件中定義一個代理介面,這個代理介面定義了Application中的回撥方法,然後各個業務元件實現這個代理介面,然後在onCreate方法中做自己想做的事情,而BaseApplication會在呼叫onCreate方法時找到所有實現了ApplicationDelegate的類,並呼叫這些實現類的方法,這樣業務元件就知道了我們應用程式的生命週期;當業務元件知道應用程式的宣告週期後,不僅可以在業務元件中將Fragment新增到Common元件中,而且還可以在業務元件中初始化資料,由於全域性Context可以在任何元件中獲取,實際上這種方式已經等同於在Application中初始化資料。

如何管理元件

Android元件化方案 中,由於所有元件都在同一個專案中,並且使用 compile project(‘:元件名’) 方式依賴其他元件,這樣就會導致很多問題。

1. 編譯很慢。由於所有的元件工程都在同一個專案中,並且元件之間或app殼工程會依賴其他元件,導致每次打包APP都需要把各個元件編譯一次,如果專案中的元件達到十幾個後,結果真的很感人!隨著元件數量的增長,編譯時間幾乎呈指數性增加,這個滋味,我想每位Android開發者都深有體會。
2. 元件不方便引用。因為我們的元件是以原始碼的形式置於專案中,如果另外一個專案也需要某個元件,這個時候就只能再複製一份程式碼到新專案中。這就導致一個元件存在於多個專案中,那麼最終肯定無法保證這個元件的程式碼會不會被修改,也就是說元件已經無法保證唯一性了。
**3. 無法控制權限,也不方便混淆。因為專案中包含所有的元件原始碼,這時候肯定沒有辦法控制程式碼許可權了,假如某個元件是另外一個部門或公司提供給你用的,那麼他們當然不希望給你原始碼。

那麼如果解決這些問題呢?我想大多數Android開發者都能想到這個辦法。如果你把開源的三方庫當做一個功能元件的話,那麼很顯然,我們在使用這些三方庫的時候是通過什麼方式呢?難道你會下載它的原始碼嗎,應該很少有人會這樣做吧。那麼讓我們看看我們是怎麼引入三方庫的:

    compile 'com.github.bumptech.glide:glide:3.8.0'
    compile 'io.reactivex.rxjava2:rxjava:2.1.3'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.google.code.gson:gson:2.8.1'
    compile 'org.greenrobot:eventbus:3.0.0'

這樣大家就很熟悉了,這些開源庫一般都是上傳到maven或jcenter倉庫上供我們引用。那麼我們自己開發的元件能不能也傳到maven或jcenter倉庫呢?當然了不是讓你傳到開源倉庫上去,我的意思是我們可以在公司內部搭建一個私有的maven倉庫,將我們開發好的元件上傳到這個私有的maven倉庫上,然後內部開發人員就可以像引用三方庫那樣輕而易舉的將元件引入到專案中了,這是他們關係就像下圖這樣:

這裡寫圖片描述

搭建倉庫管理私服主要有如下目的:

  1. 提升編譯效能和可靠性
  2. 為所有二進位制軟體元件及其依賴提供配置管理中心
  3. 為你所在組織和公開倉庫提供一個高階可配置的代理
  4. 建立私有元件釋出中心
  5. 通過改善元件的可用性、版本控制、安全、質量而提升其可維護性和可管理性。

而這也恰好解決了我們在元件化專案中碰到的問題。本來我想將Android元件化專案AndroidModulePattern 中的元件上傳到 jitpack ,然後給大家做個演示,但是很可惜,我試了很多次都失敗了,大家只能自己試試了。