網上元件化的文章很多,但大多數文章都從底層的細枝末節開始講述,由下而上給人一種這門技術“博大精深”望而生畏的感覺。而我寫這篇文章的初衷就是由上而下,希望別人在閱讀的過程中能夠覺得“元件化原來也就是這幾個東西”的感覺。
首先我們來看一下元件化專案和傳統專案的區別
在傳統的專案裡
我們通常情況下會有一個core的libary模組和一個app的application模組,業務中的邏輯都寫在app中各個功能模組放到不同的包下。這樣做有以下幾個主要的缺點:
1、實際業務變化非常快,但是單一工程的業務模組耦合度太高,牽一髮而動全身。
2、在開發過程中,任何一位成員沒辦法專注於自己的功能點,影響開發效率。
3、多人聯合開發在版本管理中很容易出現衝突和程式碼覆蓋的問題。
4、功能測試和系統測試每次都要進行。
5、無論分包做的再好,隨著專案的增大,專案會逐漸失去層次感,別人來接手的時候會很吃力。
6、我們在debug一個小功能的時候每次修改程式碼都需要重新build整個專案,這樣顯的很不合理。
在元件化專案中
除了有commonLib和app元件外,我們按照功能劃分各個業務元件(可以劃分出app1,app2,app3,app4四個大元件),之前的包變成現在的模組,增加了層次感;每個功能模組可以單獨編譯,加快了編譯速度,也為提供單元模組測試提供了支援;多人開發只負責自己的模組,直接避免了版本管理的衝突。
在明白了元件化為我們解決的主要問題後我們來看看需要怎麼做
初步實現組建化其實我們最終要解決的問題就只有2個:
1.業務元件可整合編譯也可單獨編譯--通過配置gradle即可解決
2.業務元件之間的頁面跳轉以及通訊--使用阿里開源的ARouter即可解決
接下來我們具體來看一下如何操作
首先來看一下模組間依賴的問題
我們可以參照×××的兩個元件(app1,app2)來配置,首先我們專案基本結構如下:
我們一共需要建4個元件,除了2個功能元件外還有一個基本的core元件和一個作為啟動的app元件。
在建好專案後我們需要給2個功能元件配置一個是否單獨編譯的開關:
關於開關的配置位置這是一個問題,我們把它新增在gradle.properties檔案中,這樣我們每次修改值的時候就可以觸發gradle的重新構建,便於我們單獨編譯元件。
我們單獨編譯的開關配置好了,現在我們來看看元件之間的依賴關係:
對於2個功能元件,我們要為它裝上我們之前配置的是否單獨編譯的開關,我們需要修改如下2個地方:
可以看到我們要修改的就是我紅框框住的地方,當我們的開關開啟的時候,我們就把他當成一個單獨的application來編譯,並且賦予它一個獨一無二的applicationId,這樣我們就可以通過剛剛在gradle.properties中配置的開關來控制它是否單獨作為一個application來編譯。
而對於主入口的app元件我們則需要做如下的配置:
我們除了需要配置基本的core元件依賴以外還需要在app元件的gradle檔案中根據開關選擇是否需要依賴我們的功能元件,這個和各個功能元件中的配置是相呼應的。
而對於其他元件模組,重複上述步驟即可完成元件化框架的搭建。
在完成了元件化框架的搭建後,需要解決業務元件之間的頁面跳轉以及通訊
首先,為了方便各個元件之間的互動我們借用了阿里的ARouter庫,所以在每個非core的元件(包括主Application)中都強烈建議加入對ARouter和core的依賴。
首先來看各個元件頁面間是怎樣跳轉的
我們之前已經依賴了ARouter(詳細用法參照https://github.com/alibaba/ARouter),我們要用它來幫我們實現跳轉需要以下幾步:
跳轉的方法就如上圖顯示,我們需要標明目標頁面,附帶上要傳送的引數,然後呼叫navigation()就可以跳轉了,不過有人問目標頁面怎麼看著就是一個路徑,它是怎樣定義的?
- 首先要用@Route註解標註頁面,並在path變數中給頁面定義一個路徑
- 對於傳送過來的變數我們直接定義一個同名的欄位用@Autowired變數標註,Arouter會對該欄位自動賦值
- 最後我們還需要將該頁面注入到ARouter中(原理類似ButterKnife),讓他幫我們完成我們需要的工作
這樣,我們就完成了頁面間的跳轉了,是不是比起我們傳統的方法更加簡單合理?
然後來看元件間如何通訊
這裡我想在app2元件中呼叫app1元件的sayHello方法來Toast一個人的名字,那app1元件的方法怎樣才能被其他元件(包括主元件和其他元件)呼叫
- 首先在core元件裡建立一個暴露方法的介面,並定義介面簽名,同時繼承 Iprovider 介面
- 然後在app1元件中繼承core裡定義的介面,並實現簽名方法。這裡我們同樣使用Arouter的 @Router註解來提供這次服務的路由
- 最後,我們在其他模組使用 @Autowired 註解就可以呼叫該方法了
可以看到我們同樣使用了@Autowired註解來初始定baseService服務,並將頁面注入Arouter中即可呼叫服務中的方法,且對於服務的依賴是基於介面的依賴,大大提高了其靈活性!
基本元件化框架的搭建就完成了,希望認真看完的朋友能有所收穫!如有不正之處還望指正!
關於元件化的思考
- 元件的動態裝載與解除安裝。業務元件大多都需要有自己的Application,然後在這裡做相關的初始化操作,在整合模式下,只能是主元件可以有Application,那麼問題一,業務元件怎麼獲取這個全域性唯一的Application.問題二,業務元件的大量初始化操作,怎麼統一到主元件才有的Application,假如所有初始化都放到主Application,那麼啟動速度和效能開銷如果保證。
- 程式碼隔離。有一個隱患沒有解決,那就是我們可以使用compile project(`:xxx`)來引入元件,雖然我們使用了介面+實現的架構,元件之間必須針對介面程式設計,但是一旦我們引入了(特別是主app引入)xxx元件,那就完全可以直接使用到其中的實現類,這樣我們針對介面程式設計的規範就成了一紙空文。千里之堤毀於蟻穴,只要有程式碼(不論是有意還是無意的)這麼做了,我們前面的工作就白費了。
以上兩個思考如何解決,歡迎各位在git issues
以上專案git(歡迎參與改進)