Android組件化、模塊化實現

前言

移動端平臺不斷髮展,不斷迭代更新,APP軟件越來越複雜和龐大,維護和更新亦是如此。爲了解決這些問題,降低軟件的複雜性和耦合度,同時提高開發效率,模塊化在移動端就變得勢在必行。

模塊化理解

模塊化是指解決一個複雜問題時自頂向下逐層把系統劃分成若干模塊的過程。每個模塊完成一個特定的子功能,所有的模塊按某種方法組裝起來,成爲一個整體,完成整個系統所要求的功能。

通過以下類比可以更好地理解什麼是模塊化:

我們可以把軟件看做是一輛汽車,開發一款軟件的過程就是生產一輛汽車的過程。一輛汽車由車架、發動機、變數箱、車輪等一系列模塊組成;同樣,一款大型商業軟件也是由各個不同的模塊組成的。

汽車的這些模塊是由不同的工廠生產的,一輛 BMW 的發動機可能是由位於德國的工廠生產的,它的自動變數箱可能是 Jatco(世界三大變速箱廠商之一)位於日本的工廠生產的,車輪可能是中國的工廠生產的,最後交給華晨寶馬的工廠統一組裝成一輛完整的汽車。這就類似於我們在軟件工程領域裏說的多團隊並行開發,最後將各個團隊開發的模塊統一打包成我們可使用的 App 。

一款發動機、一款變數箱都不可能只應用於一個車型,比如同一款 Jatco 的 6AT 自動變速箱既可能被安裝在 BMW 的車型上,也可能被安裝在 Mazda 的車型上。這就如同軟件開發領域裏的模塊重用。

到了冬天,特別是在北方我們可能需要開着車走雪路,爲了安全起見往往我們會將汽車的公路胎升級爲雪地胎;輪胎可以很輕易的更換,這就是我們在軟件開發領域談到的低耦合。一個模塊的升級替換不會影響到其它模塊,也不會受其它模塊的限制;同時這也類似於我們在軟件開發領域提到的可插拔。

模塊化優缺點

優點

  • 架構靈活,焦點分離
  • 耦合低,模塊間可自由組合、分解
  • 方便單個模塊功能調試、升級、測試,提升開發效率
  • 多人協作只負責單獨模塊,互不干擾

缺點

  • 系統分層,調用鏈會很長
  • 模塊間發送消息對比較損耗性能

模塊化分層

組件模塊區別定義

  • 組件:指的是單一的功能組件,地圖組件、支付組件、分享組件等功能;
  • 模塊:指的是獨立的業務模塊,如首頁模塊、聊天模塊、播放模塊等,模塊相對於組件來說粒度更大。

整個項目分爲四層,從下至上分別是:

(1)宿主層:不做具體的項目功能實現,只負責集成業務模塊,組裝成一個完整的APP;

(2)業務模塊層:將項目的每個大功能模塊拆分成的一個一個單獨的module,可獨立運行,同產品的不同項目也可複用;

(3)業務組件層:用於業務模塊間調用,例如支付組件 、地圖組件、分享組件等等;

(4)基礎組件層:基礎組件層,與業務無關,與項目也無關,所有項目都可以全部複用,包含了各種開源庫以及和業務無關的各種自研工具庫;

模塊化分層.png

模塊化分層,其實就是將業務模塊層的各個功能業務拆分層獨立的業務模塊;進行模塊化的第一步就是業務模塊劃分,劃分的粒度需要根據項目情況進行合理把控,這就需要對業務和項目有較爲透徹的理解。

模塊化過程

1、每個單獨的Module 都可以單獨作爲Application編譯成 APK運行,同時也可以作爲依賴包Library來整體編譯打包。於是,需要在每個Module下的 build.gradle 中加入如下代碼:

if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

isModule作爲開關去控制是否爲Application後者Library,只需要在項目gradle.properties文件中加入如下配置:

isModule=false

2、作爲Application時清單文件需要設置Application屬性及啓動Activity等,而Library則簡單很多,因此就需要兩套不同的AndroidManifest.xml。同樣,只要在每個Module下的 build.gradle的android配置中加入如下設置:

sourceSets {
    main {
        if (isModule.toBoolean()) {
            manifest.srcFile 'src/main/debug/AndroidManifest.xml'
        } else {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            //release模式下排除debug文件夾中的所有Java文件
            java {
                exclude 'debug/**'
            }
        }
    }
}

保持原先拷貝一份AndroidManifest.xml到新建的debug目錄下即可,如圖所示:

image-20201209134330788.png

main目錄下作爲Library清單文件:

<application>
    <activity android:name=".BottomAlignmentActivity" />
</application>

debug目錄下作爲Application中清單文件:

<application
    android:icon="@drawable/ic_author"
    android:label="@string/advancedtextview_app_name"
    android:supportsRtl="true"
    android:theme="@style/Theme.AppCompat.Light.NoActionBar">
    <activity
        android:name=".BottomAlignmentActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

3、宿主APP在Application和Library模式下,進行不同依賴配置,:

if (!isModule.toBoolean()) {
    implementation project(path: ':advancedtextview')
}else{
    implementation project(":libbase")
}

4、模塊間跳轉

宿主工程中依賴的庫是可以直接引用的,通過startActivity跳轉,但組件之間不可以依賴。因此,當常規業務模塊之間需要跳轉引用,改如何處理呢?

  1. 隱式Intent,要跳轉的活動在Manifest.xml中聲明匹配規則,然後調用
Intent intent = new Intent(Intent.ACTION_VIEW, "<scheme>://<host>:<port>/<path>");
startActivity(intent);
  1. 利用反射
Class clazz=Class.fromName("com.nianlun.expample.MainActivity");
startActivity(this,clazz);
  1. 使用路由,這裏推薦阿里的ARouter

一個用於幫助 Android App 進行組件化改造的框架 —— 支持模塊間的路由、通信、解耦。

Arouter使用

  1. 添加依賴和配置

    android {
        defaultConfig {
            ...
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [AROUTER_MODULE_NAME: project.getName()]
                }
            }
        }
    }
    
    dependencies {
        api 'com.alibaba:arouter-api:x.x.x'
        annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
        ...
    }
    
  2. 添加註解

    // 在支持路由的頁面上添加註解(必選)
    // 這裏的路徑需要注意的是至少需要有兩級,/xx/xx
    @Route(path = "/test/activity")
    public class YourActivity extend Activity {
        ...
    }
    
  3. 初始化SDK

    if (isDebug()) {           // 這兩行必須寫在init之前,否則這些配置在init過程中將無效
        ARouter.openLog();     // 打印日誌
        ARouter.openDebug();   // 開啓調試模式(如果在InstantRun模式下運行,必須開啓調試模式!線上版本需要關閉,否則有安全風險)
    }
    ARouter.init(mApplication); // 儘可能早,推薦在Application中初始化
    
  4. 發起路由操作

    // 1. 應用內簡單的跳轉(通過URL跳轉在'進階用法'中)
    ARouter.getInstance().build("/test/activity").navigation();
    
    // 2. 跳轉並攜帶參數
    ARouter.getInstance().build("/test/1")
                .withLong("key1", 666L)
                .withString("key3", "888")
                .withObject("key4", new Test("Jack", "Rose"))
                .navigation();
    

更多進階用法,可以跳轉官方GitHub網站進行文檔查看:https://github.com/alibaba/ARouter/blob/master/README_CN.md

問題解決

資源名衝突的問題,可以通過在 build.gradle 定義前綴的方式解決:

defaultConfig {
   ...
   resourcePrefix "module_name_"
   ...
}

參考資料:Android 模塊化探索與實踐

以上就是模塊化的基本過程和實現,具體項目中我們還是要理清業務之間的關係,進行具體劃分,提取公共依賴,剔除冗餘代碼,逐步進行重構。

借用以前的代碼例子,模塊化處理了一下,採用Arouter進行跳轉,具體可以查看我的Github查看,地址如下

https://github.com/MickJson/DevelopmentRecord

歡迎點擊查閱及Star,我也會繼續補充其它有用的知識及例子在項目上。

歡迎點贊/評論,你們的贊同和鼓勵是我寫作的最大動力!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章