筆者曾經不思量力的思考過『是什麼推進了互聯網技術的快速發展?』
這種偉大的命題。結論是,除了摩爾定律以外,技術經驗的快速積累和普遍分享
,也是重要的緣由。html
有人戲稱,『寫 Java,首先要學會選包』,在這裏很差評論對錯。不過這句話裏面,至少包含兩層意思:首先 Java 有大量的現成的依賴包,沒必要要本身造輪子;其次,Java 的包存放較爲集中,集成方式也方便。java
筆者從事 Android 和 Java 開發以來,經歷了幾個階段:android
閉門造輪子 > 使用別人的輪子 > 開門造輪子 > 分享輪子
git
在使用、創造、分享輪子的過程當中,maven 倉庫的使用可謂必備技能。程序員
相信各位使用 Android Studio,對於 jcenter()
、mavenCentral()
等概念應該是司空見慣了。程序員要知其然,知其因此然。本篇將按照以下脈絡介紹在 Android Studio 中 Maven 倉庫相關的概念和應用。github
至於 Maven 是什麼,請參考 Apache Maven。docker
對於 Android 開發者而言,只須要知道 Maven 是一種構建工具,Maven 包是由所謂 POM(Project Object Model)所定義的文件包格式便可。apache
Gradle 可使用 Maven 包,並且大部分的 Android 可以使用的遠程依賴包都是 Maven 包。api
先來看一個託管在某倉庫上的 Maven 包:Bugtags-Android-Lib 所包含的內容:android-studio
1 2 3 4 5 6 7 8 |
bugtags-lib-1.1.0-javadoc.jar//javadoc 文件 bugtags-lib-1.1.0-javadoc.jar.asc//javadoc 文件的簽名 bugtags-lib-1.1.0-sources.jar//源碼文件 bugtags-lib-1.1.0-sources.jar.asc//源碼文件的簽名 bugtags-lib-1.1.0.aar//Android Library 的主文件包 bugtags-lib-1.1.0.aar.asc//主文件包的簽名 bugtags-lib-1.1.0.pom//包描述文件 bugtags-lib-1.1.0.pom.asc//描述文件的簽名 |
對於一個合符規範的 Maven Package,pom 文件、aar(或者 jar) 文件
是必須的。
而 javadoc 文件、源碼文件、簽名文件都不是必要的,可是某些公開倉庫
(如 mavenCentral )有此要求。
使用這個包的方式,相信你們已經很熟悉了:
1 2 3 |
dependencies { compile 'com.bugtags.library:bugtags-lib:1.1.0' } |
一個 Maven Package,最重要就是 POM(Project Object Model) 文件,這實際上是一個 XML 文件,這裏截取 Bugtags-Android-Lib POM 主要內容以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="UTF-8"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <groupId>com.bugtags.library</groupId> <artifactId>bugtags-lib</artifactId> <version>1.1.0</version> <packaging>aar</packaging> <dependencies> <dependency> <groupId>com.android.support</groupId> <artifactId>support-v4</artifactId> <version>19.0.0</version> <scope>compile</scope> </dependency> </dependencies> </project> |
包的惟一標示:
1 2 3 4 5 6 |
<!--包組 id,一般是發佈者擁有的域名的反向,以避免跟別人的重複--> <groupId>com.bugtags.library</groupId> <!--包 artifactId,很差意思我也不知道如何準確翻譯,其實就是組如下應該有一個更小的歸類--> <artifactId>bugtags-lib</artifactId> <!--包版本--> <version>1.1.0</version> |
其中三個字段與 Gradle 的依賴格式 'com.bugtags.library:bugtags-lib:1.1.0'
冒號分割的三段一一對應。這就解釋了所謂的 Gradle 兼容 Maven 包
。
Maven 包集中存放的地方,就是 Maven 倉庫。這些倉庫,能夠是放在本地,也能夠放在某個遠程服務器上。 能夠是私有倉庫,也能夠是公開的。下面是筆者平常開發用的庫列表:
1 2 3 4 5 6 7 8 9 10 11 12 |
mavenCentral(); jcenter() maven { url 'file:///Users/my-user-name/Documents/Android/repo/' } maven { url 'http://192.168.99.100:8081/content/repositories/releases/' } maven { url "https://jitpack.io" } |
Android Studio Gradle 主要支持兩個 Maven 中央庫:mavenCentral
和 jcenter
。
讀者可能會發現兩個問題:
maven { url : xxx}
,這種格式能夠配置任何一個存在的倉庫?解釋以下:
根據這篇博客,jcenter 具備以下優勝特色,使得谷歌進行切換:
筆者親測,在 bintray 上發佈包到 jcenter 在易用性上的確比 在 sonatype 發佈到到 mavenCentral 要好得多。
沒錯,你能夠經過 maven { url : xxx }
使用任何一個符合 maven 規範的倉庫。
存在本地的
1 2 3 |
maven { url 'file:///Users/my-user-name/Documents/Android/repo/' } |
存在內網服務器的
1 2 3 |
maven { url 'http://192.168.99.100:8081/content/repositories/releases/' } |
存在某個遠程服務器的
1 2 3 |
maven { url 'https://raw.githubusercontent.com/liaohuqiu/umeng-libs/master/repository' } |
此倉庫由 liaohuqiu 同窗爲方便你們使用友盟開發者工具,把相應的包作成了符合規範的 Maven 包,託管在 github 項目中。
使用 maven 包相信已經很清楚了,讓咱們更進一步。
當咱們在平常開發實踐中,積累了一些公共庫,想要固定下來,被本身或者別人方便的使用,就須要發佈 maven 包。
一個符合規範的 maven 包至少包含 pom 文件和主文件包。難道這些都要手動編寫和建立麼?
答案是:有了 gradle 插件,你只須要幹不多的事兒。
下面以發佈這系列包爲示例:
x
代替,下面會修改。也就是'com.as-gradle.demo:x:1.0.0'
讀者要進行練習的時候,最好改一下你的
groupId
,不然可能會發布失敗
下面使用到的示例工程已經放在了 github 上。
爲了後面使用方便,首先在工程的項目 gradle.properties
中定義一些屬性,這些屬性,主要是用生成 POM 文件,將會在通篇文章中用到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 包信息 PROJ_GROUP=com.as-gradle.demo PROJ_VERSION=1.0.0 # 項目的描述 PROJ_WEBSITEURL=https://bugtags.com PROJ_ISSUETRACKERURL=https://github.com/bugtags/Bugtags-Android/issues PROJ_VCSURL=https://github.com/bugtags/Bugtags-Android.git PROJ_DESCRIPTION=Simple and effective bug & crash reporting tool for Android apps # Licence信息 PROJ_LICENCE_NAME=The Apache Software License, Version 2.0 PROJ_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt PROJ_LICENCE_DEST=repo # Developer 信息 DEVELOPER_ID=your-dev-id DEVELOPER_NAME=your-dev-name DEVELOPER_EMAIL=your-email@your-mailbox.com |
localrepo
將本地某個路徑設置爲倉庫根目錄:
/Users/your-user-name/Documents/Android/repo/
( Mac 下)
這裏使用了一個叫作 your-user-name
的用戶下的某個目錄,請讀者自行替換成本身的登陸用戶名。
爲了優雅,在 localrepo 這個 module 的 gradle.properties
定義屬性:
1 2 3 4 5 6 |
PROJ_NAME=localrepo PROJ_ARTIFACTID=localrepo PROJ_POM_NAME=Local Repository LOCAL_REPO_URL=file:///Users/your-user-name/Documents/Android/repo/ #以上是 Mac 的本地路徑,若是是 Windows,則是相似: #LOCAL_REPO_URL=file:///C:/Users/cadmanager/Documents/repo/ |
在 module 中應用和配置 maven plugin:
1 2 3 4 5 6 7 8 9 |
apply plugin: 'maven' uploadArchives { repositories.mavenDeployer { repository(url: LOCAL_REPO_URL) pom.groupId = PROJ_GROUP pom.artifactId = PROJ_ARTIFACTID pom.version = PROJ_VERSION } } |
在控制檯運行:
1 |
$ ./gradlew -p localrepo clean build uploadArchives --info |
一切順利的話,你的第一個本地包已經發布到設定的目錄的本地倉庫了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
| ├── com │ ├── as-gradle │ │ └── demo │ │ └── localrepo │ │ ├── 1.0.0 │ │ │ ├── localrepo-1.0.0.aar │ │ │ ├── localrepo-1.0.0.aar.md5 │ │ │ ├── localrepo-1.0.0.aar.sha1 │ │ │ ├── localrepo-1.0.0.pom │ │ │ ├── localrepo-1.0.0.pom.md5 │ │ │ └── localrepo-1.0.0.pom.sha1 │ │ ├── maven-metadata.xml │ │ ├── maven-metadata.xml.md5 │ │ └── maven-metadata.xml.sha1 |
要使用這個包,首先在項目的 build.gradle 中添加這個本地倉庫:
1 2 3 4 5 6 7 8 9 |
allprojects { repositories { jcenter() maven{ url 'file:///Users/your-user-name/Documents/Android/repo/' } } } |
在某個 module(如 demo 項目中的 app) 的 build.gradle 中添加依賴:
1 |
compile 'com.as-gradle.demo:localrepo:1.0.0@aar' |
這裏有兩個奇怪的地方,筆者也沒有深刻研究,初步猜想是 Android Studio 的 Bug,知道答案的讀者請到我博客文章下留言賜教:
* 依賴末尾通常都須要加一個`@aar`,在某些版本的 Android Studio,又不須要,這是爲何? * 另外,若是本地包自己使用了了遠程的依賴,也須要在使用本地包的時候,一併加上,不然會報缺乏包,這又是爲何?
想要讓更多的人使用到你的勞動成果,你就須要把 Maven 包放在一個別人有權訪問的遠程倉庫上,而不是本機,接下來要介紹發佈 Maven 到 jcenter 倉庫和 mavenCentral 倉庫。由於前者的使用簡單,本着『先易後難,快速出成效』的原則,我先介紹 jcenter 的上傳。
jcenter 是由 bintray 提供的 maven 中央庫託管服務,bintray 又是 jfrog 公司的一款產品。jfrog 是一個商業公司,經過提供高級服務盈利,又爲普通開發者提供了足夠用的免費基礎功能(截止至2016-01-24),筆者較爲推崇這種開發者服務的商業模式。
引用一張圖來表述 bintray 的工做方式
圖片來源,http://inthecheesefactory.com/
使用 jcenter 須要在 bintray 上註冊帳號,在本地進行加密簽名配置,下面開始介紹。
前方高能預警:比較繁瑣,切勿半途放棄
前面介紹過,能夠把 bintray 的包同步到 mavenCentral,然後者須要對包文件進行簽名,簽名和驗證過程須要用到一個叫作 GPG 的工具產生的公鑰和私鑰。這裏有適合多個平臺的 GPG 程序,下面只介紹 OSX 平臺。
這種工具大概的意義是產生公鑰和私鑰,把公鑰發送到公開的服務器,私鑰用來產生包文件簽名。包的使用者在拿到包文件以後,經過公鑰來驗證文件的有效性,運行具體原理參考這裏。
檢測安裝成功,在命令行運行
1 2 3 |
$ gpg --version gpg (GnuPG/MacGPG2) 2.0.28 libgcrypt 1.6.3 |
有相似的輸出,就是正常安裝了
產生證書,運行命令,按照提示輸入
1 |
$ gpg --gen-key |
檢查結果
1 |
$ gpg --list-keys |
找到剛纔建立證書的時候,輸入的相關信息那三行,例如:
1 2 3 |
pub 2048R/2E686B39 2015-06-02 uid [ultimate] Your Name <your-email@your-mailbox.com> sub 2048R/your-sub-key-id 2015-06-02 |
上傳公鑰到服務器,找到你的 pub 的那一行,2048R/後的那一串八位字符串,如上面的:2E686B39,就是公鑰 ID
1 |
$ gpg --keyserver hkp://pool.sks-keyservers.net --send-keys your-public-key-id |
輸出公鑰和私鑰成文件
1 2 |
$ gpg -a --export your-email@your-mailbox.com > public_key_sender.asc $ gpg -a --export-secret-key your-email@your-mailbox.com > private_key_sender.asc |
配置本地 gradle 運行環境的屬性,位於~/.gradle/gradle.properties
,添加內容:
1 2 3 |
signing.keyId=your-public-key-id signing.password=your-gpg-password signing.secretKeyRingFile=/Users/your-user-name/.gnupg/secring.gpg |
bintray 自己能夠經過在 profile->GPG Sining 中配置 public key 和 private key 來自動對上傳的文件進行簽名,在下圖中,對應填入 public_key_sender.asc
與 private_key_sender.asc
的內容便可。
設置 bintray maven 包自動簽名
選取 maven 倉庫首頁,進入 edit:
最下面有兩個選項:
1 2 |
GPG sign uploaded files using Bintray's public/private key pair. GPG Sign uploaded files automatically |
由於我們是但願使用本身的 key,因此勾選第二個。
首頁-> maven -> add new package,填入對應的信息,其中 name 是在下面 bintray gradle 插件上傳的時候,使用的項目名稱,例如:bintryaar,這是要上傳一個 Android Library,上傳純 Java 包的方式有點點不同
,下面有介紹。
bintray 官方在 github 上託管了 bintray-examples,方便用戶使用 gradle 上傳包。
由於這裏要上傳的是 aar 格式的包,因此,具體是參考 gradle-aar-example 例子,然而例子有一些地方沒有更新,請注意下面的描述。
1 2 3 |
bintray.user=your-bintray-user bintray.apikey=your-bintray-apikey bintray.gpg.password=your-gpg-password |
其中 your-bintray-user 就是 bintray 右上角顯示的用戶名稱,your-bintray-apikey 在 profile->API Key 能夠找到,your-gpg-password 則是建立 gpg 證書的時候的密碼
1 2 3 4 5 6 7 8 9 10 11 |
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.3.0' //下面兩個包是用於上傳的插件 classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'//注意此處 } } |
在 module 的 gradle.properties 文件中定義屬性
1 2 3 |
PROJ_NAME=bintrayaar PROJ_ARTIFACTID=bintrayaar PROJ_POM_NAME=Bintray Aar Repository |
在 module 的 build.gradle 中使用插件
1 2 |
apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'com.jfrog.bintray' |
爲了build.gradle 文件乾淨,筆者建立了一個名爲 bintray.gradle 的文件配置插件信息,請參考這個文件。
關鍵點:
1 2 3 4 |
artifacts { archives javadocJar archives sourcesJar } |
是爲了同時生成 javadoc.jar 和 sources.jar 文件
build,上傳
1 |
$ ./gradlew -p bintrayrepo/ clean build bintrayUpload --info |
若是一切順利,你會在控制檯看到多個文件上傳成功的標輸出
踩坑實錄
HTTP/1.1 401 Unauthorized
apikey 或者 user 填寫錯誤
HTTP/1.1 409 Conflict
包的該版本已經存在,須要在 bintray 管理界面上刪除該版本後才能夠再次上傳
想讓 sources.jar 或者 javadoc.jar 爲空
1 2 3 4 5 |
task sourcesJar(type: Jar) { classifier = 'sources' from sourceSets.main.java.srcDirs exclude '**' } |
在上傳 jar 的時候,使用的插件有些區別
1 2 |
apply plugin: 'maven-publish' apply plugin: 'com.jfrog.bintray' |
在生成符合規定的 pom 文件的時候,要調用 groovy 的API,具體請參考這個文件
至此,剛纔上傳的兩個庫,已經能夠經過以下方式引用了
1 2 3 4 5 6 7 |
allprojects { repositories { maven { url 'https://dl.bintray.com/freefaces/maven'//這個地址在包的頁面的右方 } } } |
1 2 |
compile 'com.as-gradle.demo:bintrayaar:1.0.0' compile 'com.as-gradle.demo:bintrayjar:1.0.0' |
可是你也發現了,包的用戶須要添加一個額外的 maven 倉庫。做爲一個以用戶價值爲先的庫的開發者,那固然不但願用戶麻煩的。那就須要把這個私有的庫,推送到 jcenter 上去。
在 bintray 的 maven 庫的界面,有 add to jcenter
點擊以後,會進入一個消息頁面,寫或者不寫均可以。提交,等待回覆便可。
記住,包必須知足以下條件:
bintray 的消息系統有些 bug,假設你的包提交申請被駁回,你修改以後再提交申請,可能沒有人回覆你。請不要傻等。直接找頁面右側的 Feedback,這個他們是很快有人回答的。
成功了以後,會出現以下的標記:
你能夠在 jcenter 服務器上看到你的包了
在包管理頁面,能夠找到推送到 mavenCentral 功能,
一個包要從 bintray 推送到 jcenter,有幾個前提:
點擊 Sync 以後,一段時間以後,右邊的 Sync Status 會反饋出結果。
固然了,如今咱還幹不了這個,由於還有兩個條件沒準備好。那我們就進入 mavenCentral 的條件準備。
進入 sonatype issue 頁面,註冊帳號。
登錄以後,頂部有按鈕,Created,下面是關鍵的條目
Community Support - Open Source Project Repository Hosting (OSSRH)
其餘的都認真填寫。確認以後,大概兩個工做日, Issue 會變成 resolved 狀態,就能夠發佈你的包了。有了這兩部,其實就能夠從 bintray 上直接反向推到 mavenCentral ,而不須要走下面的步驟了,可是我仍是簡略介紹一下下面的步驟。若是很感興趣詳情,能夠參考 trinea 的介紹。
也有方便的 gradle 插件幫助咱們進行傳送,能夠參考 chrisbanes/gradle-mvn-push 項目。配置好插件,上傳。
登錄 oss sonatype,登錄,選擇左邊欄裏的 Staging Repositories
, 而後點Close 按鈕,sonatype 會作響應的 validation,經過的話,就能夠點 Release 發佈啦,若是不經過,就檢查問題,先 Drop,並從新作 Staging 發佈。
在 https://oss.sonatype.org/content/repositories/releases 能夠看到你發佈的包。
由於這個過程真的很繁瑣,ui 也不友好,在體驗了 bintray 的便捷和友好,並發現 bintray 能夠反向推送到 mavenCentral 以後,我就不再想使用 sonatype 了。無奈,貪嗔癡是人類天性。
因爲「你懂得」的緣由,在國內訪問 jcenter,老是偶爾不穩定,常常會出現諸如 peer not found 這種錯誤。爲了保證用戶的穩定使用庫,那就要考慮搭建放在本身服務器上的私有倉庫。
Sonatype 和 bintray 都提供了可供本身部署的 maven 庫管理軟件。Sonatype 提供了免費的 sonatype/nexus,bintray 提供了免費的 artifactory-oss。
爲了部署簡便,筆者使用了 docker 進行這兩個私服搭建。對於 Docker 是什麼,怎麼用,並非系列文章的重點。有興趣能夠自行學習。入門文檔在此。
下載安裝 docker 鏡像
1 |
$ docker pull sonatype/nexus |
運行鏡像
1 |
$ docker run -d -p 8081:8081 --name nexus sonatype/nexus:oss |
訪問服務器
由於的 docker-machine ip 是:192.168.99.100
,因而能夠經過在瀏覽器訪問http://192.168.99.100:8081/
這個 URL 來訪問 sonatype 私服。
你會發現這個界面跟你在https://oss.sonatype.org/
看到的幾乎同樣。
默認帳戶密碼是:
1 2 |
admin admin123 |
設置倉庫
點擊左側 repository,會出現 repository 的列表,把其中的 Releases
的 Configutation->Access Setting-> Deploy Polocy
設置成 Allow Redeploy
使得能夠重複發佈包。
配置和使用插件
我仍是使用了 chrisbanes/gradle-mvn-push 插件,稍微改動了一下字段的值,主要改動是環境配置,如帳號密碼,repository URL 等,具體請參考這裏 mvn-push。
關鍵設置
要在gradle.properties
中設置:
1 2 3 4 5 6 |
PROJ_NAME=sonatyaar PROJ_ARTIFACTID=sonatyaar PROJ_POM_NAME=Sonatye Aar Repository POM_PACKAGING=aar RELEASE_REPOSITORY_URL=http://192.168.99.100:8081/content/repositories/releases SNAPSHOT_REPOSITORY_URL=http://192.168.99.100:8081/content/repositories/snapshots |
查看
上傳成功以後,就能夠在瀏覽器的http://192.168.99.100:8081/content/repositories/releases
看到這個包。並可引用了。
錯誤
上傳的時候,返回400,多是Configutation->Access Setting-> Deploy Polocy
沒設置好;返回401,多是帳號密碼錯誤。
bintray 其實提供了多個私有部署倉庫的版本,分別是:
1 2 3 |
Artifactory OSS Artifactory Pro Artifactory Pro Registry |
按名字來看,後二者多是收費的,這裏就只介紹 Artifactory OSS
,依然是使用 docker 進行部署運行。更詳細的使用手冊,參考 Running with Docker。
下載鏡像(截止至2016-01-27,最新版本是4.4.1)
1 |
$ docker pull jfrog-docker-reg2.bintray.io/jfrog/artifactory-oss:4.4.1 |
運行鏡像
1 |
$ docker run -d -p 8080:8081 jfrog-docker-reg2.bintray.io/jfrog/artifactory-oss:4.4.1 |
在瀏覽器中訪問http://192.168.99.100:8080/
,默認帳號密碼是:
1 2 |
admin password |
筆者寫到這,發現這個篇幅已經太長了。如今的讀者,其實也沒有太多耐心看長篇大論,因此考慮將更詳細的私服的部署,放在一篇獨立的博客中講解。
爲了方便讀者使用 gradle 將 aar、jar包推送到 jcenter 和 mavenCentral,筆者決定將本文所使用的 sample 項目,分離出一個獨立的 github 項目:kevinho/gradle-maven-complete,裏面包含以下範例:
基本上覆蓋到了主流的場景了,但願我這個小輪子,也能幫助你們,喜歡記得 star 哦!
這一篇,筆者結合實例講解了 maven 倉庫相關的知識,以及將 maven 包經過 gradle 插件上傳本地倉庫、bintray jcenter、sonatype mavenCentral,還簡要介紹了 sonatype 和 artifactory 私服的 docker 搭建。或許你已經蠢蠢欲動了,那就趕忙打開你的電腦,把你的輪子,用 maven 武裝起來吧!下一篇會介紹 gradle 插件的編寫以及發佈使用,敬請期待!