Gradle for Android(三) 依赖管理

依赖管理是Gradle的一个亮点。在最好的情况下,你只需要在构建文件中添加一行代码,Gradle就可以从远程仓库下载依赖并确保可用。当你所需的依赖还有它自己的依赖时,Gradle会自动接管这些事情。依赖的依赖称为传递依赖(transitive dependencies)。

本章将介绍依赖管理的概念,并通过多种方式在Android工程中引入依赖。主要内容有:

  • 仓库
  • 本地依赖
  • 依赖的几个概念

仓库

我们所说的依赖通常是指外部依赖,比如其他开发者提供的库。手动管理依赖是非常困难的事情。你需要找到这个库,然后下载JAR包,并拷贝到工程中,最后再引用它。一般这些JAR包的名称中并不包含版本信息,所以你需要自己添加,以便日后升级。你还需要确保这些库保存在一个源管理系统中,这样其他团队成员才可以直接引用基础代码,而不需要手动去下载依赖。

使用仓库可以解决这些问题。一个仓库可以看做是一个文件集。Gradle默认不会为你的工程定义任何仓库,所以你需要自己在repositories块中添加。如果你使用的是Android Studio,它会帮你做这些。我们在前面的章节简单提到了repositories块,如下:

repositories {
    jcenter()
}

Gradle支持三种不同的仓库:Maven、Ivy和静态文件或者目录。在构建的execution期间,Gradle会从仓库中获取这些依赖。Gradle同样会保存一个本地的缓存,这样同一个版本的依赖只会下载一次。

一个依赖由三个元素定义:

  • group指明了创建这个仓库的组织,通常是域名的翻转。
  • name是仓库的唯一标识
  • version指定仓库的版本

基于这三个元素,一个依赖可以在dependencies块中这样声明:

dependencies {
    compile 'com.google.code.gson:gson:2.3'
    compile 'com.squareup.retrofit:retrofit:1.9.0'
}

这是Groovy中map的一种简写,全写如下:

dependencies {
    compile group: 'com.google.code.gson', name: 'gson', version:'2.3'
    compile group: 'com.squareup.retrofit', name: 'retrofit', version: '1.9.0'
}

一个依赖的必需元素是namegroupversion是可选的。尽管如此,我们仍建议添加上group以使依赖变得清晰,添加上version,这样可以确保依赖不会自动更新,否则会打断构建。

预置仓库

为了方便,Gradle预置了三个Maven仓库:JCenter,Maven Central和本地Maven仓库。可以添加如下代码来引入它们:

repositories {
    mavenCentral()
    jcenter()
    mavenLocal()
}

Maven Central和JCenter是两个广泛使用的在线仓库。你不必同时使用它们,我们推荐使用JCenter,这也是Android Studio创建的Android工程的默认仓库。JCenter是Maven Central的父集,所以当你做开关的时候,你可原封不动的保留已定义的依赖项。在此之上,它还支持HTTPS,这和Maven Central不同。

本地Maven仓库是一个你所使用的所有依赖的本地缓存,你也可以添加你自己的依赖。默认情况下,这个仓库在home目录的.m2文件夹下。在Linux或者Mac OS X中,路径为~/.m2,Windows中为%UserProfile%\.m2

除了预置的仓库,你也可以添加其他公有的或者私有的仓库。

远程仓库

有些组织创建有趣的插件或者仓库,并且倾向于将它们部署在自己的Maven或者Ivy服务器上。为了将它们添加到你的构建中,你只需要在maven块中添加URL地址:

repositories {
    maven {
        url "http://repo.acmecorp.com/maven2"
    }
}

这同样适用于Ivy仓库。Apache Ivy在Ant中是一个流行的依赖管理者。Gradle以与Maven库相同的格式支持这些仓库。

repositories {
    ivy {
        url "http://repo.acmecorp.com/repo"
    }
}

如果你的组织有自己的仓库,需要有证书才可以访问,可以这样写:

repositories {
    maven {
        url "http://repo.acmecorp.com/maven2"
        credentials {
            username 'user'
            password 'secretpassword'
        }
    }
}

Maven和Ivy使用同样的方式。

保存证书
在构建文件中保存证书不是一个好主意。构建文件是纯文本的,并被检入源控制系统。更好的方式是使用单独的Gradle properties文件来保存,就像我们在第二章看到的那样。

本地仓库

在你的硬盘或者网盘上运行Maven或者Ivy仓库是可行的。为将其添加到你的构建中,你只需要配置一个相对或者绝对的URL地址:

repositories {
    maven {
        url "../repo"
    }
}

新建的Android工程,默认依赖Android Support Library。在使用SDK Manager安装Google仓库时,会在你的硬盘上创建两个Maven仓库:ANDROID_SDK/extras/google/m2repositoryANDROID_SDK/extras/android/m2repository。这是Gradle获取Google提供的库的地址,比如:Android Spport Library和Google Play Services。

你也可以使用flatDirs添加一个常规目录作为仓库。它允许你在dependency块中从该目录添加文件。

repositories {
    flatDir {
        dirs 'aars'
    }
}

本章的后续内容中,在讲解库工程时, 我们会看一个使用它的例子。

本地依赖

某种情况下,你可能仍需要手动下载Jar包或者native库。或许你想创建你自己的仓库,这样就可以在多个工程中重复使用它,而且不必将他发布到公有或者私有的仓库中。在这种情况下,不可能去使用任何在线的资源,你需要使用其他的方式来添加依赖。我们会讲述如何使用文件依赖,如何包含native库,以及如何在你的工程中引用库工程。

文件依赖

你可以使用files方法来添加一个Jar文件依赖

dependencies {
    compile files('libs/domoarigato.jar')
}

文件过多时,你可以添加文件夹:

dependencies {
    compile fileTree('libs')
}

默认情况下,新建的Android工程有一个libs目录,并且被添加为依赖目录。你还可以添加过滤器,只添加Jar包为依赖:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

这意味着在Android Studio创建的任何工程中,你可以在libs文件夹下放置Jar包,它会被自动包含在编译路径和最终的APK中。

Native库

用C或C++编写的库可以编译成特定平台的native代码。这些库由几个.so文件组成,每个文件对应一个平台。Android 插件默认支持native库,你只需要在模块中创建jniLibs目录,并且为每个平台创建相应的子目录就可以了。最后把.so文件放入对应的子目录。

结构如下:

app
├── AndroidManifest.xml
└── jniLibs
    ├── armeabi
    │    └── nativelib.so
    ├── armeabi-v7a
    │    └── nativelib.so
    ├── mips
    │    └── nativelib.so
    └── x86
         └── nativelib.so

如果这个规则对你不适用,你可以在构建文件中自定义目录:

android {
    sourceSets.main {
        jniLibs.srcDir 'src/main/libs'
    }
}

库工程

如果你想分享一个使用了Android API或者包含Android资源的库,你需要创建一个库工程。库工程和应用工程大体相同。你可以使用同样的任务来构建和测试库工程,它们同样可以有不同的构建变体。不同的是输出。应用工程会生成一个APK,它可以安装和运行在Android设备上;库工程会生成一个.aar文件,该文件可以作为Android应用工程的库。

创建和使用库工程模块

在构建脚本中,使用Android库插件来代替Android应用插件:

apply plugin: 'com.android.library'

有两种方式可以在你的应用中包含一个库工程。

  • 将其作为模块包含在工程中
  • 创建一个.aar文件,该文件可以多个应用中重复使用。

第一种方式需要将模块添加到settings.gradle文件中,并将其作为应用模块的依赖。settings文件如下:

include ':app', ':library'

在这种情况下,库模块被称为library,相应的会有一个同名的文件夹。为了使用这个库,需要在应用模块的build.gradle文件将其添加为依赖:

dependencies {
    compile project(':library')
}

这样应用模块的类路径将会包含库的输出。我们将在第五章详细讨论这个方法。

使用.arr文件

如果你想创建一个可以在不同Android应用中重复使用的库,你可以构建一个.aar文件,然后将其作为依赖添加到工程中。构建库时,.aar文件会在模块目录的build/output/aar/文件夹下生成。为了使用它,你需要在应用模块中创建一个文件夹,将.aar文件拷贝其中,然后添加该目录为仓库。

repositories {
    flatDir {
        dirs 'aars'
    }
}

这样每个被放置其中的文件都会被依赖。你可以如下引用这些依赖:

dependencies {
    compile(name:'libraryname', ext:'aar')
}

这样Gradle就会添加libraryname.aar作为依赖。

依赖的几个概念

这里有一些依赖相关的概念,值得了解一下。其中一个是configurations,它解释了我们在本章用到的compile关键字。

Configurations

有时我们需要使用一些特定设备提供的SDK,比如某些厂商的蓝牙SDK。为了编译代码,你需要将SDK添加到compile classpath中。但你并不需要将其包含在APK中,因为它已经存在于设备上了。这时候依赖的配置就起作用了。

Gradle将依赖分成各种配置,由文件集命名。下面是Android应用或者库的标准配置:

  • compile
  • apk
  • provided
  • testCompile
  • androidTestCompile

compile配置是默认的,包含编译main应用所需的全部依赖。该配置中的依赖不仅包含在类路径中,还包含在生成的APK中。

apk配置只会包含在APK中,不会包含在类路径中。

providedapk相反。这两种配置只接受JAR依赖项,试图将库工程加入其中会导致错误。

testCompileandroidTestCompile会添加测试所需的特定外部库。这两个配置可以添加测试框架,比如JUnit或者Espresso。它们只在运行测试相关任务的时候才会用到。你只想在测试APK中使用这些框架,而不是发布的APK中。

除了这些标准的配置,Android插件还会为每个构建变体创建配置,比如debugCompile,releaseProvided等。如果你只想在debug版本添加一个日志框架,这将会非常有用。你会在第四章学习到更多。

语义版本控制

版本控制是依赖管理很重要的一个方面。添加到仓库(如JCenter)的依赖被认为遵循一组版本控制规则,称为语义版本控制。在语义版本控制下,版本号遵循major.minor.patch的规则,版本号的增长遵循如下规则:

  • major(主版本号)在API有不兼容的修改时增长
  • minor(小版本号)以向后兼容的方式添加功能时增长
  • patch(补丁)修改bug时增长

动态版本

某些情况下,你希望每次构建应用或者库时都能获得最新的依赖。最好的方式就是使用动态版本。下面是使用动态版本的几种方式:

dependencies {
    //获取最新的patch
    compile 'com.android.support:support-v4:22.2.+'
    //获取最新的小版本号至少是2的小版本
    compile 'com.android.support:appcompat-v7:22.2+'
    //获取最新版本
    compile 'com.android.support:recyclerview-v7:+'
}

你需要慎重使用动态版本。使用动态版本,Gradle可能会选择不稳定的版本,导致构建失败。更严重的是,构建服务器和你的本地机器可能会使用不同版本的依赖,导致应用的行为不一致。

在你使用动态版本时,Android Studio会给出警告。

Inside Android Studio

本节略过

依赖顺序

配置文件中依赖的排列顺序决定了优先级的高低,排在前面的库优先级大于排在后面的库。这个顺序在资源合并和清单文件元素合并的时候非常重要。

比如,如果你的项目声明如下:

  • 依赖于A和B,且A在B前面
  • A依赖于C和D,C在D前面
  • B依赖于C

那么,展开的依赖顺序如下:

  1. A
  2. D
  3. B
  4. C

这个顺序保证了A和B都可以覆写C;D的优先级高于B,因为A的优先级高于B。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容