組件化之開發總結


 

Android組件化探索與實踐
在Android開發中,隨着業務復雜性不斷的增加,項目的代碼量會不斷的增大,這對於項目的維護提出了更大的挑戰。Android的組件化開發就是旨在解決大型項目的可維護性、可擴展性的難題。
一、組件化簡介
組件化是大型Android項目的一個好的解決方案。通過對項目工程進行組件化,利於代碼的維護與擴展,特別是在多團隊協作開發的模式中,尤其有效。

對於Android組件化的討論其實已經持續很長的一段時間了,也產生了很多種的組件化方案,不同的組件化方案有不同的利弊,但是其組件化目標基本一致,只是實現思路可能不同。本篇文章就是講解我們實現的一種組件化方案。

首先需要了解什么是組件化?簡單來說,組件化就是對項目依據業務功能拆分成不同的模塊,這些業務功能模塊彼此獨立,不相互依賴,既可以獨立編譯運行,也可一起打包成一個app。通過下圖來對比一下組件化的特點。


圖1.1傳統項目組織方式

 

 

圖1.2 Android組件化方式

 

 

通過上圖可以明顯的看出,傳統的項目組織形式,不同模塊之間會相互引用,造成耦合。而在組件化工程中,不同業務模塊之間是沒有相互引用關系的,彼此項目獨立。

 

二、組件化目標
在了解了組件化的定義之后,需要明確組件化的目標主要是什么。組件化的目標主要是解決如下幾方面的問題。

解耦合
隨着項目不斷的迭代,代碼越來越龐大、臃腫,這時代碼的維護就會顯得極其繁重。如果能夠根據業務來對項目進行業務模塊划分,不同開發人員來維護不同業務模塊,同時業務模塊之間彼此無關聯,這樣就方便了項目的維護。

代碼復用
組件化后,可以將一些功能組件、業務組件做成aar,上傳到maven私服,這樣便於其他團隊通過maven來引用,快速的引入相關的功能模塊。

加快編譯速度、偏於開發調試
當項目很大的時候,編譯一次是非常耗時的。如果項目采用組件化,那么業務模塊可以作為主module運行,這樣在開發階段,可以只加載開發人員關心的模塊,而無需加載所有模塊,這樣會大大的提供編譯速度,同時也可以明確問題邊界,利於bug的分析、處理。

便於多團隊協同開發、維護
當不同團隊維護一個app時,是極容易造成代碼沖突的。組件化即可完美解決這個問題,不同的業務團隊維護不同的業務組件,業務組件之間彼此隔離,不會相互干擾。

三、組件化設計
在上一節中明確組件化需要解決的問題,本小結就來討論下如何通過組件化的設計來逐條解決上述問題。

3.1 解耦合
在傳統方式中,不同的module之間想要相互調用,需要通過在gradle文件中相互引用,這樣才可以獲取到module中的類。而在組件化中,需要在不同模塊彼此隔離的情況下相互調用,這樣才能達到解耦合的目的。

可以通過路由框架來實現不同業務模塊之間的隔離。我們的組件化實踐采用了阿里的ARouter。通過ARouter可以實現Fragment的實例獲取、Activity之間的跳轉、交互等。舉例如下:

業務組件B想要跳轉到業務組件A中的Tab頁面,可以通過路由框架跳轉:
業務組件A中TabActivity定義如下:

@Route(path = "/app/tab")
public class TabActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}

業務組件B中利用路由框架來跳轉:

ARouter.getInstance().build("/app/tab").navigation()  



可以看到,通過路由框架,在業務組件B中,無需引入業務組件A中的TabActivity類即可實現頁面的跳轉。這樣就達到了解耦合。

組件之間頁面的跳轉可以通過如上的方式來解決,還有一種情景,通過如上的路由方式是解決不了的,即接口的調用。例如:業務組件B想要調用登錄組件中獲取登錄信息的方法。想要實現這樣的功能,比較通用的實現方式是接口下沉。

 

 


圖3.1接口下沉方式

這種方式的缺點就是:每個業務組件都需要把對外暴露的方法下沉到底部的組件中,這樣其實並沒有很好的實現解耦合。

而我們的組件化方案則采用了一種不同的方式,這也是本文所論述的組件化實踐中的一個核心內容。我們采取的方案是將業務組件拆分成兩個模塊:對外暴露的接口模塊、業務功能模塊。如下圖所示:

 

 

圖3.2 模塊拆分

以上圖為例,將原本的業務模塊Module A,拆分成接口模塊Module A interface、業務模塊Module A。接口模塊的作用是對外暴露接口,而業務模塊是接口真正的實現類,其會實現Module A interface中的接口。若其他模塊想要調用此模塊的相關功能,則只需引用其接口模塊,而無需引用其業務模塊,這樣就實現了業務模塊之間的解耦合。

剩下要解決的問題就是如何在只引用接口的情況下,就能調用接口實現類的相關方法。我們采用的方式是編譯期注解處理、反射等技術。詳細論述如下。

首先,需要定義兩個注解,BxBundleService、BxService。BxService的作用是標記對外暴露接口的實現類,BxBundleService的作用是標記需要注入的接口實例。以登錄模塊為例示例如下:

登錄接口模塊對外提供的接口:

 

 


登錄業務模塊中會實現上述的接口,通過BxService注解來標記其為實現類:

其他業務模塊可以在需要的時候調用登錄模塊中的方法:

 

 


其次,有了注解的標記,接下來就是注解的處理,我們為了提高程序的運行效率,沒有采用運行時處理注解,而是采用了編譯期間處理注解,具體如下:

1.引入編譯期注解處理類庫,在gradle文件中引入:

 

 

2. 定義Processsor,在編譯期對注解進行處理

注解處理,這點也是本組件化實踐過程中的核心要點
3.1 遍歷BxService注解標記的類,對其生成一個輔助源文件,以登錄模塊為例,生成如下的輔助文件

 

 

3.2 遍歷含有BxBundleService注解的類,完成BxBundleService標記的屬性對象的注入,其也是通過生成輔助源文件的方式來完成。生成的文件如下

 

 


輔助源文件的生成可以使用javapoet庫來實現。這里不再論述。

4.流程串聯,在使用登錄模塊時,需要通過BxbankModuleBus.inject(this)方法先完成服務的注入,其實現方式如下

 

 

通過代碼可以看出,其通過反射獲取到BxbankModuleBusInterface實例,並調用期bxbankModuleBusInject方法,此時再看上面3.2中的BxbankModuleBusInterface的實現類可以看到,bxbankModuleBusInject方法的主要作用就是找到LoginService的全路徑名,並通過反射生成其實例,並最終將其實例注入到引用對象中。至此完成了loginService的實例注入,之后就可以通過loginService實例來進行登錄模塊的相關調用。

通過前面的論述可以看到模塊間只需引入模塊的接口模塊,就可完成模塊間的相互調用,達到了解耦合的目的。

3.2 代碼復用
對代碼進行模塊拆分后可以看到,代碼已經完成了高內聚、低耦合,一些基礎的功能模塊、業務模塊可以提供給其他項目組來復用。

3.3 加快編譯速度、偏於開發調試
有時全量編譯整個工程是很耗時的,也是不必要的。例如某個開發者只負責登錄模塊,那么其可以排除其他的業務模塊,而只編譯、運行登錄模塊來進行開發和維護,這樣可以大大的提供編譯效率,提升開發體驗。可以采用如下的技術方案來達到這個目的:

首先:在工程的build.gradle文件中定義個變量來控制組件是否可以獨立編譯、運行

 

 


其次:在模塊組件的build.gradle文件中通過上面定義的變量來決定其是否作為主module運行

 

 


最后:需要對源文件進行處理,因為主module的manifest文件中,應包含launcher頁面的入口,如下:

 

 

 

從配置中可以看出,根據isModule變量的不同,源碼則引入不同的manifest文件,這樣就實現了業務組件的獨立編譯運行。

3.4便於多團隊協同開發、維護
通過之前的論述可以看出,項目進行組件化改造之后,各個業務組件之間相互獨立,無直接的引用,這樣不同業務組件之間可以進行獨立的黑盒開發、面向接口開發,而彼此不會相互依賴,極大的方便了多團隊的協同開發,減少沖突的可能行。

四、總結
前面論述了組件化的定義、目標、實現思路等內容。通過之前的論述可以看到,我們在進行組件化探索與實踐中,主要用到的技術包括路由、編譯期注解處理、反射、gradle配置等技術、技巧,通過這些技術方案的實施,形成了我們自己的組件化方案框架,達到了組件化的目的。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM