大型移動應(yīng)用解決之道 - 依賴管理

讀者如果閱讀過”插件化”與”組件化”這兩篇文章的話妖碉,可能多少對下面這張圖應(yīng)該會有印象

image

上圖我用紅色著重標(biāo)記出來的Maven倉庫咆瘟,它的作用是什么温鸽?為什么會引入這樣一臺服務(wù)器瓤鼻?

如果我們足夠細(xì)化架構(gòu)秉版,那么必然會有通用的組件或模塊被提取出來,通常每個通用組件或模塊都有專門的團(tuán)隊來負(fù)責(zé)開發(fā)維護(hù)茬祷,既然是通用的清焕,那么其他功能模塊的研發(fā)團(tuán)隊都需要依賴他們來做事情,而依賴的方式大概有以下兩種:

1. 代碼依賴

image

就像上圖祭犯,組件團(tuán)隊提交代碼與依賴庫到SCM秸妥,模塊團(tuán)隊將組件工程從SCM上checkout下來,將代碼copy到工程內(nèi)或?qū)⒔M件工程作為lib依賴沃粗,這兩種做法非常容易引起依賴沖突與無用的依賴粥惧,而且這種依賴是直接的代碼依賴,具有很大的安全性性問題最盅。

2. 二進(jìn)制包依賴

二進(jìn)制包的依賴應(yīng)該說是比較好的依賴方式(沒有安全性等問題)突雪,但是面臨的問題是以什么樣的方式去獲取依賴包?

2.1 copy依賴包

組件團(tuán)隊將組件打包成AAR/JAR后檩禾,將組件包與依賴的第三方包都上傳到約定的服務(wù)器上,各模塊團(tuán)隊都到指定服務(wù)器上去取疤祭。 這種方式雖然避免了模塊團(tuán)隊直接依賴代碼盼产,但是由于需要手動從服務(wù)器下載組件包與依賴包,出錯概率很高勺馆,很有可能出現(xiàn)在手動copy過程中出現(xiàn)依賴版本copy錯誤的問題戏售,而且要想更新組件所依賴的第三方庫的版本也是非常繁瑣的事情。

2.2 通過maven

image

在看上圖草穆,組件團(tuán)隊將組件打包成AAR/JAR(同時生成POM文件灌灾,文件中聲明依賴),并通過Maven插件上傳到指定的私有Maven服務(wù)器上悲柱,各模塊團(tuán)隊通過Maven插件去引用組件(沒有手動過程)锋喜,而依靠Maven的依賴傳遞性將自動把各組件所依賴的所有第三方庫及第三方庫依賴的包進(jìn)行下載并引入。

很明顯2.2部分,使用maven進(jìn)行依賴的管理是最佳的方案嘿般,那么Maven是什么段标?

Maven是一個項目的管理工具,它能夠貫穿整個研發(fā)流程炉奴,從開發(fā)逼庞,測試,打包瞻赶,發(fā)布等都能派上用場赛糟,而Maven最突出的能力就是依賴管理,也就是我們今天所說的內(nèi)容砸逊。 使用了Maven的依賴管理璧南,從此告別手動copy依賴包,人工維護(hù)依賴包版本痹兜,依賴沖突與重復(fù)依賴等問題穆咐,節(jié)省了很多時間,同時避免了很多錯誤字旭。
在Android中对湃,我們基本上都是通過Gradle來操作maven(Gradle繼承了maven的依賴管理操作,并將依賴的引用變的更加簡單)遗淳,使用nexus來搭建Maven服務(wù)器(nexus實現(xiàn)了maven的依賴管理)拍柒。
在iOS中,則通過cocoapods來進(jìn)行依賴管理屈暗,我們后面會在iOS系列文章中講解cocoapods的使用拆讯。

Maven是如何進(jìn)行依賴管理的,在Gradle中如何使用养叛?

定位依賴包

我們要使用依賴包种呐,首先需要定位到這個依賴包,而在maven中弃甥,要定義一個依賴包爽室,需要了解包的定位坐標(biāo)groupid,artifactId,version,基于三者的信息就可以在本地或遠(yuǎn)程服務(wù)器進(jìn)行定位淆攻。

例如:commons-collections的定位坐標(biāo)阔墩,看下圖XML部分定義

image

Gradle中定義一個依賴包

commons-collections:commons-collections:3.2.2

依賴版本

通常我們可能會有多個組件引用了commons-collections包,如果需要統(tǒng)一升級commons-collections版本瓶珊,我們只能逐個到項目中去修改澎办,這樣就增加了出錯的概率蔑歌。 而maven中支持我們通過引用變量的方式筒扒,在POM文件中以${變量名}來進(jìn)行引用护奈。那么我們通過修改這個變量名對應(yīng)的值就可以做到批量修改對應(yīng)的版本號。

變量名=值(commonscollections.version = 3.2.2)
Maven配置

<dependency>
  <groupId>commons-collections</groupId>
  <artifactId>commons-collections</artifactId>
  <version>${commonscollections.version}</version>
  </dependency>

Gradle引入

commons-collections:commons-collections:${commonsnet.version}

在Gradle中,我們通常將版本配置在.gradle中柑土,或配置在.properties文件中蜀肘。

依賴范圍

根據(jù)依賴范圍的配置,我們可以控制所依賴的包稽屏,是否被打包進(jìn)我們的產(chǎn)品中扮宠。

<dependency>
  <groupId>commons-collections</groupId>
  <artifactId>commons-collections</artifactId>
  <version>${commonscollections.version}</version>
  <scope>provided</scope> 
</dependency>
依賴范圍      是否被打包
compile        true
runtime        true
provided       false
test           false

依賴分類

通常可以通過groupid(組ID),artifactId(依賴包名稱),version(依賴包版本)來定位一個依賴包狐榔。 但是有時坛增,我們還需要依賴包的其他部分:比如,源碼(src)薄腻,資源(sources)收捣,文檔(javadoc)等,這些包他們的groupid,artifactid,version是相同的庵楷,只是類型不同罢艾,也就是classifier不同。 我們看下dbutils組件都做了哪些分類尽纽,從下圖XML部分咐蚯,可以看到當(dāng)前classifier是src,也就表明弄贿,這是一個源代碼的包春锋。

image

從下圖XML中,可以看到當(dāng)前classifier是sources差凹,表明這是一個資源的包期奔。

image

我們也可以將同一個jar包打包成兩個不同classifier的jar包來進(jìn)行區(qū)分,使用者根據(jù)classifier來進(jìn)行引用危尿。

通過classifier呐萌,使我們可供依賴的資源變的更加豐富和靈活。

通過Gradle來引入classifier的依賴包

Gradle引入

commons-dbutils:commons-dbutils:1.6:sources

依賴傳遞

假設(shè)我們有模塊A , B, C , D谊娇, A->B(A直接依賴B)肺孤,B->C,C->D邮绿。那么A間接依賴B,C,D模塊(A->B->C->D)渠旁,這就是依賴傳遞攀例。

禁止傳遞

Maven配置船逮,排除所有依賴

<dependency>  
    <groupId>xx.xx</groupId>  
    <artifactId>YY</artifactId>  
    <version>oo</version>  
    <exclusions>  
        <exclusion>  
            <groupId>*</groupId>  
            <artifactId>*</artifactId>  
        </exclusion>  
    </exclusions>  
</dependency>  

gradle禁止依賴傳遞

compile('xx:YY:xx') {
    transitive = false
}

依賴排除

按照上面例子,A間接依賴B,C,D模塊粤铭,如果A想排除對D的依賴挖胃,可以在pom文件中添加如下配置。

<dependency> 
  <groupId>xx.xx</groupId> 
  <artifactId>YY</artifactId> 
  <version>oo</version>
  <exclusions> 
    <exclusion> 
      <groupId>xx.xx</groupId> 
      <artifactId>YY</artifactId>
     </exclusion>
  </exclusions> 
</dependency>

在gradle中,排除依賴

dependencies {
    compile("xx.xx.xx:YY:oo") {
        exclude group: 'xx.xx', module: 'YY'
    }
}

依賴沖突

A->B->collections3.1, B->C->D->collections3.2.2

從上面依賴關(guān)系可以看出酱鸭,B依賴collections3.1吗垮,而按照B的依賴路徑,同時也會依賴collections3.2.2凹髓,細(xì)心的讀者可能發(fā)現(xiàn)了問題烁登。

maven會選擇最短的路徑進(jìn)行依賴蔚舀,也就是v3.1,而我們本意是要使用最新的版本做為依賴赌躺,這樣可能會導(dǎo)致崩潰或功能性錯誤。我們可以縮短高版本的依賴路徑礼患,修改POM文件為A->B->collections3.2.2,而B->C->D->collections3.1
而Gradle默認(rèn)是按照高版本進(jìn)行依賴的缅叠, 即便上面的依賴路徑,Gradle也會去依賴v3.2.2的版本痪署。而有時码泞,我們可能也需要強(qiáng)制的依賴某個版本余寥,就像下面:

compile('xxx.xxx:YY:oo') {
    force = true
}

全局配置強(qiáng)制依賴

configurations.all {
    resolutionStrategy {
      force 'xx.xx:YY:oo'
    }
 }

通過上面的講述,相信讀者基本已經(jīng)了解maven的依賴管理的優(yōu)勢悯森。 那么如何搭建自己的Maven服務(wù)器呢宋舷? 上面也提到了使用Nexus,關(guān)于Nexus的搭建教程推薦大家這篇文章

http://blog.csdn.net/wang379275614/article/details/43940259

通過這篇文章瓢姻,相信讀者也基本了解了Nexus相關(guān)概念和搭建技巧祝蝠。

而如何去操作nexus,通常有兩種方法:
1. 手動上傳
在nexus的web ui上進(jìn)行管理

2. 通過gradle的maven-plugin
通過gradle命令進(jìn)行管理

gradle如何配置和使用命令

1. 配置
Nexus相關(guān)配置 build-nexus-config.gradle

gradle.ext {
  NEXUS_SERVER_GROUP='http://ip:port/nexus/content/groups/public/'
  NEXUS_SERVER_RELEASE='http://ip:port/nexus/content/repositories/release/'
  NEXUS_SERVER_SNAPSHOT='http://ip:port/nexus/content/repositories/snapshot/'
  NEXUS_SERVER_UNAME='admin'
  NEXUS_SERVER_UPWD='123456'
}

版本相關(guān)配置build-artifact-config.gradle

gradle.ext {
  ARTIFACT_SNAPSHOT_VERSION='1.0.0.5-SNAPSHOT'
  ARTIFACT_RELEASE_VERSION='1.0.0.5'
  ARTIFACT_ID=’common’
  ARTIFACT_GROUP_ID='com.xx.xx'
}

在build.gradle中配置倉庫地址

allprojects {
    repositories {
        maven{ url gradle.NEXUS_SERVER_GROUP }
        jcenter()
    }
}

2. 上傳組件
在上傳時需要區(qū)分當(dāng)前要上傳的是snapshot倉庫幻碱,還是relaese倉庫绎狭。
可以通過參數(shù)來進(jìn)行區(qū)分(由自己定義),默認(rèn)情況下褥傍,發(fā)布的為快照版本儡嘶,如果添加參數(shù)名為'repositoryRelease'則表示為正式版本。

Gradle發(fā)布snapshot命令:

gradle uploadArchives

Gradle發(fā)布release命令:

gradle uploadArchives -P repositoryRelease
apply plugin: 'maven'
apply plugin: "maven-publish"

def isSnapshot = true
if(project.hasProperty('repositoryRelease')){
    isSnapshot = false
}

gradle.ext.isSnapshotVersion = isSnapshot;

uploadArchives {
    repositories {
        mavenDeployer {

            repository(url: isSnapshot ? gradle.NEXUS_SERVER_SNAPSHOT : gradle.NEXUS_SERVER_RELEASE) {
                authentication(userName: gradle.NEXUS_SERVER_UNAME, password: gradle.NEXUS_SERVER_UPWD)
            }

            pom.version = isSnapshot ? gradle.ARTIFACT_SNAPSHOT_VERSION :gradle.ARTIFACT_RELEASE_VERSION
            pom.artifactId = gradle.ARTIFACT_ID
            pom.groupId = gradle.ARTIFACT_GROUP_ID
            
            pom.withXml {
                def dependenciesNode = asNode().appendNode('dependencies')
            
                configurations.compile.allDependencies.each {
                    def dependencyNode = dependenciesNode.appendNode('dependency')
                    dependencyNode.appendNode('groupId', it.group)
                    dependencyNode.appendNode('artifactId', it.name)
                    dependencyNode.appendNode('version', it.version)
                }
            }
        }
    }
}

通過以上恍风,我們可以總結(jié)出下圖:

image

從上圖可以看出蹦狂,所有通用組件(業(yè)務(wù)/技術(shù)/SDK等)全部都存儲在Nexus服務(wù)器上誓篱,Nexus上由不同的倉庫(snapshot/release/proxy/group等)組成。組件團(tuán)隊與業(yè)務(wù)團(tuán)隊均通過配置Maven插件達(dá)到對指定的倉庫進(jìn)行上傳和下載凯楔。

另外一個Maven重要的功能窜骄,既是代理遠(yuǎn)程的倉庫,見下圖:

image

通過代理遠(yuǎn)端倉庫摆屯,達(dá)到加快本地快速訪問的目的邻遏。

寫到這里,不知道讀者是不是已經(jīng)對依賴管理有了一些了解虐骑。如果讀者在搭建過程中有任何問題党远,我們可以一起討論。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末富弦,一起剝皮案震驚了整個濱河市沟娱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌腕柜,老刑警劉巖济似,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盏缤,居然都是意外死亡砰蠢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門唉铜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來台舱,“玉大人,你說我怎么就攤上這事潭流。” “怎么了拆宛?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵浑厚,是天一觀的道長根盒。 經(jīng)常有香客問我,道長炎滞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任盖矫,我火速辦了婚禮击奶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘柜砾。我一直安慰自己,他們只是感情好证芭,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布担映。 她就那樣靜靜地躺著,像睡著了一般官硝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上氢架,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天朋魔,我揣著相機(jī)與錄音,去河邊找鬼孙援。 笑死扇雕,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的邻辉。 我是一名探鬼主播腮鞍,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吱瘩!你這毒婦竟也來了迹缀?” 一聲冷哼從身側(cè)響起蜜徽,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤拘鞋,失蹤者是張志新(化名)和其女友劉穎矢门,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體隔躲,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡宣旱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年叛薯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片买置。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡忿项,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出轩触,到底是詐尸還是另有隱情家夺,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布榨为,位于F島的核電站煌茴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蔓腐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一散罕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧欧漱,春花似錦、人聲如沸硫椰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至派继,卻和暖如春捻艳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绅络。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工嘁字, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纪蜒。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像纯续,于是被迫代替她去往敵國和親牙丽。 傳聞我的和親對象是個殘疾皇子烤芦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354

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