天天看點

MVVM架構結合阿裡ARouter,打造一套Android-Databinding元件化

前言

關于Android的元件化,相信大家并不陌生,網上談論元件化的文章,多如過江之鲫,然而一篇基于MVVM模式的元件化方案卻很少。結合自身的調研和探索,在此分享一篇基于MVVMHabit架構(https://github.com/goldze/MVVMHabit )的一套Android-Databinding元件化開發方案。文章寫的比較簡單基礎,沒有大篇幅單向技術的讨論點,目的是讓學習了此方案的開發人員都可以快速上手搭建MVVM元件化項目。

整體架構

MVVM架構結合阿裡ARouter,打造一套Android-Databinding元件化

淺淡

MVVM的優勢

想必熟悉前端的朋友對MVVM非常了解,這種模式在目前web前端火的一塌糊塗,比如vue、angular、react都是采用MVVM設計模式實作的前端架構。而在Android開發中,MVVM并不是唯一的架構模式,最常用的可能是MVC模式(通常不是理想的實作)。比較流行的是MVP,它在某種程度上與MVVM模式非常相似。不過MVVM在MVP的基礎上更進一步的提高了開發效率,擁有了資料綁定的能力。說到Android MVVM,很自然的聯想到谷歌出的Databinding,它提供了xml與java的完美綁定,就像html與js的綁定一樣。雖然Android端的MVVM還不是很火,但我相信它是一種趨勢。趁着web前端MVVM的熱度,移動前端也應該崛起了。

元件化開發

代碼是死的,産品是活的。在日常開發中,各種各樣頻繁變動的需求,給開發上帶來了不小的麻煩。為了盡量把代碼寫“活”,是以出現了設計模式。但光有設計模式,還是很難滿足産品BT的需求。

對于簡單的小項目,大多都采用的是單一工程,獨立開發。由于項目不大,編譯速度及維護成本這些也在接受範圍之内。而對于做好一個App産品,這種多人合作、單一工程的App架構勢必會影響開發效率,增加項目的維護成本。每個開發者都要熟悉如此之多的代碼,将很難進行多人協作開發,而且Android項目在編譯代碼的時候電腦會非常卡,又因為單一工程下代碼耦合嚴重,每修改一處代碼後都要重新編譯打包測試,導緻非常耗時,最重要的是這樣的代碼想要做單元測試根本無從下手,是以必須要有更靈活的架構代替過去單一的工程架構。

使用元件化方案架構,高内聚,低耦合,代碼邊界清晰,每一個元件都可以拆分出來獨立運作。所有元件寄托于宿主App,加載分離的各個元件,各自編譯自己的子產品,有利于多人團隊協作開發。

MVVM架構結合阿裡ARouter,打造一套Android-Databinding元件化

MVVM模式 + 元件化

光說理論沒用,來點實際的東西,這裡要提兩個重要的架構。

  • MVVMHabit:基于谷歌最新AAC架構,MVVM設計模式的一套快速開發庫,整合Okhttp+RxJava+Retrofit+Glide等主流子產品,滿足日常開發需求。使用該架構可以快速開發一個高品質、易維護的Android應用。
  • ARouter:阿裡出的一個用于幫助 Android App 進行元件化改造的架構 —— 支援子產品間的路由、通信、解耦。

MVVMHabit + ARouter:MVVM模式 + 元件化方案,前者是設計模式,後者是方案架構,兩者并用,相得益彰。有這兩個架構作支撐,事半功倍,可快速開發元件化應用。

項目搭建

MVVM架構結合阿裡ARouter,打造一套Android-Databinding元件化

建立項目

先把工程中最基本的架子建立好,再一步步将其關聯起來

1. 建立宿主

搭建元件化項目與單一工程項目一樣,先通過Android Studio建立一個正常項目。

File->New->New Project...

建立的這個項目将其定義為“ 宿主 ”(大多數人都是這種叫法),也可以叫空殼項目。它沒有layout,沒有activity,它的職責是将分工開發的元件合而為一,打包成一個可用的Apk。

在宿主工程中,主要包含兩個東西,一個是AndroidManifest.xml:配置application、啟動頁面等;另一個是build.gradle:負責配置建構編譯/打包參數,依賴子子產品。

2. 建立元件

所謂的元件,其實也就是一個Module,不過這個Module有點特殊,在合并打包的時候它是一個library:apply plugin: ‘com.android.library’,在獨立編譯運作的時候,它是一個application:apply plugin: ‘com.android.application’。

File->New->New Module->Android Library...

一般可以取名為module-xxx(元件名)

3. 建立Library

除了業務元件之外,還需要建立兩個基礎Library,library-base 和 library-res。

  • library-base:存放一些公共方法、公共常量、元件通信的契約類等。上層被所有元件依賴,下層依賴公共資源庫、圖檔選擇庫、路由庫等通用庫,通過它,避免了元件直接依賴各種通用庫,承上啟下,作為整個元件化的核心庫。
  • library-res:為了緩解base庫的壓力,專門分離出一個公共資源庫,被base庫所依賴,主要存放與res相關的公共資料,比如圖檔、style、anim、color等。

4. 第三方架構準備

還需要準備兩個第三方的架構,即前面說的 MVVMHabit 和 ARouter

MVVMHabit:

allprojects { repositories { ... google() jcenter() maven { url 'https://jitpack.io' } }}
dependencies { ... implementation 'com.github.goldze:MVVMHabit:?'}           

ARouter:

defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = [AROUTER_MODULE_NAME: project.getName()] } }}
dependencies { api 'com.alibaba:arouter-api:?' annotationProcessor 'com.alibaba:arouter-compiler:?'}           

元件分離

元件化其實是一個 分離--組合 的過程,分離是分離産品原型,組合是組合代碼子產品。拿到需求後,一定不要急着開幹,首先将産品原型分離成一個個子原型,分工開發後,将編寫完成的子業務子產品又打包組合成一個完整的Apk。

最常見的應屬這種底部幾個tab的設計。

MVVM架構結合阿裡ARouter,打造一套Android-Databinding元件化

通過元件化,可以按照業務大緻将項目拆分為:首頁子產品、工作子產品、消息子產品、使用者子產品,當然還可以再分細一點,比如使用者子產品再分離一個身份驗證子產品出來。拆分的越細,複用起來就越友善。

那麼在上面 建立元件 時,則建立以下幾個元件Module:module-home、module-work、module-msg、module-user、module-sign。

元件配置

gradle是元件化的基石,想搭建好元件化項目,gradle知識一定要紮實(Android已經留下了gradle的烙印)。

1. 依賴關系

項目建立好後,需要将他們串聯起來,組合在一起。依賴關系如下圖所示:

MVVM架構結合阿裡ARouter,打造一套Android-Databinding元件化

宿主依賴業務元件

dependencies { //主業務子產品 implementation project(':module-main') //身份驗證子產品 implementation project(':module-sign') //首頁子產品 implementation project(':module-home') //工作子產品 implementation project(':module-work') //消息子產品 implementation project(':module-msg') //使用者子產品 implementation project(':module-user')}           

業務元件依賴library-base

dependencies { //元件依賴基礎庫 api project(':library-base') //按需依賴第三方元件}           

library-base依賴公共庫

dependencies { //support相關庫 api rootProject.ext.support["design"] api rootProject.ext.support["appcompat-v7"] //library-res api project(':library-res') //MVVMHabit架構 api rootProject.ext.dependencies.MVVMHabit //ARouter架構 api rootProject.ext.dependencies["arouter-api"] //其他公共庫,例如圖檔選擇、分享、推送等}           

2. 開啟dataBinding

Android MVVM模式離不開DataBinding,每個元件中都需要開啟,包括宿主App

android { //開啟DataBinding dataBinding { enabled true }}           

3. 模式開關

需要一個全局變量來控制目前運作的工程是隔離狀态還是合并狀态。在gradle.properties中定義:

isBuildModule=false           

isBuildModule 為 true 時可以使每個元件獨立運作,false 則可以将所有元件內建到宿主 App 中。

4. debug切換

在元件的build.gradle中動态切換library與application

if (isBuildModule.toBoolean()) { //作為獨立App應用運作 apply plugin: 'com.android.application'} else { //作為元件運作 apply plugin: 'com.android.library'}           

當 isBuildModule 為 true 時,它是一個application,擁有自己的包名

android { defaultConfig { //如果是獨立子產品,則使用目前元件的包名 if (isBuildModule.toBoolean()) { applicationId 元件的包名 } }}           

5. manifest配置

元件在自己的AndroidManifest.xml各自配置,application标簽無需添加屬性,也不需要指定activity的intent-filter。當合并打包時,gradle會将每個元件的AndroidManifest合并到宿主App中。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.goldze.main"> <application> ... </application></manifest>           

元件獨立運作時,就需要單獨的一個AndroidManifest.xml作為調試用。可以在src/main檔案夾下建立一個alone/AndroidManifest.xml。配置application标簽屬性,并指定啟動的activity。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.goldze.main"> <application ... > <activity ... > <intent-filter> ... </intent-filter> </activity> </application></manifest>           

并在build.gradle中配置

android { sourceSets { main { ... if (isBuildModule.toBoolean()) { //獨立運作 manifest.srcFile 'src/main/alone/AndroidManifest.xml' } else { //合并到宿主 manifest.srcFile 'src/main/AndroidManifest.xml' resources { //正式版本時,排除alone檔案夾下所有調試檔案 exclude 'src/main/alone/*' } } } }}           

6. 統一資源

在元件的build.gradle配置統一資源字首

android { //統一資源字首,規範資源引用 resourcePrefix "元件名_"}           

可以将每個元件的build.gradle公共部分抽取出一個module.build.gradle

if (isBuildModule.toBoolean()) { //作為獨立App應用運作 apply plugin: 'com.android.application'} else { //作為元件運作 apply plugin: 'com.android.library'}android { ... defaultConfig { ... //阿裡路由架構配置 javaCompileOptions { annotationProcessorOptions { arguments = [AROUTER_MODULE_NAME: project.getName()] } } } sourceSets { main { if (isBuildModule.toBoolean()) { //獨立運作 manifest.srcFile 'src/main/alone/AndroidManifest.xml' } else { //合并到宿主 manifest.srcFile 'src/main/AndroidManifest.xml' resources { //正式版本時,排除alone檔案夾下所有調試檔案 exclude 'src/main/alone/*' } } } } buildTypes { ... } dataBinding { enabled true }}
      

元件中引入module.build.gradle即可

apply from: "../module.build.gradle"android { defaultConfig { //如果是獨立子產品,則使用目前元件的包名 if (isBuildModule.toBoolean()) { applicationId 元件的包名 } } //統一資源字首,規範資源引用 resourcePrefix "元件名_"}dependencies { ...}           

完成

運作效果如下:

MVVM架構結合阿裡ARouter,打造一套Android-Databinding元件化

到此為止,一個最基本的元件化工程搭建完畢。

可行性方案

元件初始化

元件在獨立運作時,也就是debug期,有單獨的manifest,當然也就可以指定Application類進行初始化。那麼當元件進行合并的時,Application隻能有一個,并且存在宿主App中,元件該如何進行初始化?

1. 反射

反射是一種解決元件初始化的方法。

在library-base下定義一個 ModuleLifecycleConfig 單例類,主要包含兩個公共方法:initModuleAhead(先初始化)、initModuleLow(後初始化)。

為何這裡要定義兩個初始化方法?

元件多了,必定會涉及到初始化的先後順序問題,元件中依賴的第三方庫,有些庫需要盡早初始化,有些可以稍晚一些。比如ARouter的init方法,官方要求盡可能早,那麼就可以寫在library-base初始化類的onInitAhead中,優先初始化。

@Overridepublic boolean onInitAhead(Application application) { KLog.init(true); //初始化阿裡路由架構 if (BuildConfig.DEBUG) { ARouter.openLog(); // 列印日志 ARouter.openDebug(); // 開啟調試模式(如果在InstantRun模式下運作,必須開啟調試模式!線上版本需要關閉,否則有安全風險) } ARouter.init(application); // 盡可能早,推薦在Application中初始化 return false;}

      

再定義一個元件生命周期管理類 ModuleLifecycleReflexs ,在這裡注冊元件初始化的類名全路徑,通過反射動态調用各個元件的初始化方法。

注意:元件中初始化的Module類不能被混淆

2. 初始化接口

定義一個 IModuleInit 接口,動态配置Application,需要初始化的元件實作該接口,統一在宿主app的Application中初始化

public interface IModuleInit { //初始化優先的 boolean onInitAhead(Application application); //初始化靠後的 boolean onInitLow(Application application);}           

3. 初始化實作

反射類和接口都有了,那麼在各自的元件中建立一個初始化類,實作IModuleInit接口。最後在宿主的Application中調用初始化方法

@Overridepublic void onCreate() { super.onCreate(); //初始化元件(靠前) 

ModuleLifecycleConfig.getInstance().initModuleAhead(this); //.... //初始化元件(靠後) 

ModuleLifecycleConfig.getInstance().initModuleLow(this);}           

最後即實作元件的初始化效果

MVVM架構結合阿裡ARouter,打造一套Android-Databinding元件化

小優化: 當元件獨立運作時,宿主App不會執行onCreate方法,但是元件業務又需要初始化單獨調試。正常做法是元件中單獨定義Application,但這樣每個元件都需要建立一個Application,比較繁瑣。我們有了上述的初始化方法,可以在 library-base中定義一個 DebugApplication ,debug包下的代碼不參與編譯,僅作為獨立子產品運作時初始化資料。最後記得在元件的調試版alone/AndroidManifest下指定為 base 中的 DebugApplication。

元件間通信

元件間是完全無耦合的存在,但是在實際開發中肯定會存在業務交叉的情況,該如何實作無聯系的元件間通信呢?

1. ARouter

ARouter 之是以作為整個元件化的核心,是因為它擁有強大的路由機制。ARouter在library-base中依賴,所有元件又依賴于library-base,是以它可以看作為元件間通信的橋梁。

MVVM架構結合阿裡ARouter,打造一套Android-Databinding元件化

在元件A中跳轉到元件B頁面:

ARouter.getInstance() .build(router_url) .withString(key, value) .navigation();           

在元件B頁面中接收傳過來的參數:

@Autowired(name = key)String value;           

更多ARouter用法:

https://github.com/alibaba/ARouter/blob/master/README_CN.md

2. 事件總線(RxBus)

MVVMHabit 中提供了RxBus,可作為全局事件的通信工具。

當元件B頁面需要回傳資料給元件A時,可以調用:

_Login _login = new _Login();RxBus.getDefault().post(_login);           

在元件A中注冊接收(注冊在調用之前完成):

subscribe = RxBus.getDefault().toObservable(_Login.class) .subscribe(new Consumer<_Login>() { @Override public void accept(_Login l) throws Exception { //登入成功後重新重新整理資料 initData(); //解除注冊 RxSubscriptions.remove(subscribe); } });RxSubscriptions.add(subscribe);           

base規範

library-base 有兩個主要作用:一是依賴通用基礎jar或第三方架構,二是存放一些公共的靜态屬性和方法。下面列舉一些基礎通用類的約定規範。

1. config

在base的config包下面,統一存放全局的配置檔案,比如元件生命周期初始化類:ModuleLifecycleConfig、ModuleLifecycleReflexs,網絡ROOT_URL,SD卡檔案讀寫目錄等

2. contract

RxBus元件通信,需要經過base層,統一規範。那麼可以在contract包下面定義RxBus的契約類,寫好注釋,便于其他元件開發人員使用。

3. global

主要存放全局的Key,比如 IntentKeyGlobal: 存放元件間頁面跳轉傳參的Key名稱; SPKeyGlobal: 全局SharedPreferences Key 統一存放在這裡。單個元件中内部的key可以另外在單獨元件中定義。

4. router

ARouter 路由@Route注解中Path可以單獨抽取一個或者兩個RouterPath類出來,比如定義一個RouterActivityPath:

public class RouterActivityPath { /** * 主業務元件 */ public static class Main { private static final String MAIN = "/main"; /*主業務界面*/ public static final String PAGER_MAIN = MAIN +"/Main"; }           

Activity的路由路徑統一在此類中定義,并使用靜态内部類分塊定義各個元件中的路徑路由。

總結一下

項目元件化,就好比制造業,生活中的絕大多數工業産品。比如汽車,由發動機、輪子、引擎等各個重要零件拼裝而成。同樣,我們的app也是由各個元件并聯起來,形成一個完整可執行的軟體。它的精髓就是這麼3點:獨立、完整、自由組合。 而且元件化甚至都不算是人類的發明。即使放在自然界,這也是早已存在的模式。想想我們人體多麼複雜,絕對不亞于windows作業系統。但除去幾個非常重要的數器官之外,大多部分損壞或缺失,我們都能活下來。這不得不說是元件化的奇迹。

寫軟體一定要注重架構,不要等到代碼越寫越爛,越爛越寫,最後連自己都看不下去了才想到去重構。元件化是一個很好隔離每個業務子產品的方案,即使其中一個元件出了問題,也不用像單一工程那樣整體地去調試。配合MVVM設計模式,使得我們工作中的具體項目變得更輕、好組裝、編譯建構更快,不僅提高工作效率,同時自我對移動應用開發認知有進一步的提升。元件化架構具有通用性,特别适用于業務子產品疊代多,量大的大中型項目,是一個很好的解決方案。

歡迎移動開發最新技術交流學習群;964557053。加群請備注csdn

Android架構的演進,由 子產品化 到 元件化 再到 插件化。我們在元件化開發的道路上,盡可能的完善元件開發規範,豐富元件功能庫,有一些粒度大的業務元件可以進一步的細化,對元件功能進行更單一的内聚,同時基于現有的元件化架構,便于過度在未來打造插件化架構。

繼續閱讀