在目前移動互聯網時代,每一個 APP 就是流量入口,與過去 PC Web 瀏覽器時代不一樣的是,APP 的體驗與迭代速度影響着用戶的粘性,這同時也對從事移動開發人員提出更高要求,進而移動端框架也層出不窮。java
上圖顯示的是傳統的服務端架構和客戶端 App 架構對比。傳統的服務端架構中最底下是一個 OS,通常是 Linux,最上面服務端的業務,而中間有很是多的層次能夠在架構上,按照咱們的意願搭建中間的各個層次的銜接環節,使得架構具備足夠的靈活性和擴展性。可是到了 App 就會面對一個徹底不一樣的現狀,App 的OS(Android或iOS)本質上並非一個很瘦的像 Linux 這樣的 OS,而是在 OS 上有一個很重的 App Framework,開發一個普通的客戶端應用所要用到的絕大多數接口都在 Framework 裏,而上面的業務也是一個很是複雜多樣化的業務,最後會發現「架構」是在中間的一個很是尷尬的夾心層,由於會遇到不少在服務端架構中不須要面臨的挑戰。好比如下兩點:android
客戶端 APP 與服務端在架構上是有着必定的區別,在選擇對客戶端架構須要謹慎對待,須要有許多權衡的條件,在此前提上,是否有一種歸一的方式呢,能夠分而治之,並行開發,把業務分隔成一個個單獨的組件,整個架構圍繞組件開發,構建也是組件,一切皆組件。答案是有的,那就是打造客戶端組件框架。git
客戶端 APP 自身在飛速發展,APP 版本不斷迭代,新功能不斷增長,業務模塊數量不斷增長,業務上的處理邏輯越變越複雜,同時每一個模塊代碼也變得愈來愈多,這就引起一個問題,所維護的代碼成本愈來愈高,稍微一改動可能就牽一髮而動全身,改個小的功能點就須要迴歸整個 APP 測試,這就對開發和維護帶來很大的挑戰。同時原來APP 架構方式是單一工程模式,業務規模擴大,隨之帶來的是團隊規模擴大,那就涉及到多人協做問題,每一個移動端軟件開發人員勢必要熟悉如此之多代碼,若是不按照必定的模塊組件機制去劃分,將很難進行多人協做開發,隨着單一項目變大,並且 Andorid 項目在編譯代碼方面就會變得很是卡頓,在單一工程代碼耦合嚴重,每修改一處代碼後都須要從新編譯打包測試,致使很是耗時,最重要的是這樣的代碼想要作單元測試根本無從下手,因此必需要有一個更靈活的架構去代替過去單一工程模式。github
一樣這樣的問題在咱們工做具體項目中到處碰到,就拿咱們組內負責的某個移動端 APP 來講,就碰到以下幾個問題:編程
項目工程架構模式改變是大勢所趨,那又該如何作呢?那就是:打造組件化開發框架,用以解決目前所面臨問題,在講解如何打造以前,須要談談組件化概念,組件化框架是什麼。瀏覽器
問:什麼是組件,什麼是組件化?網絡
答:在軟件開發領域,組件(Component)是對數據和方法的簡單封裝,功能單一,高內聚,而且是業務能劃分的最小粒度。舉個咱們生活中常見的例子就是電腦主板上每一個元件電容器件,每一個元件負責的功能單1、容易組裝、即插即拔,但做用有限,須要必定的依賴條件纔可以使用。以下圖:架構
那麼一樣,組件化 就是基於組件可重用的目的上,將一個大的軟件系統按照分離關注點的形式,拆分紅多個獨立的組件,使得整個軟件系統也作到電路板同樣,是單個或多個組件元件組裝起來,哪一個組件壞了,整個系統可繼續運行,而不出現崩潰或不正常現象,作到更少的耦合和更高的內聚。併發
問:組件化、模塊化容易混淆,二者區別又是什麼?app
答:模塊化就是將一個程序按照其功能作拆分,分紅相互獨立的模塊,以便於每一個模塊只包含與其功能相關的內容,模塊咱們相對熟悉,好比登陸功能能夠是一個模塊,搜索功能能夠是一個模塊等等。而組件化就是更關注可複用性,更注重關注點分離,若是從集合角度來看的話,能夠說每每一個模塊包含了一個或多個組件,或者說模塊是一個容器,由組件組裝而成。簡單來講,組件化相比模塊化粒度更小,二者的本質思想都是一致的,都是把大往小的方向拆分,都是爲了複用和解耦,只不過模塊化更加側重於業務功能的劃分,偏向於複用,組件化更加側重於單一功能的內聚,偏向於解耦。
問:組件化能帶來什麼好處?
答:簡單來講就是提升工做效率,解放生產力,好處以下:
回到剛開始講的 APP 單一工程模式,看張常見 APP 單一工程模式架構圖:
上圖是目前比較廣泛使用的 Android APP 技術架構,每每是在一個界面中存在大量的業務邏輯,而業務邏輯中充斥着各類網絡請求、數據操做等行爲,整個項目中也沒有模塊的概念,只有簡單的以業務邏輯劃分的文件夾,而且業務之間也是直接相互調用、高度耦合在一塊兒的。單一工程模型下的業務關係,總的來講就是:你中有我,我中有你,相互依賴,沒法分離。以下圖:
組件化的指導思想是:分而治之,並行開發,一切皆組件。要實現組件化,不管採用什麼樣的技術方式,須要考慮如下七個方面問題:
組件單獨運行。由於每一個組件都是高度內聚的,是一個完整的總體,如何讓其單獨運行和調試?
代碼隔離。組件之間的交互若是仍是直接引用的話,那麼組件之間根本沒有作到解耦,如何從根本上避免組件之間的直接引用,也就是如何從根本上杜絕耦合的產生?
組件化架構目標:告別結構臃腫,讓各個業務變得相對獨立,業務組件在組件模式下能夠獨立開發,而在集成模式下又能夠變爲 AAR 包集成到「 APP 殼工程」中,組成一個完整功能的 APP。
先給出框架設計圖,而後再對這七個問題進行一一解答。
從圖中能夠看到,業務組件之間是獨立的,互相沒有關聯,這些業務組件在集成模式下是一個個 Library,被 APP 殼工程所依賴,組成一個具備完整業務功能的 APP 應用,可是在組件開發模式下,業務組件又變成了一個個 Application,它們能夠獨立開發和調試,因爲在組件開發模式下,業務組件們的代碼量相比於完整的項目差了很遠,所以在運行時能夠顯著減小編譯時間。
各個業務組件通訊是經過路由轉發,如圖:
這是組件化工程模型下的業務關係,業務之間將再也不直接引用和依賴,而是經過「路由」這樣一箇中轉站間接產生聯繫。
那麼針對以上提出的七個問題,具體解決以下:
1,代碼解耦問題
對已存在的項目進行模塊拆分,模塊分爲兩種類型,一種是功能組件模塊,封裝一些公共的方法服務等,做爲依賴庫對外提供,一種是業務組件模塊,專門處理業務邏輯等功能,這些業務組件模塊最終負責組裝APP。
2,組件單獨運行問題
經過 Gradle 腳本配置方式,進行不一樣環境切換。好比只須要把 Apply plugin: 'com.android.library' 切換成Apply plugin: 'com.android.application' 就能夠,同時還須要在 AndroidManifest 清單文件上進行設置,由於一個單獨調試須要有一個入口的 Activity。好比設置一個變量 isModule,標記當前是否須要單獨調試,根據isModule 的取值,使用不一樣的 gradle 插件和 AndroidManifest 清單文件,甚至能夠添加 Application 等 Java 文件,以即可以作一下初始化的操做。
3,組件間通訊問題
經過接口+實現的結構進行組件間的通訊。每一個組件聲明本身提供的服務 Service API,這些 Service 都是一些接口,組件負責將這些 Service 實現並註冊到一個統一的路由 Router 中去,若是要使用某個組件的功能,只須要向Router 請求這個 Service 的實現,具體的實現細節咱們全然不關心,只要能返回咱們須要的結果就能夠了。在組件化架構設計圖中 Common 組件就包含了路由服務組件,裏面包括了每一個組件的路由入口和跳轉。
4,UI 跳轉問題
能夠說 UI 跳轉也是組件間通訊的一種,可是屬於比較特殊的數據傳遞。不過通常 UI 跳轉基本都會單獨處理,通常經過短鏈的方式來跳轉到具體的 Activity。每一個組件能夠註冊本身所能處理的短鏈的 Scheme 和 Host,並定義傳輸數據的格式,而後註冊到統一的 UIRouter 中,UIRouter 經過 Scheme 和 Host 的匹配關係負責分發路由。但目前比較主流的作法是經過在每一個 Activity 上添加註解,而後經過 APT 造成具體的邏輯代碼。目前方式是引用阿里的 ARouter 框架,經過註解方式進行頁面跳轉。
5,組件生命週期問題
在架構圖中的核心管理組件會定義一個組件生命週期接口,經過在每一個組件設置一個配置文件,這個配置文件是經過使用註解方式在編譯時自動生成,配置文件中指明具體實現組件生命週期接口的實現類,來完成組件一些須要初始化操做而且作到自動註冊,暫時沒有提供手動註冊的方式。
6,集成調試問題
每一個組件單獨調試經過並不意味着集成在一塊兒沒有問題,所以在開發後期咱們須要把幾個組件機集成到一個 APP 裏面去驗證。因爲通過前面幾個步驟保證了組件之間的隔離,因此能夠任意選擇幾個組件參與集成,這種按需索取的加載機制能夠保證在集成調試中有很大的靈活性,而且能夠加大的加快編譯速度。須要注意的一點是,每一個組件開發完成以後,須要把 isModule 設置爲 true並同步,這樣主項目就能夠經過參數配置統一進行編譯。
7,代碼隔離問題
若是仍是 compile project(xxx:xxx.aar) 來引入組件,咱們就徹底能夠直接使用到其中的實現類,那麼主項目和組件之間的耦合就沒有消除,那以前針對接口編程就變得毫無心義。咱們但願只在 assembleDebug 或者 assembleRelease 的時候把 AAR 引入進來,而在開發階段,全部組件都是看不到的,這樣就從根本上杜絕了引用實現類的問題。
目前作法是主項目只依賴 Common 的依賴庫,業務組件經過路由服務依賴庫按需進行查找,用反射方式進行組件加載,而後在主工程中調用組件服務,組件與組件之間調用則是經過接口+實現進行通訊,後續規劃經過自定義Gradle 插件,經過字節碼自動插入組件的依賴進行編譯打包,實現自動篩選 assembleDebug 或 assembleRelease 這兩個編譯命任務,只有屬於包含這兩個任務的命令才引入具體實現類,其餘的則不引入。
一,建立工程
1,APP空殼工程
經過AndroidStudio建立一個APP空殼工程,如圖:
而後在 APP 工程添加依賴具體業務組件 Module。好比:
2,具體業務組件Module
須要遵循必定組件命名規範,爲什麼須要規範呢,由於須要經過組件命名規範來約束和保證組件的統一性和一致性,避免出現衝突。好比登錄組件,那麼名稱:b(類型)-ga(部門縮寫)-login(組件名稱),這就是咱們基於共同的約定進行命名的,爲後期維護和擴展都帶來辨識度。
二,業務組件配置文件
1,build.gradle配置文修改。以下:
if (isModule.toBoolean()) { apply plugin: 'com.android.application' } else { apply plugin: 'com.android.library' } android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion if (isModule.toBoolean()) { applicationId "com.hik.ga.business.login" versionCode 1 versionName "1.0" } else { //ARouter javaCompileOptions { annotationProcessorOptions { arguments = [ moduleName : project.getName() ] } } } } sourceSets { main { if (isModule.toBoolean()) { manifest.srcFile 'src/main/module/AndroidManifest.xml' } else { manifest.srcFile 'src/main/AndroidManifest.xml' //集成開發模式下排除debug文件夾中的全部Java文件 java { exclude 'debug/**' } } } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation project(':b-ga-common-function-comlib') if (!isModule.toBoolean()) { annotationProcessor "com.alibaba:arouter-compiler:${rootProject.annotationProcessor}" } }
這裏須要有幾點說明一下:
1,經過 isModule.toBoolean() 方法來進行組件間集成模式和組件模式的切換,包括模塊是屬於Application 仍是 Library,因爲集成了 ARouter,因此須要對 ARouter 配置文件進行處理。
2,若是組件模式下, 則須要從新設置 AndroidManifest.xml 文件,裏面配置新的Application路徑。好比Login組件單獨運行 AndroidManifest 清單文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="hik.ga.business.applogin" > <application android:name="debug.LoginApplication" android:allowBackup="false" android:icon="@mipmap/ic_launcher" android:label="@string/login_btn_str" android:supportsRtl="true" android:theme="@style/Theme.AppCompat.Light.NoActionBar"> <activity android:name="hik.ga.business.applogin.login.views.LoginActivity" android:label="@string/login_btn_str" android:launchMode="singleTop" android:screenOrientation="portrait" android:theme="@style/AppTheme.NoActionBarFullScreen"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
3,實現組件全局應用配置類,這個類的目的是在組件加載時初始化一些組件自身的資源,以下:
public class LoginApplicationDelegate implements IApplicationDelegate { private static final String TAG = "LoginApplicationDelegate"; @Override public void onCreate() { EFLog.d(TAG, "*------------------onCreate()---------------->"); } @Override public void enterBackground() { EFLog.d(TAG, "*------------------enterBackground()---------------->"); } @Override public void enterForeground() { EFLog.d(TAG, "*------------------enterForeground()---------------->"); } @Override public void receiveRemoteNotification(Map<String, String> message) { EFLog.d(TAG, "receiveRemoteNotification msg = " + message); } @Override public void onTerminate() { EFLog.d(TAG, "*------------------onTerminate()---------------->"); } @Override public void onConfigurationChanged(Configuration configuration) { EFLog.d(TAG, "*------------------onConfigurationChanged()---------------->"); } @Override public void onLowMemory() { EFLog.d(TAG, "*------------------onLowMemory()---------------->"); } @Override public void onTrimMemory(int var1) { EFLog.d(TAG, "*------------------onTrimMemory()---------------->"); } }
三,路由服務
1,定義公共組件路由API和入口,經過路由服務組件查找,如圖:
2,組件路由實現
每一個組件對外提供什麼能力,首先須要在路由服務組件建立一個接口文件,以下是登錄組件接口聲明和實現。
Login 接口:
具體實現:
路由使用:好比咱們想從設置頁面跳轉到登錄頁面,使用 Login 接口裏的方法,使用以下:
ILoginProvider loginService = (ILoginProvider) ARouter.getInstance().build(RouterPath.ROUTER_PATH_TO_LOGIN_SERVICE).navigation(); if(loginService != null){ loginService.accountToLogin(AccountActivity.this); }
總的來講,經過應用組件化框架,使得咱們工做中的具體項目變得更輕、好組裝、編譯構建更快,不只提升工做效率,同時自我對移動應用開發認知有進一步的提高。由於組件化框架具備通用性,特別適用於業務模塊迭代多,量大的大中型項目,是一個很好的解決方案。至於組件化框架以後演化的道路,則是打造組件倉庫,完善組件開發規範,豐富組件功能庫,有一些粒度大的業務組件能夠進一步的細化,對組件功能進行更單一的內聚,同時基於現有組件化框架,便於過分在將來打造插件化框架,進一步升級 APP 動態能力,好比熱加載、熱修復等,那又是另外一種使用場景和設計架構了,其實組件化和插件化框架最大的區別就是在是否具有動態更新能力。
把項目簡化下,github地址:DemoComponent,感興趣的能夠下過去看看。
參考文章: