Android項(xiàng)目模塊化/組件化開發(fā)(非原創(chuàng))

文章大綱

一、項(xiàng)目模塊化初步介紹
二、項(xiàng)目模塊化的兩種模式與比較
三、大型項(xiàng)目模塊化的演進(jìn)
四、項(xiàng)目模塊化總結(jié)
五、參考文章

一、項(xiàng)目模塊化初步介紹

1. 前言

在Android開發(fā)中,隨著項(xiàng)目的不斷擴(kuò)展,項(xiàng)目會(huì)變得越來越龐大,而隨之帶來的便是項(xiàng)目維護(hù)成本與開發(fā)成本的增加!每次調(diào)試時(shí),不得不運(yùn)行整個(gè)項(xiàng)目;每當(dāng)有新成員加入團(tuán)隊(duì)時(shí),需要更多的時(shí)間去了解龐大的項(xiàng)目。。。而為了解決這些問題,團(tuán)隊(duì)通常會(huì)將項(xiàng)目模塊化,以此來降低項(xiàng)目的復(fù)雜度和耦合度,讓團(tuán)隊(duì)可以并行開發(fā)與測(cè)試,讓團(tuán)隊(duì)成員更加專注于自己所負(fù)責(zé)的功能模塊開發(fā)。。。
對(duì)于一些大廠如BAT或者美團(tuán)等這些大型互聯(lián)網(wǎng)公司,都會(huì)自己造輪子,實(shí)現(xiàn)項(xiàng)目模塊化。而對(duì)于中小型公司,限于成本因素,一般都是選用這些大廠造的優(yōu)秀的輪子來進(jìn)行項(xiàng)目模塊化。

2. 模塊化需要做什么

首先,在開始項(xiàng)目模塊化之前,我們必須要明確模塊化需要做些什么?這就等于寫書之前必須得有個(gè)總綱,否則越寫到后面,越是混亂。以下是我認(rèn)為在模塊化時(shí)需要注意的幾個(gè)問題:
(1)如何拆分項(xiàng)目
(2)模塊之間的通信
(3)模塊內(nèi)的代碼隔離
(4)模塊在調(diào)試與發(fā)布模式之間的切換

3. 如何拆分項(xiàng)目

如上圖所示,我將項(xiàng)目大概劃分為五層:
宿主層
不做具體的項(xiàng)目功能實(shí)現(xiàn),只負(fù)責(zé)集成業(yè)務(wù)模塊,組裝成一個(gè)完整的APP
業(yè)務(wù)模塊層
將項(xiàng)目的每個(gè)大功能模塊拆分成的一個(gè)一個(gè)單獨(dú)的module
基礎(chǔ)業(yè)務(wù)組件層
此層最大的作用是為了復(fù)用,例如首頁模塊與新盤模塊中都有樓盤搜索這個(gè)功能,且UI顯示相似,這時(shí)在兩個(gè)模塊中都實(shí)現(xiàn)樓盤搜索就顯得繁瑣了,像這種與業(yè)務(wù)有關(guān)聯(lián)且需要多處使用的情況,我們完全可以將其抽離出來作為基礎(chǔ)業(yè)務(wù)組件
功能組件層
項(xiàng)目中常用的功能庫,如圖片加載、網(wǎng)絡(luò)請(qǐng)求等
底層SDK
從公司項(xiàng)目中長期積累出來的底層類庫
以上是大多數(shù)項(xiàng)目模塊化時(shí)的拆分方式,每個(gè)人也可以根據(jù)項(xiàng)目的實(shí)際情況進(jìn)行調(diào)整。

4. 模塊之間的通信

4.1 常用的通信方式
當(dāng)項(xiàng)目被拆分成多個(gè)模塊后,模塊之間的良好的通信是我們必須考慮的問題。ARouter本身也提供一套通信機(jī)制,但是一般很難滿足我們所有的需求,所以我們會(huì)容易想到的常用的幾種通信方式:EvenBus、協(xié)議通信、廣播或者是將通信的部分下沉到公共組件庫。對(duì)于這幾種方式,在一些大廠的技術(shù)文章中都有提到一些他們的看法,下面我簡單總結(jié)一下:
EventBus: 我們非常熟悉的事件總線型的通信框架,非常靈活,采用注解方式實(shí)現(xiàn),但是難以追溯事件,微信、餓了么認(rèn)為這是個(gè)極大的缺點(diǎn),不是很推薦,但是美團(tuán)覺得只要自身控制的好就行(自己設(shè)計(jì)了一套基于LiveData的簡易事件總線通信框架)。
協(xié)議通信: 通信雙發(fā)必須得都知曉協(xié)議,且協(xié)議需要放在一個(gè)公共部分保存。雖然解耦能力強(qiáng),但是協(xié)議一旦變化,通訊雙方的同步會(huì)變的復(fù)雜,不方便。
廣播: 安卓的四大組件之一,常見的通信方式,但是相對(duì)EventBus來說,過重。
下沉到公共組件庫: 這是在模塊化中常見的做法,不斷的將各種方法、數(shù)據(jù)模型等公共部分下成到公共組件庫,這樣一來,公共組件庫會(huì)變的越來越龐大,越來越中心化,違背了項(xiàng)目模塊化的初衷。最后,越來越難以維護(hù),不得不在重新拆分公共組件庫。

4.2 如何對(duì)外暴露接口
解決了通信手段的問題,我們就得考慮另一個(gè)問題,為其他模塊提供的接口+數(shù)據(jù)結(jié)構(gòu)我們應(yīng)該放在哪里?下沉到公共模塊嗎?或者另外新建一個(gè)module用來維護(hù)這些接口+數(shù)據(jù)結(jié)構(gòu)?但是這樣一來,成本就有些大了,也不方便。

在微信的模塊化文章中提出了一個(gè)解決方法,將你要暴露的接口+數(shù)據(jù)結(jié)構(gòu)甚至其他想要暴露的文件都.api化,即將你要暴露的文件的后綴名改為api,然后通過特定的方法將api后綴的文件拷貝出來,在module外部重新組成一個(gè)新的module(也可稱為SDK),而想要使用的模塊只需要調(diào)用這個(gè)SDK即可。當(dāng)然,拷貝文件和組件SDK是完全自動(dòng)化的,并非手工,這樣才能節(jié)省成本。

由于微信的模塊化文章中沒有涉及到.api化的具體實(shí)現(xiàn),所以根據(jù)這種思路,我使用了其他方法來實(shí)現(xiàn)要達(dá)到的效果。具體思路如下:

創(chuàng)建一個(gè)名為_exports的文件夾,需要對(duì)外暴露的文件都放在里面

將_exports文件夾打包成jar

/**
 * 創(chuàng)建 jar 包
 */
task makeExportJar(type: Jar) {
    baseName = "hpauth-exports"
    version = "1.0.0"
    extension = "jar"
    // java文件編譯后的所在位置
    from('build/intermediates/classes/debug/')
    // kotlin文件編譯后的所在位置
    from('build/tmp/kotlin-classes/debug/')
    include('com/homeprint/module/auth/_exports/**')
    // jar包導(dǎo)出路徑
    destinationDir = file('build/_exports')
}

將jar發(fā)布到本地maven倉庫(發(fā)布到本地僅僅針對(duì)個(gè)人開發(fā)的時(shí)候;團(tuán)隊(duì)開發(fā)時(shí),大家使用各自的電腦,無法訪問到你本地的maven倉庫,所以這時(shí)需要在局域網(wǎng)中建立一個(gè)maven倉庫,詳情請(qǐng)查看《Android:超詳細(xì)的本地搭建maven私服以及使用Nexus3.x搭建maven私服的講解》)

artifacts {
    archives makeExportJar
}

uploadArchives {
    repositories {
        mavenDeployer {
            // 本地的maven倉庫路徑
            repository(url: uri("../repo"))
            pom.project {
                groupId 'com.homeprint.module'
                artifactId 'auth-exports'
                version '1.0.0'
            }
        }
    }
}

在需要的模塊調(diào)用jar

compileOnly 'com.homeprint.module:auth-exports:1.0.0@jar'

注: 此處必須使用compileOnly來調(diào)用,compileOnly是provided的替代方法,provided將被google廢棄。此處使用compileOnly代表,jar包只在編譯時(shí)有效,不參與打包。如果使用api或者implementation,因?yàn)槲覀冎皇菍⑽募截惓鰜沓蔀橐粋€(gè)單獨(dú)的SDK,并未修改包名和文件名,當(dāng)將多個(gè)模塊集成為一個(gè)app時(shí),會(huì)拋出異常,提示jar包中的文件已存在。

5. 組件的生命周期管理

在組件化開發(fā)時(shí),每個(gè)組件都應(yīng)該有自己獨(dú)立的生命周期,這個(gè)生命周期類似于組件自己的Application,在這個(gè)生命周期中,組件可以做一些類庫的初始化等工作,否則如果每個(gè)組件都將這些工作集中到殼工程的Applicaiton中實(shí)現(xiàn)的話,會(huì)顯得殼工程的Application太過中心化,并且一旦需要修改,會(huì)很麻煩,且容易產(chǎn)生沖突。

基于上述原因,我們可以自己搭建一個(gè)簡易的組件生命周期管理器,主要分為兩步:

構(gòu)建組件的生命周期模型,構(gòu)建的模型持有整個(gè)app的Application引用,同時(shí)提供三個(gè)基礎(chǔ)方法:生命周期創(chuàng)建、生命周期停止以及生命周期的優(yōu)先級(jí)設(shè)置。

public abstract class BizLifeCycle {
    private Application app;
    // 優(yōu)先級(jí),priority 越大,優(yōu)先級(jí)越高
    @IntRange(from = 0)
    private int priority = 0;

    public BizLifeCycle(@NonNull Application application) {
        this.app = application;
    }

    public Application getApp() {
        return app;
    }

    /**
     * 獲取優(yōu)先級(jí),priority 越大,優(yōu)先級(jí)越高
     */
    public int getPriority() {
        return priority;
    }

    public void setPriority(int priority) {
        this.priority = priority;
    }

    public abstract void onCreate();

    public abstract void onTerminate();
}

在每個(gè)組件中只要實(shí)現(xiàn)上述模型即可。
有了生命周期模型,我們還需要一個(gè)管理器,用于管理這些組件的生命周期模型,在這個(gè)管理器中,我們同樣需要實(shí)現(xiàn)三個(gè)基礎(chǔ)方法:生命周期模型的注冊(cè),生命周期模型的反注冊(cè)以及執(zhí)行已存在的生命周期模型。

public class BizLifeCycleManager {
    private static ArrayList<BizLifeCycle> sPinLifeCycleList;

    /**
     * 注冊(cè)組件的生命周期
     */
    public static <T extends BizLifeCycle> void register(@NonNull T lifeCycle) {
        if (sPinLifeCycleList == null) {
            sPinLifeCycleList = new ArrayList();
        }
        if (!sPinLifeCycleList.contains(lifeCycle)) {
            sPinLifeCycleList.add(lifeCycle);
        }
    }

    /**
     * 執(zhí)行組件生命周期
     */
    public static void execute() {
        if (sPinLifeCycleList != null && !sPinLifeCycleList.isEmpty()) {
            // 冒泡算法排序,按優(yōu)先級(jí)從高到低重新排列組件生命周期
            BizLifeCycle temp = null;
            for (int i = 0, len = sPinLifeCycleList.size() - 1; i < len; i++) {
                for (int j = 0; j < len - i; j++) {
                    if (sPinLifeCycleList.get(j).getPriority() < sPinLifeCycleList.get(j + 1).getPriority()) {
                        temp = sPinLifeCycleList.get(j);
                        sPinLifeCycleList.set(j, temp);
                        sPinLifeCycleList.set(j + 1, temp);
                    }
                }
            }
            for (BizLifeCycle lifeCycle : sPinLifeCycleList) {
                lifeCycle.onCreate();
            }
        }
    }

    /**
     * 解除組件生命周期
     */
    public static <T extends BizLifeCycle> void unregister(@NonNull T lifeCycle) {
        if (sPinLifeCycleList != null) {
            if (sPinLifeCycleList.contains(lifeCycle)) {
                lifeCycle.onTerminate();
                sPinLifeCycleList.remove(lifeCycle);
            }
        }
    }

    /**
     * 清除所有的組件生命周期
     */
    public static void clear() {
        if (sPinLifeCycleList != null) {
            if (!sPinLifeCycleList.isEmpty()) {
                for (BizLifeCycle lifeCycle : sPinLifeCycleList) {
                    lifeCycle.onTerminate();
                }
            }
            sPinLifeCycleList.clear();
        }
    }
}

調(diào)用示例如下,直接在殼工程的Application中使用,因?yàn)樯芷谀P椭卸荚O(shè)置有優(yōu)先級(jí),所以在設(shè)置了優(yōu)先級(jí)的情況下,可以不必在意register的順序。如此一來,將生命周期注冊(cè)后,需要更改每個(gè)組件件的一些初始化工作,可以直接在組件的生命周期中修改,而不需要變更殼工程的Application。

override fun onCreate() {
        super.onCreate()
        BizLifeCycleManager.register(CommonBizLifeCycle(this))
        BizLifeCycleManager.register(HttpBizLifeCycle(this))
        BizLifeCycleManager.register(RouterBizLifeCycle(this))
        BizLifeCycleManager.register(ThirdBizLifeCycle(this))
        BizLifeCycleManager.execute()
    }

6. 模塊在調(diào)試與發(fā)布模式之間的切換

項(xiàng)目開發(fā)時(shí),一般提供調(diào)試環(huán)境與正式發(fā)布環(huán)境。在不同的環(huán)境中,app的有些功能是不需要用到的,或者是有所不同的。另外,在模塊化開發(fā)時(shí),有些業(yè)務(wù)模塊在調(diào)試時(shí),可以作為單獨(dú)的app運(yùn)行調(diào)試,不必每次都編譯所有的模塊,極大的加快編譯速度,節(jié)省時(shí)間成本?;冢陨戏N種原因,我們就必須對(duì)項(xiàng)目的調(diào)試與正式環(huán)境做不同的部署配置,然而如果全靠每次手動(dòng)修改,當(dāng)模塊量達(dá)到數(shù)十時(shí),則會(huì)非常麻煩,且容易出錯(cuò)。所以我們需要盡可能的用代碼做好配置。

首先,來看如何配置module在app與library之間的切換,實(shí)現(xiàn)module在調(diào)試時(shí)作為app單獨(dú)運(yùn)行調(diào)試??词纠a,這也是比較常見的方式:

在gradle.properties中,配置字段isAuthModule,控制auth模塊是作為一個(gè)模塊還是一個(gè)app

# true: 作為一個(gè)模塊 
# false: 作為一個(gè)app
isAuthModule=true

在auth模塊的build.gradle文件中作如下配置:

// 獲取 gradle.properties 文件中的配置字段值
def isApp = !isAuthModule.toBoolean()

// 判斷是否為 app,選擇加載不同的插件
if (isApp) {
   apply plugin: 'com.android.application'
} else {
   apply plugin: 'com.android.library'
}
......

android {
   defaultConfig {
        // library 是沒有 applicationId 的,只有為 app 時(shí),才有
         if (isApp) {
             applicationId "com.homeprint.module.auth"
         }
         ......
   }
   
   sourceSets {
          main {
              // 作為app與模塊時(shí)的AndroidManifest.xml會(huì)有所不同,在不同狀態(tài)時(shí)選擇不同的AndroidManifest.xml
              if (isApp) {
                  manifest.srcFile 'src/main/AndroidManifest.xml'
              } else {
                  // 記得在 main 文件夾下創(chuàng)建 module 文件夾,添加AndroidManifest.xml
                  manifest.srcFile 'src/main/module/AndroidManifest.xml'
          }
   }
}

這樣只要我們更改一下,gradle.properties文件中的配置字段,就可以自由實(shí)現(xiàn)module在模塊與app之間的切換。下面我們?cè)賮砜聪拢绾螌?shí)現(xiàn)app在調(diào)試與發(fā)布環(huán)境時(shí),加載不同的模塊,看以下示例:

假如有兩個(gè)模塊,lib_debug 與 lib_release,lib_debug 是只有在調(diào)試環(huán)境才需要使用,
lib_release 是只有在正式環(huán)境才需要使用,以下提供兩種方式實(shí)現(xiàn)

方式一:
 if(mode_debug){
     implementation project(':lib_debug')
 }else{
     implementation project(':lib_release')
 }


方式二:
 debugImplementation project(':lib_debug')
 releaseImplementation project(':lib_release')

以上兩種方式,方式一類似于上述的模塊在 app 與 library 之間的切換,方式二是使用 gradle 提供的方法實(shí)現(xiàn)

7. maven私服的講解與項(xiàng)目上傳與引用

https://blog.csdn.net/liyi1009365545/article/details/84766956

二、項(xiàng)目模塊化的兩種模式與比較

目前項(xiàng)目模塊化大體可以分為兩種模式,分別是submodule和multi-project。根據(jù)字面意思,我們就可以很容易理解這兩種模式,下面就讓我們來具體了解一下這兩種模式!

1. submodule模式

如上圖所示,項(xiàng)目中只有一個(gè)project工程,在project中構(gòu)建多個(gè)module組件,每個(gè)module都有自己的git倉庫,非常直觀,這也是我們最常見的模塊化架構(gòu)。

優(yōu)點(diǎn)
架構(gòu)直觀,可以讓加入開發(fā)的新成員比較快速的理解項(xiàng)目的構(gòu)建
團(tuán)隊(duì)協(xié)作靈活,在項(xiàng)目開發(fā)階段(特別是起始不穩(wěn)定的階段),有更多的module依賴選擇,例如直接依賴project,或者通過aar/jar依賴,或者是maven依賴,可以更加快速的進(jìn)行開發(fā)調(diào)試

缺點(diǎn)
整個(gè)project的git分支會(huì)很復(fù)雜
團(tuán)隊(duì)協(xié)作的時(shí)候,大家都是在同一個(gè)app模塊中做測(cè)試自己開發(fā)的模塊,比較容易產(chǎn)生沖突
因?yàn)樗械膍odule都在一個(gè)project中,每個(gè)人都可以修改他人負(fù)責(zé)的module,不是很安全

2. multi-project模式

如上圖所示,這種模式是將每個(gè)功能模塊都拆分成一個(gè)單獨(dú)的功能project,每個(gè)功能project都至少包含自己測(cè)試使用的app模塊和功能模塊這兩個(gè)module。此外,還有一個(gè)專門的project用來作為app殼工程,組合所有的功能模塊。這樣每個(gè)project都有自己的單獨(dú)的git倉庫和單獨(dú)的測(cè)試使用的app模塊。

優(yōu)點(diǎn)
每個(gè)project有自己單獨(dú)的git倉庫,減少項(xiàng)目的git復(fù)雜度
每個(gè)project有自己的app模塊用于測(cè)試,避免影響他人
開發(fā)成員只需要負(fù)責(zé)自己的project,不需要關(guān)注其他的功能模塊,更加專注
開發(fā)成員只能關(guān)注自己負(fù)責(zé)的功能模塊,無法修改到其他人負(fù)責(zé)的功能模塊,更加安全
更加解耦

缺點(diǎn)
對(duì)于新加入的開發(fā)成員不是很友好,不能直觀的了解項(xiàng)目的構(gòu)建
需要在項(xiàng)目開發(fā)前達(dá)成一些規(guī)范協(xié)議,否則在協(xié)作時(shí)容易產(chǎn)生沖突,比如資源文件的命名,如果在兩個(gè)module中出現(xiàn)命名一樣的資源文件,則會(huì)報(bào)錯(cuò)
因?yàn)槊總€(gè)功能都是單獨(dú)的project,所以開發(fā)調(diào)式時(shí),只能使用aar/jar或者maven來依賴需要的module,不如submodule模式靈活,在項(xiàng)目初期不穩(wěn)定時(shí)的開發(fā)成本要高于submodule模式
會(huì)增大項(xiàng)目的體積

3. 建議

(1)在個(gè)人開發(fā)或者小團(tuán)隊(duì)開發(fā)時(shí),沒有必要使用multi-project模式,成本太高
(2)開發(fā)小項(xiàng)目時(shí),submodule模式足以,如果項(xiàng)目后期變的越來越大,可以在轉(zhuǎn)multi-project模式
(3)在項(xiàng)目前期不穩(wěn)定時(shí),如果要使用multi-project模式架構(gòu),對(duì)于基礎(chǔ)組件與公共組件部分盡可能的要考慮完善,否則每次更改都需要重新發(fā)布aar/jar或者上傳到maven倉庫中,成本較高(畢竟Android Studio的運(yùn)行速度有時(shí)確實(shí)不盡人意)

三、大型項(xiàng)目模塊化的演進(jìn)

1. 美團(tuán)外賣Android平臺(tái)化架構(gòu)演進(jìn)實(shí)踐

https://tech.meituan.com/2018/03/16/meituan-food-delivery-android-architecture-evolution.html

2. 微信Android模塊化架構(gòu)重構(gòu)實(shí)踐

https://mp.weixin.qq.com/s/6Q818XA5FaHd7jJMFBG60w

四、項(xiàng)目模塊化總結(jié)

今天我們講的主題是基于項(xiàng)目模塊化來說的,模塊化是什么大家肯定都是知道了的。我們說,做模塊化其實(shí)跟項(xiàng)目重構(gòu)很像,都是從這幾個(gè)點(diǎn)來做的,只是側(cè)重點(diǎn)不同。分別是:刪除、組織、降級(jí)、解耦。

模塊化重構(gòu)

刪除:刪除不必要的文件,盡可能減小工程體積。這里有一組數(shù)據(jù),是我統(tǒng)計(jì)我們餓了么的一款 APP 在模塊化前后一些文件的數(shù)量。
可以看到,.java文件從1677個(gè)減少到了1543個(gè)。其實(shí)這不是重點(diǎn),重點(diǎn)是下面的drawable,這里drawable只包含圖片、和xml布局,當(dāng)經(jīng)過模塊化重構(gòu)后文件數(shù)從 693 減少到 538 個(gè)。圖片資源減少接近 200 個(gè),apk 的大小也會(huì)隨之降低。

而組織呢,指的是:按照有意義的標(biāo)準(zhǔn)將代碼分組。這其實(shí)也是java的包所存在的目的之一。
但是隨著項(xiàng)目的不斷迭代,需求很緊的情況下是很難有時(shí)間去真正規(guī)范的將類分組的。看到圖中,我們之前的結(jié)構(gòu)很亂,就是因?yàn)轫?xiàng)目快速迭代和人員更替的過程中,不免會(huì)有這樣的現(xiàn)象。所以這也是模塊化重構(gòu)時(shí)所作的一件大事。

接下來就是我們經(jīng)常說的內(nèi)聚和耦合了,降級(jí)。我們之前有一個(gè)類叫:Navigator,它是負(fù)責(zé)幾乎所有Activity直接跳轉(zhuǎn)的。就是我們會(huì)把所有的startActivity()的跳轉(zhuǎn)放到這個(gè)類里面去寫。之前少的時(shí)候還好,結(jié)果等我看到這個(gè)類的時(shí)候,這個(gè)類已經(jīng)有 200 多個(gè)方法了,全是Activity跳轉(zhuǎn)的方法。

而我們?cè)谧瞿K化重構(gòu)時(shí)的做法就是,首先觀察自己的項(xiàng)目,這是重構(gòu)很重要的一步,就是要結(jié)合自身。把這個(gè)類拆分成了三大部分,我們有兩塊業(yè)務(wù)是會(huì)頻繁跳轉(zhuǎn)的但這兩個(gè)業(yè)務(wù)跳轉(zhuǎn)的頁面又都是在自身的模塊內(nèi),分別是用戶模塊和商戶模塊。因此將這兩個(gè)模塊中分別建立兩個(gè)用于模塊自己內(nèi)部的跳轉(zhuǎn)叫UserNavigator和ShopNavigator,而模塊間的跳轉(zhuǎn)或一些小模塊內(nèi)部的則使用Router去做。

之后解耦,也是今天的重點(diǎn),如何優(yōu)雅移除模塊間的耦合。
到目前為止,我們已經(jīng)能夠做到讓所有不包含業(yè)務(wù)狀態(tài)接口的模塊的增刪,不需要改動(dòng)任何一行代碼。
具體到一個(gè)示例就是這樣:

或者,也可以是這樣:

這兩個(gè)段代碼的區(qū)別就是一個(gè)是手動(dòng)管理Debug的狀態(tài),另一個(gè)是交給Gradle的編譯任務(wù)去控制,原理上是一樣的。
而這么做是如何實(shí)現(xiàn)的呢,其思路就是:一個(gè)模塊就是一個(gè)功能,你想要讓你的 apk 具備這個(gè)功能,就添加這個(gè)模塊一起編譯就可以了。這才是我們說的真正的組件化,模塊之間零耦合,增減模塊零改動(dòng)。
例如圖中:debug這個(gè)模塊,肯定不會(huì)用在正式的生產(chǎn)環(huán)境;而相反的tinker這個(gè)模塊,熱補(bǔ)丁肯定也不會(huì)用于調(diào)試階段。所以我在開發(fā)時(shí)就可以不使用這個(gè)模塊相關(guān)的代碼。
另外再舉個(gè)使用的例子:我有一個(gè)訂單模塊,訂單模塊需要播放鈴聲,比如大家在飯店經(jīng)常聽到“您有新的餓了么訂單,請(qǐng)及時(shí)處理”。但我在開發(fā)訂單模塊的時(shí)候,如果我已經(jīng)確定鈴聲播放是沒有問題的,那我可以選擇開發(fā)階段不打鈴聲的包,直到發(fā)布到線上了再去加上鈴聲的包。那我沒有添加這個(gè)鈴聲模塊的時(shí)候,我就默認(rèn)不具備播放鈴聲的功能,但完全不影響其他的訂單模塊的業(yè)務(wù)功能,而這個(gè)鈴聲模塊的增刪,是不需要修改任何代碼的。
聽到這里相信大家都很好奇這是怎么實(shí)現(xiàn)的。接下來就跟大家講講內(nèi)部的原理。

鐵金庫解耦
所有的核心功能都來自我們自己寫的一個(gè)庫:IronBank。取《自冰與火之歌》中的【鐵金庫】,叫鐵金庫不容拖欠。
鐵金庫的內(nèi)部實(shí)現(xiàn),其實(shí)是使用了 APT 注解處理器,去在編譯時(shí)解析注解生成一個(gè)類,讓這個(gè)類去生成跨模塊的對(duì)象。鐵金庫使用了與后端 SOA 設(shè)計(jì)思路類似的方式:將模塊之間的主動(dòng)依賴倒置,變?yōu)楣δ艿奶峁┡c使用。

例如圖上左邊有一個(gè)對(duì)外提供媒體功能的服務(wù)提供者,他告知IronBank我提供媒體服務(wù):“嘿,老鐵,我這有個(gè)媒體服務(wù),你那邊有誰要用的時(shí)候可以用我的?!?br> 到了另一邊,如果此刻有模塊說是,我需要媒體服務(wù):“老鐵,你那有沒有媒體服務(wù),我這邊需要播一個(gè)鈴聲啊!”。
“有的,給你?!?br> IronBank就會(huì)將之前服務(wù)提供者提供給他的媒體對(duì)象交給服務(wù)使用者。

五、參考文章

  1. https://blog.csdn.net/liyi1009365545/article/details/84032509
  2. https://blog.csdn.net/liyi1009365545/article/details/84766956
  3. https://blog.csdn.net/liyi1009365545/article/details/85853027
  4. https://tech.meituan.com/2018/03/16/meituan-food-delivery-android-architecture-evolution.html
  5. https://mp.weixin.qq.com/s/6Q818XA5FaHd7jJMFBG60w
  6. https://xiaozhuanlan.com/topic/3629451870
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容