Android組件化架構(gòu)

什么是組件化

組件(Component)是對數(shù)據(jù)和方法的簡單封裝碍沐,功能單一狸捅,高內(nèi)聚,并且是業(yè)務(wù)能劃分的最小粒度累提。

組件化是基于組件可重用的目的上尘喝,將一個大的軟件系統(tǒng)按照分離關(guān)注點的形式,拆分成多個獨立的組件斋陪,使得整個軟件系統(tǒng)也做到電路板一樣朽褪,是單個或多個組件元件組裝起來置吓,哪個組件壞了,整個系統(tǒng)可繼續(xù)運行缔赠,而不出現(xiàn)崩潰或不正逞苊現(xiàn)象,做到更少的耦合和更高的內(nèi)聚嗤堰。

區(qū)分模塊化與組件化
  • 模塊化
    模塊化就是將一個程序按照其功能做拆分戴质,分成相互獨立的模塊,以便于每個模塊只包含與其功能相關(guān)的內(nèi)容踢匣,模塊我們相對熟悉,比如登錄功能可以是一個模塊,搜索功能可以是一個模塊等等置森。

  • 組件化
    組件功能單一,高內(nèi)聚符糊,是業(yè)務(wù)能劃分的最小粒度凫海。組件化就是更關(guān)注可復用性,更注重關(guān)注點分離男娄,如果從集合角度來看的話行贪,可以說往往一個模塊包含了一個或多個組件,或者說模塊是一個容器模闲,由組件組裝而成建瘫。

組件可以單獨成獨立項目進行編譯運行,通常每個組件由單人或者固定人員負責尸折,別人可能不清楚這個組件下的業(yè)務(wù)及代碼啰脚,因為這些組件可以做成遠程倉庫依賴的方式。

組件化優(yōu)勢

1.提高編譯速度实夹,從而提高并行開發(fā)效率
2.每個組件有自己獨立的版本橄浓,可以獨立編譯、測試亮航、打包和部署
3.避免模塊之間的交叉依賴荸实,做到低耦合、高內(nèi)聚
4.組件之間可以靈活組建缴淋,快速生成不同類型的定制產(chǎn)品准给,可拆可裝

組件化需要考慮問題

  • 組件分層
    如何將龐大工程分成有機整體。


  • 組件之間跳轉(zhuǎn)

組件之間是無法相互引用的重抖,所以做跳轉(zhuǎn)和通信需要做處理露氮,實現(xiàn)辦法就是路由,業(yè)務(wù)組件之間相互通信钟沛,需要訪問基礎(chǔ)組件中的路由框架畔规,路由來尋址找到目標頁面或者目標功能。

什么是路由讹剔?
app的一個頁面就可以類比于一個個網(wǎng)站里面的頁面油讯,瀏覽器的每個頁面由url定義详民,給不同url傳遞不同參數(shù),頁面的表現(xiàn)形式還稍有不通過陌兑,這里的映射關(guān)系就是url對應(yīng)頁面沈跨,每個app的每個頁面也可以類比于網(wǎng)站的頁面,那是不是可以采用url的方式來定義每個頁面呢兔综?這樣是不是也就有了url對應(yīng)app頁面的映射關(guān)系饿凛,如果有了這樣的映射關(guān)系,給定一個url软驰,那是不是就可以知道跳轉(zhuǎn)到某一個具體的Activity了涧窒?Android路由也是一個映射表,用來映射Uri和對應(yīng)的頁面跳轉(zhuǎn)锭亏,這個url就是組件名+頁面名來拼接纠吴。

我這里做跳轉(zhuǎn)用的是ARouter。這里是我的另一篇ARouter解析慧瘤。

  • 組件化解決重復依賴

組件從開始設(shè)計的時候就需要嚴格分好依賴層級戴已,組件之間不可相互依賴,不可重復依賴锅减,業(yè)務(wù)組件只可依賴必須的base組件糖儡。主app殼組件依賴其他所有組件。

  • 組件單獨運行和集成測試

只需要把 Apply plugin: 'com.android.library' 切換成Apply plugin: 'com.android.application' 怔匣。我們可以通過 Gradle腳本配置方式握联,修改properties配置可讓某個組件單獨運行。比如使用isDebug變量對兩種引用插件進行選擇每瞒。

集成測試需要空manifest金闽,獨立運行需要完善manifest文件,需要用gradle配置進行2個manifest文件切換独泞。


然后將公共配置的gradle代碼抽出配置文件進行apply使用呐矾。

  • 組件化時資源名沖突

color,shape懦砂,drawable,圖片資源组橄,布局資源荞膘,或者anim資源等等,都有可能造成資源名稱沖突玉工。大家都在不同組件下羽资,通常不會交流,有可能造成沖突遵班,所以在項目創(chuàng)建初期屠升,需要定義好公共資源潮改,很少修改。在版本不斷升級腹暖,業(yè)務(wù)不斷復雜汇在,肯定還是避免不了不同的資源文件,所以需要要有按模塊區(qū)分命名規(guī)則脏答。

  • 組件之間相互調(diào)用

組件之間相互通信是少不了的糕殉,各組件間不能直接調(diào)用。組件之間的交互如果還是直接引用的話殖告,那么組件之間根本沒有做到解耦阿蝶。

需要暴露功能給別的組件調(diào)用的組件,需要在公共模塊base里面去聲明接口黄绩,繼承ARouter庫中的Iprovider接口羡洁。然后在自己模塊中實現(xiàn)該接口的功能。別的模塊直接調(diào)用該暴露的接口而實現(xiàn)功能爽丹。

在公共模塊給各個組件定義一個包焚廊,里面創(chuàng)建需要提供給外部使用的接口SwitchFarmProvider,需要繼承ARouter提供的IProvider接口习劫。

interface SwitchFarmProvider: IProvider {

    fun showSwitchFarmDialog(pos: Int, fragmentManager: FragmentManager)

}

然后在自己組件中去實現(xiàn)該方法的功能咆瘟,這樣就能提供給外部組件調(diào)用內(nèi)部的方法,而不用將該功能將低到公共組件中诽里。

@Route(path = "/xxx/xxx", name = "xxx")
class SwitchFarmImp: SwitchFarmProvider {

    override fun showSwitchFarmDialog(pos: Int, fragmentManager: FragmentManager) {
        SwitchFarmAllDialog().switchTab(pos).show(fragmentManager, "")
    }

    override fun init(context: Context?) {

    }
}

調(diào)用方式袒餐,通過ARouter提供的ARouter.getInstance().navigation(Class)方法獲取該實現(xiàn)類,調(diào)用其公共方法谤狡。

ARouter.getInstance().navigation(SwitchFarmProvider.class).showSwitchFarmDialog(2, getFragmentManager());
  • 將組件發(fā)布到遠程倉庫

不同部門的開發(fā)分工更加明確之后灸眼,不屬于自己維護的組件范圍不能隨意地修改∧苟基本上自己負責自己模塊下的組件焰宣,盡可能少地改動別的組件代碼。這一塊的配置是全文終點捕仔,敲黑板了匕积。

我這里將各個組件發(fā)布到阿里云 maven庫中,發(fā)布方法見我另一篇文章——發(fā)布開源庫到阿里云 maven倉庫榜跌。發(fā)布之后闪唆,可以看到遠程倉庫里的庫。這里需要注意的是钓葫,組件不要依賴本地組件悄蕾,而是從底層開始逐漸依賴,按照依賴順序上傳础浮,否則很可能會依賴錯誤帆调。

然后各個模塊引入庫奠骄,在app下都引入遠程依賴,在settings.gradle中移除各個組件的include番刊。那么項目文件夾就變?yōu)榱藷o本地依賴的狀態(tài):

此時你的項目仍然是能夠運行起來的含鳞,不過編譯運行的代碼就不是你本地的了,而是直接運行各個遠程的aar包撵枢。

那么平時開發(fā)怎么修改我們本地的代碼呢民晒?做配置,可分別設(shè)置某個組件是依賴本地還是遠程锄禽,依賴本地的組件可盡心開發(fā)修改潜必,發(fā)布上傳新的版本。
做法是在各組件下新建gradle.properties讀取里面的配置沃但,比如設(shè)置true表示依賴遠程磁滚。在settings.gradle中讀取該文件的屬性,看是否需要依賴本地的組件宵晚。在項目的build.gradle中配置垂攘,讀取該true/false屬性,判斷是依賴本地庫還是遠程庫淤刃。

settings.gradle中配置:

includeCompat ':module-play'
includeCompat ':module-notice'
includeCompat ':module-community'
includeCompat ':module-user'
includeCompat ':module-login'
includeCompat ':module-home'
includeCompat ':module-entrance'
includeCompat ':library-res'
includeCompat ':library-network'
includeCompat ':library-base'
includeCompat ':app'
rootProject.name = "MvvmFrame"

def includeCompat(String name) {
    if (!isMaven(name)) {
        include(name)
    }
}

def isMaven(String name) {
    println("isMaven" + name)
    Properties properties = new Properties()
    def file = new File("${name.replace(":", "")}/maven.properties")
    if (file.exists()) {
        InputStream inputStream = file.newDataInputStream()
        properties.load(inputStream)
        def str = properties.getProperty('MAVEN')
        if (str == null) {
            return false
        } else {
            return Boolean.parseBoolean(str)
        }
    }
    return false
}

項目的build.gradle中配置:

    //根據(jù)是否為遠程依賴設(shè)置依賴遠程庫還是本地庫
    ext.projectCompat = { name ->
        def realName = name.replace(":", "")
        if (isMaven(realName)) {
            return "com.libo:${realName}:${mavenVersion(realName)}"
        } else {
            return project(name)
        }
    }

    //判斷當前組件是否為遠程依賴
    ext.isMaven = { name ->
        Properties properties = new Properties()
        def file = rootProject.file("${name}/maven.properties")
        if (file.exists()) {
            InputStream inputStream = file.newDataInputStream()
            properties.load(inputStream)
            def str = properties.getProperty('MAVEN')
            if (str == null) {
                return false
            } else {
                return Boolean.parseBoolean(str)
            }
        }
        return false
    }

    //讀取maven.property文件中庫版本
    ext.mavenVersion = { name ->
        println("mavenVersion::${name}")
        Properties properties = new Properties()
        def file = rootProject.file("${name}/maven.properties")
        if (file.exists()) {
            InputStream inputStream = file.newDataInputStream()
            properties.load(inputStream)
            def str = properties.getProperty('VERSION')
            if (str == null) {
                throw Exception(file.path + "    VERSION == null")
            } else {
                return str
            }
        }
        return ""
    }

app下的build.gradle中這樣依賴晒他,判斷是依賴遠程還是本地。

    implementation projectCompat(":library-res")
    implementation projectCompat(":library-base")
    implementation projectCompat(":library-network")
    implementation projectCompat(":module-entrance")
    implementation projectCompat(":module-home")
    implementation projectCompat(":module-community")
    implementation projectCompat(":module-notice")
    implementation projectCompat(":module-user")
    implementation projectCompat(":module-login")
    implementation projectCompat(":module-play")

該項目配置的github地址逸贾,需要細看的戳這里

參考:
https://www.bilibili.com/video/BV1oK4y1R7Hx?p=9&vd_source=40c24e77b23dc2e50de2b7c87c6fed59

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末陨仅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子铝侵,更是在濱河造成了極大的恐慌灼伤,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咪鲜,死亡現(xiàn)場離奇詭異狐赡,居然都是意外死亡,警方通過查閱死者的電腦和手機疟丙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門颖侄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人隆敢,你說我怎么就攤上這事发皿。” “怎么了拂蝎?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惶室。 經(jīng)常有香客問我温自,道長玄货,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任悼泌,我火速辦了婚禮松捉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘馆里。我一直安慰自己隘世,他們只是感情好,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布鸠踪。 她就那樣靜靜地躺著丙者,像睡著了一般。 火紅的嫁衣襯著肌膚如雪营密。 梳的紋絲不亂的頭發(fā)上械媒,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天,我揣著相機與錄音评汰,去河邊找鬼纷捞。 笑死,一個胖子當著我的面吹牛被去,可吹牛的內(nèi)容都是我干的主儡。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼惨缆,長吁一口氣:“原來是場噩夢啊……” “哼糜值!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起踪央,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤臀玄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后畅蹂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體健无,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年液斜,在試婚紗的時候發(fā)現(xiàn)自己被綠了累贤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖轮洋,靈堂內(nèi)的尸體忽然破棺而出危尿,到底是詐尸還是另有隱情,我是刑警寧澤渗磅,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響始鱼,放射性物質(zhì)發(fā)生泄漏仔掸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一医清、第九天 我趴在偏房一處隱蔽的房頂上張望起暮。 院中可真熱鬧,春花似錦会烙、人聲如沸负懦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纸厉。三九已至,卻和暖如春葫盼,著一層夾襖步出監(jiān)牢的瞬間残腌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工贫导, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留抛猫,地道東北人。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓孩灯,卻偏偏與公主長得像闺金,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子峰档,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

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