Kotlin之模塊化開(kāi)發(fā)

一、模塊化淺談

1. 什么是模塊化開(kāi)發(fā)派敷?

模塊化就是將一個(gè)程序按照其功能做拆分癌压,分成相互獨(dú)立的模塊仰泻,以便于每個(gè)模塊只包含與其功能相關(guān)的內(nèi)容。比如登錄功能可以是一個(gè)模塊滩届。

2. 模塊化開(kāi)發(fā)的優(yōu)勢(shì)集侯?

  • 解耦性強(qiáng):隨著業(yè)務(wù)的增多,代碼變的越來(lái)越復(fù)雜帜消,每個(gè)模塊之間的
    代碼耦合變得越來(lái)越嚴(yán)重棠枉,解耦問(wèn)題急需解決。
  • 編譯時(shí)間大大減少:以為業(yè)務(wù)場(chǎng)景對(duì)泡挺,代碼越來(lái)越大辈讶,同時(shí)編譯時(shí)間也會(huì)越來(lái)越長(zhǎng)。
  • 提高團(tuán)隊(duì)協(xié)同開(kāi)發(fā):團(tuán)隊(duì)協(xié)同開(kāi)發(fā)存在較多的沖突.不得不花費(fèi)更多的時(shí)間去溝通和協(xié)調(diào),影響開(kāi)發(fā)效率 娄猫。

二贱除、模塊化開(kāi)發(fā)的架構(gòu)分層

本項(xiàng)目是一個(gè)移動(dòng)端的后臺(tái)運(yùn)營(yíng)管理系統(tǒng)。按照功能劃分媳溺,暫有統(tǒng)計(jì)模塊月幌、直播模塊、音頻模塊和個(gè)人中心悬蔽。


基本項(xiàng)目結(jié)構(gòu)圖

命名建議:
底層:Library
中間層:Module + 業(yè)務(wù)或功能名字
上層:App + 項(xiàng)目名字

1. 基本結(jié)構(gòu)

app模塊是每個(gè)項(xiàng)目初始都有的扯躺,實(shí)質(zhì)上仍然是一個(gè)module。Android Stuido項(xiàng)目中屯阀,我們和代碼打交道的大部分時(shí)間都在module中缅帘。當(dāng)然,具體的module機(jī)制我們不去深究难衰,有時(shí)間可以另開(kāi)一坑去研究。下面的三個(gè)module大家可以理解為kotlin中的library逗栽,當(dāng)然這樣說(shuō)不準(zhǔn)確盖袭。
app作為主要的module,主要承擔(dān)了應(yīng)用啟動(dòng)以及最上層的通用邏輯。比如在這個(gè)例子中鳄虱,應(yīng)用啟動(dòng)弟塞、首頁(yè)展示和模塊切換的業(yè)務(wù)邏輯都在這個(gè)模塊。
而下屬的三個(gè)子模塊承擔(dān)了各自細(xì)分的業(yè)務(wù)拙已,并且可以被app模塊或者其他模塊引用决记,每個(gè)模塊只負(fù)責(zé)和考慮自己的業(yè)務(wù)。以此達(dá)到業(yè)務(wù)邏輯和代碼分離的邏輯倍踪。

按照之前的業(yè)務(wù)邏輯劃分系宫,項(xiàng)目結(jié)構(gòu)可以先大致分為如下的結(jié)構(gòu):


其中箭頭方向表示了引用關(guān)系

2. 系統(tǒng)層分離

僅僅這樣是不夠的。因?yàn)樵贏S中建车,module的引用是單向的扩借。如果A module引用了B module,那么對(duì)A來(lái)講缤至,B是可見(jiàn)的潮罪,B的所有公開(kāi)功方法理論上都可以在A中使用,但是對(duì)B來(lái)說(shuō)领斥,A是不可見(jiàn)的嫉到。所以,這樣的結(jié)構(gòu)出現(xiàn)的問(wèn)題就是我們有大量的底層通用方法都放在app模塊中月洛,對(duì)于子模塊來(lái)講是不可見(jiàn)的何恶,子模塊無(wú)法引用封裝好的底層方法,例如網(wǎng)絡(luò)請(qǐng)求膊存,圖片加載导而,文件拷貝等,這肯定是不行的隔崎。所以這個(gè)結(jié)構(gòu)還得再優(yōu)化今艺。

按照module單向引用的原則,我們可以把公共底層通用方法單獨(dú)分出來(lái)作為一個(gè)moduleBase爵卒,同時(shí)這個(gè)moduleBase也是最底層的module虚缎,保證對(duì)于其他module來(lái)說(shuō)它都是可見(jiàn)的。這樣還有一個(gè)好處就是這個(gè)moduleBase中的方法和配置大部分都是可以高度復(fù)用的钓株。在新開(kāi)其他項(xiàng)目的時(shí)候這個(gè)模塊可以直接遷移到新項(xiàng)目中实牡,而不用在為代碼分離和剔除浪費(fèi)時(shí)間。所以轴合,項(xiàng)目的結(jié)構(gòu)進(jìn)一步細(xì)化成了如下的結(jié)構(gòu):


基本的模塊化項(xiàng)目結(jié)構(gòu)

3. 公共層分離

上述的模塊如果在使用中创坞,有很大概率會(huì)遇到一個(gè)問(wèn)題,部分的實(shí)體類(lèi)受葛、自定義view题涨、布局文件或者資源文件在各個(gè)模塊都需要用到偎谁,但是這些如果放在系統(tǒng)層的moduleBase里面,又會(huì)破壞系統(tǒng)層的通用性纲堵。所以巡雨,我們還需要一個(gè)公共層Provider來(lái)專(zhuān)門(mén)提供上層業(yè)務(wù)邏輯模塊的公共資源。直接上圖:


完整的模塊化項(xiàng)目結(jié)構(gòu)

4. 擴(kuò)展

待完善...

三席函、如何進(jìn)行安卓模塊化開(kāi)發(fā)

1. 創(chuàng)建Module模塊

將所需的模塊在對(duì)應(yīng)項(xiàng)目的Project目錄下拷貝出來(lái)铐望,粘貼到要開(kāi)發(fā)的項(xiàng)目的Project根目錄下即可。也可以直接在project下新建一個(gè)Module茂附。

File --> New --> New Module --> Android Library (建議選擇這個(gè)) --> Finish

一個(gè)模塊這樣就創(chuàng)建完成了正蛙。默認(rèn)的名字是app,根據(jù)項(xiàng)目的需要將模塊的名字進(jìn)行修改何之,直接Refactor > Rename...即可


模塊的名稱修改

2. 將Module模塊引入主項(xiàng)目中

設(shè)置setting.gradle 中

include ':app', ':UserCenter'

設(shè)置后發(fā)現(xiàn)項(xiàng)目目錄下增加了一個(gè)模塊


新增的用戶登錄模塊

在主模塊的build.gradle中設(shè)置

api project(':UserCenter')


3. 模塊中application和library狀態(tài)切換配置

1. 設(shè)置一個(gè)開(kāi)關(guān)控制application和library狀態(tài)切換
我們?cè)陂_(kāi)發(fā)的時(shí)候跟畅,Module如果是一個(gè)庫(kù),會(huì)使用com.android.library插件溶推,如果是一個(gè)應(yīng)用徊件,則使用com.android.application插件,接下來(lái)根據(jù)這個(gè)變量來(lái)進(jìn)行判斷并且實(shí)現(xiàn)狀態(tài)切換蒜危。
Project根目錄下gradle.properties中設(shè)置變量來(lái)控制虱痕。

isUserModule = false

isUserModule=false:表示這個(gè)模塊是一個(gè)UserCenter的Module;
isUserModule=true:表示這個(gè)模塊是一個(gè)app;

2. 依賴項(xiàng)目中build.gradle配置
在模塊的build.gradle的開(kāi)頭處設(shè)置。

//頂部插件引入
if(isUserModule.toBoolean()){
    apply plugin: 'com.android.library'
}else {
    apply plugin: 'com.android.application'
}
android {
    ...
    sourceSets{
        main{
            if (isUserModule.toBoolean()){
                manifest.srcFile 'src/main/release/AndroidManifest.xml'
                //release模式下排除debug文件夾中的所有Java文件
                java{
                    exclude 'debug/**'
                }
            }else{
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            }
        }
    }
}

3. 提供兩套 AndroidManifest.xml并進(jìn)行動(dòng)態(tài)切換
mainfest文件也需要提供兩套

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


四辐赞、Kotlin模塊化開(kāi)發(fā)過(guò)程中遇到的問(wèn)題

1. 資源名稱沖突

Android Studio 默認(rèn) library 的所有的 resource 為 public , 所以在模塊化開(kāi)發(fā)過(guò)程中總會(huì)遇到資源沖突問(wèn)題部翘。列出兩種解決方法。

  • 方法一:
    保護(hù)某些 resources 不被外部訪問(wèn)响委,可以創(chuàng)建res/values/public.xml新思,因?yàn)?public 是關(guān)鍵詞,搜易需要用 new file 的方式創(chuàng)建赘风。至少添加一行夹囚,為添加的視為 private
<resources>
    <public name="mylib_app_name" type="string"/>
</resources>
  • 方法二:
    在 library 的 build.gradle 中添加 resourcePrefix , 則所有的資源須以此 prefix 開(kāi)頭,否則報(bào)錯(cuò)邀窃。注意荸哟,圖片資源雖然不提示報(bào)錯(cuò)誤,但是也需要修改名字瞬捕。
android {
    ...
    buildTypes {
    ...
    }
    resourcePrefix 'my_prefix_'
}


2.重復(fù)依賴

將所有的依賴都寫(xiě)在library層(BaseLibrary鞍历、Provider)的module,將所有的依賴統(tǒng)一成一個(gè)入口給上層的app去引用肪虎。

3.控制臺(tái)報(bào)錯(cuò)解決

Error:Module 'qsp_release:libLive:unspecified' depends on one or more Android Libraries but is a jar

報(bào)這個(gè)錯(cuò)誤的場(chǎng)景劣砍,Moudle A 的build.gradle下

apply plugin: 'java'
...
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':library')
    ...
}

可以看出 Moudle A 是一個(gè)jar形式的依賴模塊,而library是apply plugin: 'com.android.library'所以引用報(bào)如上錯(cuò)誤扇救。

4. 使用Dagger2時(shí)秆剪,需要分別在app層級(jí)下的build.gradle中添加如下代碼

apply plugin: 'kotlin-kapt'//kapt 插件
dependencies {
    //Dagger2
    kapt "com.google.dagger:dagger-compiler:$dagger_version"
}

5. implementation赊淑、api與多模塊依賴

自從gradle升級(jí)3.+版本后爵政,gradle原來(lái)的依賴方法全部都被替換了仅讽,之前的compile替換成了implementation和api,新建工程時(shí)發(fā)現(xiàn)gradle默認(rèn)使用的也是implementation钾挟。
其中 api 與之前的 compile 功能基本一致洁灵,不再贅述;implementation 就比較高級(jí)了掺出,其作用就是徽千,使用 implementation 添加的依賴不會(huì)再編譯期間被其他組件引用到,但在運(yùn)行期間是完全可見(jiàn)的汤锨。這也是一種代碼隔離双抽。舉個(gè)例子:

組件A依賴lib1,既A implementation lib1
組件B依賴組件A闲礼,既B api A

在 gradle3.0.0 之前牍汹,B是完全可以引用到 lib1 里面的類(lèi)的,但是現(xiàn)在B在編譯期間就做不到了柬泽,只能在運(yùn)行期可以慎菲。
在kotlin中,為了避免以后依賴庫(kù)引用無(wú)效的悲劇發(fā)生 锨并,盡量用 implementation ,只有要用到依賴中的依賴,再用api

6. compileDebugKotlin 解決方案之一(api錯(cuò)用導(dǎo)致的重復(fù)引用)

首先先補(bǔ)下 依賴的知識(shí) ,依賴可以用2種方式implementation和 api,舉例場(chǎng)景:
app 依賴 bModule , bModule依賴cModule

api:app既可以引用bModule的方法和類(lèi)又可以引用cModule中代碼 
implementation: app能引用bModule中的方法和類(lèi)  app不能引用cModule中的方法    這樣的好處是提高編譯效率

我項(xiàng)目的依賴關(guān)系是:

app api bModule            
app implementation cModule

導(dǎo)致 重復(fù)引用cModule露该,一直報(bào)錯(cuò)compileDebugKotlin
解決辦法就是app implementation bModule
為了避免以后這種悲劇發(fā)生 盡量只用 implementation ,只有要用到依賴中的依賴,再用api

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市第煮,隨后出現(xiàn)的幾起案子解幼,更是在濱河造成了極大的恐慌,老刑警劉巖包警,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撵摆,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡揽趾,警方通過(guò)查閱死者的電腦和手機(jī)台汇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)篱瞎,“玉大人苟呐,你說(shuō)我怎么就攤上這事±睿” “怎么了牵素?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)澄者。 經(jīng)常有香客問(wèn)我笆呆,道長(zhǎng)请琳,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任赠幕,我火速辦了婚禮俄精,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘榕堰。我一直安慰自己竖慧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布逆屡。 她就那樣靜靜地躺著圾旨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪魏蔗。 梳的紋絲不亂的頭發(fā)上砍的,一...
    開(kāi)封第一講書(shū)人閱讀 51,482評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音莺治,去河邊找鬼廓鞠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛产雹,可吹牛的內(nèi)容都是我干的诫惭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蔓挖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼夕土!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起瘟判,我...
    開(kāi)封第一講書(shū)人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤怨绣,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后拷获,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體篮撑,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年匆瓜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赢笨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驮吱,死狀恐怖茧妒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情左冬,我是刑警寧澤桐筏,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站拇砰,受9級(jí)特大地震影響梅忌,放射性物質(zhì)發(fā)生泄漏狰腌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一牧氮、第九天 我趴在偏房一處隱蔽的房頂上張望琼腔。 院中可真熱鬧,春花似錦蹋笼、人聲如沸展姐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至教馆,卻和暖如春逊谋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背土铺。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工胶滋, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人悲敷。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓究恤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親后德。 傳聞我的和親對(duì)象是個(gè)殘疾皇子部宿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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