Android組件化實(shí)現(xiàn)方案(一)

這里的組件化每個(gè)模塊可以單獨(dú)運(yùn)行描滔、打包、測試踪古,可隨意拆卸含长、隨意組裝,既不互相依賴又可以互相調(diào)用伏穆。是通過在一個(gè)Project下通過創(chuàng)建多個(gè)Module實(shí)現(xiàn)的拘泞。
假設(shè)三個(gè)模塊:App(可以認(rèn)為是首頁或是空殼)、ShoppingCar枕扫、Order陪腌。

問題一:每個(gè)Module下都有build.gradle,怎么統(tǒng)一管理版本號和引入包烟瞧?

  1. 在Project目錄下創(chuàng)建config.gradle诗鸭,并且在Project目錄下的build.gradle中引入:
// Top-level build file where you can add configuration options common to all sub-projects/modules.

apply from: "config.gradle"

buildscript {
   ...
}

allprojects {
   ...
}

...
  1. config.gradle中統(tǒng)一管理:
// 添加多個(gè)自定義屬性,可以通過ext代碼塊
ext {

    androidId = [
            compileSdkVersion: 28,
            buildToolsVersion: "29.0.0",
            minSdkVersion    : 21,
            targetSdkVersion : 28,
            versionCode      : 1,
            versionName      : "1.0"
    ]

    appId = [
            app    : "com.yu.modular",
    ]


    supportLibrary = "28.0.0"
    dependencies = [
            "appcompat" : "com.android.support:appcompat-v7:${supportLibrary}",
            "recyclerview" : "com.android.support:recyclerview-v7:${supportLibrary}",
            "constraint" : "com.android.support.constraint:constraint-layout:1.1.3"
    ]
}
  1. app的build.gradle中使用:
apply plugin: 'com.android.application'

// 賦值與引用
def androidId = rootProject.ext.androidId
def appId = rootProject.ext.appId
def support = rootProject.ext.dependencies

android {
    compileSdkVersion androidId.compileSdkVersion
    buildToolsVersion androidId.buildToolsVersion
    defaultConfig {
        applicationId appId.applicationId
        minSdkVersion androidId.minSdkVersion
        targetSdkVersion androidId.targetSdkVersion
        versionCode androidId.versionCode
        versionName androidId.versionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    // 標(biāo)準(zhǔn)寫法
    // implementation group: 'com.android.support', name:'appcompat-v7', version:'28.0.0'
    // 簡寫
    // implementation 'com.android.support:appcompat-v7:28.0.0'

    // 依賴第三方庫最簡潔的方式:
    support.each { k, v -> implementation v } // 上面已經(jīng)定義過support参滴,是在Project的config.gradle中統(tǒng)一管理的

}

其他的Module的build.gradle中也是一樣用强岸,只是如果是library的話不需要applicationId

問題二:怎么組合Module? 怎么單獨(dú)運(yùn)行Module卵洗?

如果創(chuàng)建Module的是Phone & Tablet Module请唱, 那么這個(gè)Module是可以單獨(dú)運(yùn)行的,但是怎么集成進(jìn)app过蹂?這樣的Module直接implementation會報(bào)循環(huán)引用的錯(cuò)誤十绑。
如果創(chuàng)建Module的是Android Library,那這個(gè)Module是可以在app中引入的酷勺,但是這樣的Module如何獨(dú)立運(yùn)行本橙?

這里就需要?jiǎng)討B(tài)去切換Module的類型。對比Phone Module和Android Library的build.gradle可看出不同處只有兩點(diǎn):

  • Phone Module中:apply plugin: 'com.android.application'脆诉,而Android Library中:apply plugin: 'com.android.library'
  • Phone Module有applicationId甚亭,而Android Library沒有。

要完成在Module的類型切換击胜,只需要?jiǎng)討B(tài)修改這兩個(gè)地方亏狰。
config.gradle中定義一個(gè)標(biāo)簽用來標(biāo)示組件化還是集成化,并且統(tǒng)一管理Module的applicationId:

ext {
    // false: 組件化模式(子模塊可以獨(dú)立運(yùn)行)偶摔,true :集成化模式(打包整個(gè)項(xiàng)目apk暇唾,子模塊不可獨(dú)立運(yùn)行)
    isRelease = false
    ···
    appId = [
            app      : "com.yu.modular",
            order    : "com.yu.order",
            shopping : "com.yu.shopping"
    ]
    ...
}

app中不需要修改,無論什么環(huán)境下都是Phone Module辰斋,下面以order模塊的build.gradle為例策州,按上面兩個(gè)不同點(diǎn),這里需要修改一下兩處:

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

...
def appId = rootProject.ext.appId

android {

    ...

    defaultConfig {
        if (!isRelease) {
            applicationId appId.order
        }
        ...
    }

    ...

}

...

ShoppingCar也需要做同樣修改宫仗,然后在app的build.gradle中加入:

apply plugin: 'com.android.application'

...

dependencies {
    ...

    if (isRelease) {
        implementation project(':order')
        implementation project(':shoppingcar')
    }
}

這里有兩點(diǎn)需要注意:

  • 創(chuàng)建Module的時(shí)候建議選擇Phone & Tablet Module够挂,如果選擇的是Android Library,AndroidManifest文件和res目錄內(nèi)容不全藕夫,需要手動修改孽糖。
  • Module中java和res目錄下的命名前面盡量加上Module名,類似:Order_MainActivity.java汁胆、order_activity_main.xml這樣梭姓,方便區(qū)分。

完成以上操作嫩码,只要修改config.gradle中的isRelease誉尖,就可以實(shí)現(xiàn)集成化和插件化的切換了。isRelease=true時(shí)铸题,打包出來是一個(gè)apk铡恕,除app外其他的module不可以單獨(dú)運(yùn)行;isRelease=false時(shí)丢间,打包出來有幾個(gè)module做過上述配置就有幾個(gè)apk探熔,所有的module都可以單獨(dú)運(yùn)行。

isRelease = true烘挫,集成化

isRelease = false诀艰,插件化

問題三:Debug時(shí)候的一些類并不想在Release的時(shí)候打包進(jìn)去柬甥,怎么去隔離這些文件?比如單獨(dú)模塊提供給測試的時(shí)候其垄,需要有個(gè)Activity去模擬觸發(fā)其他模塊傳遞過來的操作苛蒲,而這個(gè)Activity打包的時(shí)候并不需要。

這里需要為這些module在不同環(huán)境下創(chuàng)建不同的AndroidManifest文件绿满,并且設(shè)置Release環(huán)境下的過濾目錄臂外,以O(shè)rder模塊為例,在Order的build.gradle文件中添加:

...

android {

    ...

    // 配置資源路徑喇颁,方便測試環(huán)境漏健,打包不集成到正式環(huán)境
    sourceSets {
        main {
            if (!isRelease) {
                // 如果是組件化模式,需要單獨(dú)運(yùn)行時(shí)
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                // 集成化模式橘霎,整個(gè)項(xiàng)目打包apk
                manifest.srcFile 'src/main/AndroidManifest.xml'
                java {
                    // release 時(shí) debug 目錄下文件不需要合并到主工程
                    exclude '**/debug/**'
                }
            }
        }
    }

}

...

對應(yīng)創(chuàng)建出文件和目錄:


這樣在debug包下的文件蔫浆,就不會打包進(jìn)Release時(shí)的apk中,如果是Activity姐叁,也只需要配置Debug環(huán)境下的AndroidManifest就可以克懊。這里就不貼圖了,可以在打包出來的apk中驗(yàn)證下(驗(yàn)證時(shí)關(guān)閉分包更好找一些)七蜘。

問題四:公共基礎(chǔ)庫怎么引用谭溉,比如圖片下載,網(wǎng)絡(luò)請求等橡卤?

這里沒什么特殊處理扮念,新創(chuàng)建一個(gè)Android Library,比如:common碧库,然后再需要用到的module的build.gradle中跟引入其他三方庫一樣柜与,添加:

..
dependencies {
    ...

    implementation project(':common') // 公共基礎(chǔ)庫
}

問題五:集成化的Module之間怎么交互?

集成化環(huán)境下app模塊可以調(diào)用其他的模塊嵌灰,因?yàn)?code>isRelease==true時(shí)會引入其他模塊:

    if (isRelease) {
        implementation project(':order')
        implementation project(':shoppingcar')
    }

但是其他模塊之間的互相調(diào)用呢弄匕?
EventBus?很雜沽瞭,不方便管理迁匠。
反射?高版本api有可能被@hide驹溃。
隱示意圖城丧?廣播? 都不夠舒服豌鹤,沒人愿意這么寫亡哄。
先看下面兩種實(shí)現(xiàn)方式

  1. 用類加載
Class clazz = Class.forName("com.yu.modular.shoppingcar.ShoppdingCar_MainActivity");
Intent intent = new Intent(this, clazz);
startActivity(intent);

這樣寫很容易寫錯(cuò)先不說,一旦一模塊修改了包名或類名布疙,其他模塊都要改蚊惯,很難維護(hù)愿卸。

  1. 在公共庫common中對Activity進(jìn)行統(tǒng)一管理,因?yàn)楦髂K都引入了公共庫common截型,所以都可以對common進(jìn)行操作擦酌。

公共庫的PathManager:

public class PathManager {

    /**
     *
     * @param groupName 組名,如:"order"
     * @param pathName  路勁名菠劝,如:"Order_MainActivity"
     * @param clazz     類對象,如:Order_MainActivity.class
     */
    public static void set(String groupName, String pathName, Class<?> clazz) {
        ...
    }

    /**
     *
     * @param groupName 組名
     * @param pathName  路徑名
     * @return 跳轉(zhuǎn)目標(biāo)的class類對象
     */
    public static Class<?> get(String groupName, String pathName) {
        ...
    }
    ...
}

以什么形式存睁搭?
這里把Activity路徑和類封裝成一個(gè)Bean去儲存:

public class PathBean {
    private String path;
    private Class clazz;

    public PathBean(String path, Class clazz) {
        this.path = path;
        this.clazz = clazz;
    }

    ...
}

Map里的存儲結(jié)構(gòu)類似這樣:

[
  app : [app_PathBean1,  app_PathBean2, app_PathBean3],
  shoppingCar: [shoppingCar_PathBean1,  shoppingCar_PathBean2, shoppingCar_PathBean3],
  order: [order_PathBean1,  order_PathBean2, order_PathBean3]
]

什么時(shí)候去存赶诊?如果在Activity中去存肯定是來不及的,所以app(主模塊)的Application的onCreate方法中去存:

public class AppApplication extends BaseApplication {
    @Override
    public void onCreate() {
        super.onCreate();

        PathManager.set("app", "MainActivity", MainActivity.class);
        PathManager.set("shoppingcar", "ShoppingCar_MainActivity", MainActivity.class);
        PathManager.set("order", "Order_MainActivity", MainActivity.class);
    }
}

跳轉(zhuǎn)時(shí)這樣調(diào)用:

    Class clazz = PathManager.get("order", "Order_MainActivity");
    Intent intent = new Intent(this, clazz);
    startActivity(intent);

這樣就完成集成化模塊間通訊了园骆。
項(xiàng)目地址

手動去管理這個(gè)存儲的Map還是不太舒服舔痪,有沒有什么辦法可以自動生成?
有锌唾,使用APT+JavaPoet技術(shù)锄码。Android組件化實(shí)現(xiàn)方案(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市晌涕,隨后出現(xiàn)的幾起案子滋捶,更是在濱河造成了極大的恐慌,老刑警劉巖余黎,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件重窟,死亡現(xiàn)場離奇詭異,居然都是意外死亡惧财,警方通過查閱死者的電腦和手機(jī)巡扇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垮衷,“玉大人厅翔,你說我怎么就攤上這事〔笸唬” “怎么了刀闷?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長仰迁。 經(jīng)常有香客問我涩赢,道長,這世上最難降的妖魔是什么轩勘? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任筒扒,我火速辦了婚禮,結(jié)果婚禮上绊寻,老公的妹妹穿的比我還像新娘花墩。我一直安慰自己悬秉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布冰蘑。 她就那樣靜靜地躺著和泌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪祠肥。 梳的紋絲不亂的頭發(fā)上武氓,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音仇箱,去河邊找鬼县恕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛剂桥,可吹牛的內(nèi)容都是我干的忠烛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼权逗,長吁一口氣:“原來是場噩夢啊……” “哼美尸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起斟薇,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤师坎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后堪滨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屹耐,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年椿猎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惶岭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡犯眠,死狀恐怖按灶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情筐咧,我是刑警寧澤鸯旁,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站量蕊,受9級特大地震影響铺罢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜残炮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一韭赘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧势就,春花似錦泉瞻、人聲如沸脉漏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侧巨。三九已至,卻和暖如春鞭达,著一層夾襖步出監(jiān)牢的瞬間司忱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工畴蹭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坦仍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓撮胧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親老翘。 傳聞我的和親對象是個(gè)殘疾皇子芹啥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 不怕跌倒,所以飛翔 組件化開發(fā) 參考資源 Android組件化方案 為什么要組件化開發(fā) 解決問題 實(shí)際業(yè)務(wù)變化非常...
    筆墨Android閱讀 2,968評論 0 0
  • 轉(zhuǎn)載請標(biāo)明出處: http://blog.csdn.net/guiying712/article/details/...
    呂志豪閱讀 1,905評論 1 39
  • MVVMHabitComponent 關(guān)于Android的組件化铺峭,相信大家并不陌生墓怀,網(wǎng)上談?wù)摻M件化的文章,多如過江...
    goldze閱讀 5,177評論 2 22
  • 1. Android組件化開發(fā) 在Android項(xiàng)目組件化之前卫键,我們的項(xiàng)目都是像下圖那樣傀履,一個(gè)單一工程下,根據(jù)不同...
    CHSmile閱讀 4,509評論 1 34
  • ——讀《張幼儀傳》有感 讀這本書莉炉,從開始到大半數(shù)钓账,我都為張幼儀感到不值。身為徐志摩的原配妻子絮宁,她為徐志摩奉獻(xiàn)了一生...
    1704李鑫閱讀 536評論 0 0