赞
踩
Gradle让Android中的依赖管理、库管理、渠道管理以及一些动态地编译配置变得极为方便!!
本文是对Gradle在Android项目中进阶使用的知识点整理和简要讲解
较为详细的Gradle教科书Github
Gradle脚本配置文档:Google Github
下面简述对我们工程最重要的几个Gradle文件,后续也会围绕他们进行详细讲解和补充
(请仔细看代码中的注释哈)
build.gradle
: 工程控制Gradle编译配置build.gradle
: 控制每个Module的编译过程gradle.properties
: gradle动态参数的配置文件local.properties
: 本地的配置,如:SDK位置gradle-wrapper.properties
:gradle本地代理,声明了指向目录和版本
distributionUrl
: 指定gradle版本不存在时,就从Value的地址中去下载。很多时候,我们只要版本换成我们本地存在的gradle版本就可以了settings.gradle
: 配置Gradle中的Module管理 ~
表示 gradlew (Mac 使用gradle ,没有w)gradlew task -all
: 罗列出所有Task ,同时携带具体作用和相互关系gradlew assembleDebug
: 导出所有渠道测试包
~ assembleRelease
: 导出所有渠道正式包~ assembleBaiduDebug --stacktrace
: 导出指定渠道测试包,同时携带异常信息~ --stop
: 立即停止编译
~ check
: 检查任务~ build
: 执行了 check和assemble~ clean
: 清除所有中间编译结果 在Gradle中动态配置资源参数
我们可以根据各自的需求在不同的领域(如:buildType 的debug, defaultConfig ...)下去动态替换或配置我们项目中所使用到的资源,如 log 开关, 针对不同渠道的对应内容字段,不同版本定义引入的不同值等等
首先说清一点,对于动态资源在build.gradle
中多个领域中的使用,会遵循一下顺序来进行覆盖:
buildType > productFlavor > defaultConfig > Manifest中的配置 > 依赖的第三方库的配置 > 任意领域中的默认值(也就是没有设置值)
Manifest
占位符: 可以动态配置Manifest的参数- 在Manifest的Application节点下
- //这里以友盟为例
- <!-- 友盟统计相关meta-data -->
- <meta-data
- android:name="UMENG_APPKEY"
- android:value="balabalabala" />
- <meta-data
- android:name="UMENG_CHANNEL"
- android:value="${UMENG_CHANNEL_VALUE}" />
-
- 在build.gradle中对参数进行动态配置
- productFlavors {
- baidu {
- manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
- }
- }
gradle.properties
的使用
systemProp.proName=123
System.properties['proName']
ray.proName=123
,无key : proName = 123
project。property('proName')
, 无key : proName
- gradle.properties中的配置
-
- #使用系统参数配置
- systemProp.keyStore=ray.jks
- #使用key/value键值对配置
- ray.keyPassword=123456
- #属性配置
- mKeyAlias=ray
-
- build.gradle中的使用
- //签名打包
- release {
- //签名文件所在路径
- storeFile file(System.properties['keyStore'])
- //签名密码
- storePassword "111111"
- //别名
- keyAlias mKeyAlias
- keyPassword project.property('ray.keyPassword')
- }
BuildConfig
文件
app/build/generated/source/buildConfig
文件夹下面buildConfigField "String" , "key" , "\"value\""
buildConfig
中的属性参数 String
: 参数类型(int,boolean...), key
: 属性的名字, value
: 属性的值, \
为转义字符resValue
动态修改工程资源
resValue("string","key","value")
string
表示 会在 app/build/generated/res/resValue/.../generated.xml
中生成对应的String 的 key 和Value, 代码中可以直接getResources().getString(R.string.key);
获取到value- //下面模拟在不同渠道下修改资源参数
- productFlavors{
- baidu{
- buildConfigField "String" , "productCode" , "\"baidu 1.0\""
- resValue("string","productName","baidu")
- }
- }
build.gradle
的buildscript
中声明ext
和自定义属性,然后在其他module中就可以直接使用这个属性了-
- Project : build.gradle
- buildscript {
- //自定义工程使用的属性
- ext {
- kotlin_version = '1.1.0'
- compile_version = 25
- }
-
- //声明依赖Android Gradle 插件版本
- dependencies {
- classpath 'com.android.tools.build:gradle:2.3.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
- }
-
- Module : build.gradle 中使用
- Android
- {
- compileSdkVersion compile_version
- }
- dependencies
- {
- compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
- }
text.gradle
, 然后在Project
中引入,最后就可以在其他的module中直接使用了,具体应用可以参考印象笔记Github- text.gradle 编写如下:
- ext {
- kotlinVersion = "1.1.0"
- rxjavaLibVersion = "1.2.0"
-
- dependencies = [
- // Rx
- rxJava: "io.reactivex:rxjava:$rxjavaLibVersion"
- ]
- }
-
- Project : build.gradle 中引入
- //就是引入他的相对根目录路径
- apply from: 'config/text.gradle'
-
- Module : build.gradle 中使用
- rootProject.ext.XXXXX
-
- dependencies {
-
- def dependencies=rootProject.ext.dependencies
-
- compile dependencies.rxJava
-
- compile "org.jetbrains.kotlin:kotlin-stdlib:$rootProject.ext.kotlinVersion"
-
- }
- //声明引入的参数配置文件
- apply from: 'config/dependencies.gradle'
- apply from: 'config/text.gradle'
-
- //编译配置
- buildscript {
-
- //自定义参数
- ext {
- kotlin_version = '1.1.0'
- compile_version = 25
- }
-
- //Gradle指定使用jcenter代码仓库
- repositories {
- jcenter()
- }
- //声明依赖Android Gradle 插件版本
- dependencies {
- classpath 'com.android.tools.build:gradle:2.3.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
- }
-
- //这里可以为项目整体配置属性
- allprojects{
- repositories {
- jcenter()
- }
- }
-
- //任务:每次构建的时候删除指定目录
- task clean(type: Delete) {
- delete rootProject.buildDir
- }
控制每个Module的编译过程以及具体的参数配置
基本配置信息
- //默认配置
- defaultConfig {
- //包名
- applicationId "com.rayhahah.gradledemo"
- //最低版本
- minSdkVersion 19
- //目标版本
- targetSdkVersion 25
- //版本代码
- versionCode getVersinCode()
- //版本
- versionName "1.0"
- //自动化测试
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
-
- resValue "int","test","1"
- }
签名配置信息
- signingConfigs {
-
- //debug模式签名文件
- debug {}
- //签名打包
- release {
- //签名文件所在路径
- storeFile file("ray.jks")
- //签名密码
- storePassword "111111"
- //别名
- keyAlias "rayhahah"
- keyPassword "111111"
- }
-
- //自定义签名配置
- ray{
- //和上面的属性一致,根据个人需求实现不同配置
- }
- }
编译类型 : 指定编译不同类型情况下的不同配置信息
这里是列举了部分属性和方法,全部的方法和属性请看官网文档
- //构建配置
- buildTypes {
- release {
-
- //是否启用资源优化
- minifyEnabled
- //启用舍弃无用资源,只有当开启混淆才能够启用
- shrinkResources false
- //指定混淆文件
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
-
- //指定我们release包的输出文件名就是我们的渠道名字
- applicationVariants.all { variant ->
- variant.outputs.each { output ->
- def outputFile = output.outputFile
- if (outputFile != null && outputFile.name.endsWith(".apk")) {
-
- def fileName = "${variant.productFlavors[0].name}" + ".apk"
- output.outputFile = new File(outputFile.parent, fileName);
- }
- }
- }
- }
-
- debug {
-
- }
-
- //继承
- // rayhahah.initWith(debug)
-
- //自定义buildType
- rayhahah {
- //指定签名配置文件
- signingConfig signingConfigs.debug
- //包名增加后缀
- applicationIdSuffix ".ray"
-
- }
- }
配置资源逻辑组
- 指定Android所需要文件夹所在具体路径
-
- sourceSets {
- //这样的配置适用于将Eclipse中的项目结构迁移到AndroidStudio中
- main {
- //指定src资源目标目录
- java.srcDirs = ['src']
- //指定asset的目标目录
- assets.srcDirs = ['assets']
- //指定res的目标目录
- res.srcDirs = ['res']
- //指定依赖C文件的目标目录
- jni.srcDirs = ['jni']
- //指定依赖so文件的目标目录
- jniLibs.srcDirs = ['libs']
- //指定Manifest的目标文件路径
- manifest.srcFile 'AndroidManifest.xml'
- }
- }
- 1. 在res下新建文件夹 layouts(其实叫什么都无所谓)
- 2. 然后在 layouts下 新建你要分的包 如: activity,fragment 或按照业务模块来分
- 3. 在分包内新建Android resource directory -> layout 不要改名字
- 4. 在module:build gradle 如下配置
- 5. 然后将以前的layout文件拷贝到对应分包的layout下就可以使用了
-
- PS:只有在Project目录才能看到,Android目录结构是看不到的
-
- sourceSets {
- main {
- res.srcDirs = [
- 'src/main/res/layouts/activity',
- 'src/main/res/layouts/fragment',
- 'src/main/res/layouts',
- 'src/main/res'
-
- ]
- }
- }
打包渠道配置信息(仔细看代码注释)
- //多渠道打包配置
- //利用Manifest占位符动态参数配置
- productFlavors {
- baidu {
- manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
- }
-
- wandoujia {
- manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
- }
-
- googleplayer {
- manifestPlaceholders = [UMENG_CHANNEL_VALUE: "googleplayer"]
- }
-
- //不想每一个都去配置渠道名称也可以用下面这个函数
- //这个函数可以将 Manifest中的占位符替换成 每个渠道的名字
- productFlavors.all {
- flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
- }
- }
- //编译配置项
- //主要配置Java编译版本
- compileOptions {
- sourceCompatibility org.gradle.api.JavaVersion.VERSION_1_8
- targetCompatibility org.gradle.api.JavaVersion.VERSION_1_8
- }
-
- //lint配置项
- lintOptions {
-
- //启用出错停止grgradle构建
- abortOnError false
-
- // true--检查所有问题点,包含其他默认关闭项
- checkAllWarnings true
-
- // 关闭指定问题检查
- disable 'TypographyFractions','TypographyQuotes'
- // 打开指定问题检查
- enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
- // 仅检查指定问题
- check 'NewApi', 'InlinedApi'
-
- // true--生成HTML报告(带问题解释,源码位置,等)
- htmlReport true
- // html报告可选路径(构建器默认是lint-results.html )
- htmlOutput file("lint-report.html")
-
- // 忽略指定问题的规则(同关闭检查)
- ignore 'TypographyQuotes'
- }
AndroidStuido 给我们提供了十分友好地对于Build.gradle 图形化配置的界面,使用如下:
图形化界面
- //dependencies : 当前Android Module构建过程中所依赖的所有库
- dependencies {
- //依赖指定目录下所有指定后缀的文件
- compile fileTree(dir: 'libs', include: ['*.jar'])
- //测试工具依赖
- testCompile 'junit:junit:4.12'
- //远程库的依赖 (当从远程库中下载一次过后,就会缓存到本地了)
- //默认远程库配置为 jcenter()
- compile 'com.android.support:appcompat-v7:25.2.0'
-
- //依赖指定文件(这里依赖的是jar包)
- compile file('libs/test-1.0.0.jar')
- //依赖本地项目库
- compile project(':testLibrary')
-
- //格式: groupId: com.squareup.retrofit2
- // artifactId : retrofit
- // version: 2.1.0
- // SNAPSHOT : 表示依赖 retrofit 及其依赖的所有项目,如果他所依赖的项目在本项目中重复出现依赖,则只依赖retrofit项目中的。
- // @aar : 表示只依赖retrofit,不依赖他所依赖的项目
- compile ('com.squareup.retrofit2:retrofit:2.1.0-SNAPSHOT@aar')
- {
- //强制刷新远程库,避免远程库刷新,本地未更新
- transitive = true
- //exclude : 单独去除okhttp3的依赖
- exclude module : 'com.squareup.okhttp3:okhttp:3.3.0'
- }
-
- }
so库依赖
src/main
目录下创建jniLibs
,然后将so文件拷贝进去就可以了sourceSet
指定jniLib
的目标目录来自定义管理依赖的so文件存放本地Module依赖
build.gradle
的dependencies
领域中添加 compile project(':testLibrary')
setting.gradle
中添加module到include
中 如:include ':app',':testLibrary'
在Gradle中你可以写方法供 配置信息动态调用
- //自定义函数
- def getVersinCode() {
- // ......
- }
-
- Android{
- defaultConfig{
- versionCode getVersinCode()
- }
- }
gradlew build -profile
: 编译工程同时生成编译性能分析文件,在根目录build/reports/profile/profile-xxxx.xxx....html
,通过浏览器打开以后Task Execution
profile.html
TaskExcution
可以看到lint耗时最多,然后我们就可以根据自己项目中的具体情况来做优化
Project:build.gradle
中的 buildScript
中动态配置编译时禁用即可, 代码:gradle.startParameter.excludedTaskNames.add('lint')
就可以实现禁用了,具体需要继续禁用的可以根据项目输出的编译分析文件来作出添加和调整aapt即Android Asset Packaging Tool,在SDK的build-tools目录下。该工具可以查看,创建, 更新ZIP格式的文档附件(zip, jar, apk)。也可将资源文件编译成二进制文件,尽管你可能没有直接使用过aapt工具,但是build scripts和IDE插件会使用这个工具打包apk文件构成一个Android 应用程序(百度百科)
- aaptOtions{
- cruncherEnabled = false
- }
gradle.properties
中配置- //开启守护线程支持
- org.gradle.daemon=true
-
- //开启并行编译
- org.gradle.parallel=true
- //按需编译
- org.gradle.configureondemand=true
-
- //手动配置Gradle编译时内存分配
- # Default value: -Xmx10248m -XX:MaxPermSize=256m
- org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-
- # 开启JNI编译支持过时API
- android.useDeprecatedNdk=true
build.gradle
中配置- //增加编译内存到4g
- dexOptions{
- incremental true
- javaMaxHeapSize "4g"
- }
-
- task testTask << {
-
- println 'testTask << print'
-
- // 表示在task最前面来执行的过程
- doFirst {
- println 'testTask do first'}
- // << 和 doLast表示对当前task追加执行过程,效果是一样的
- doLast{
- println 'testTask do last!'}
- }
-
- //task之间的依赖 dependsOn
- //当执行存在依赖的task时,会先执行他的父类也就是依赖目标
- task testDependsOn(dependsOn:testTask){
- println 'testDependsOn default print '
- }
- //或者
- testDependsOn.dependsOn testTask
- //当执行testDependsOn是 打印顺序: testDependsOn default print -> testTask do first -> testTask << print -> testTask do last!
- //顺序总结为:
- //1.不加doLast和doFirst的最先执行
- //2.依赖task优先级高于自己的doFirst和doLast
- //3.同一个task中的doLast按从上向下顺序执行
- //4.同一个task中的doFirst按从下到上倒序执行
- //5.同一个task的doFirst优先级高于doLast
-
- //显示声明类型为Copy, 不声明默认为defaultTask
- task testCopy(type : Copy){
- //将当前gradle文件从src目录拷贝到dst目录
- from "src"
- into "dst"
- }
- //每一个特定的Task类型还可以含有特定的Property,比如Copy的from和to等。
-
- //自定义property
- ext.testProperty = ""
-
- task testExtProperty << {
- //直接使用自定义的property
- println testProperty
- }
- //局部自定义Task
- //直接在build.gradle中自定义Task
- //但是也只能在当前module中引用
- class TestCustomTask extends DefaultTask {
- //@Optional,表示在配置该Task时,message是可选的。
- @Optional
- String message = 'I am jjx'
- //@TaskAction表示该Task要执行的动作,即在调用该Task时,hello()方法将被执行
- @TaskAction
- def hello() {
- println "hello world $message"
- }
- }
-
- //hello使用了默认的message值
- task hello(type: TestCustomTask)
-
- //重新设置了message的值
- task helloOne(type: TestCustomTask) {
- message = "I am a android developer"
- }
-
-
- 全局自定义Task
- 如果需要自定义大量的Task,就要新建一个Gradle文件来统一管理
- 通过apply来引入使用
- //这是插件
- apply plugin: 'com.android.application'
- //这里gradle-quality.gradle就是另外单独定义了task的gradle
- apply from: '../build-config/gradle-quality.gradle'
自定义Plugin可以让我们在工程编译根据需求中自动去完成一些操作
下面就是一个编译后自动打印当前时间的Plugin
- 与自定义Task十分类似
- 可以在build.gradle中自定义plugin
- apply plugin: DateAndTimePlugin
-
- dateAndTime {
- timeFormat = 'HH:mm:ss.SSS'
- dateFormat = 'MM/dd/yyyy'
- }
-
- class DateAndTimePlugin implements Plugin<Project> {
- //该接口定义了一个apply()方法,在该方法中,我们可以操作Project,
- //比如向其中加入Task,定义额外的Property等。
- void apply(Project project) {
- //加载Extension
- project.extensions.create("dateAndTime", DateAndTimePluginExtension)
-
- //使用Extension配置信息
- project.task('showTime') << {
- println "Current time is " + new Date().format(project.dateAndTime.timeFormat)
- }
-
- project.tasks.create('showDate') << {
- println "Current date is " + new Date().format(project.dateAndTime.dateFormat)
- }
- }
- }
- //每个Gradle的Project都维护了一个ExtenionContainer,
- //我们可以通过project.extentions进行访问
- //比如读取额外的Property和定义额外的Property等。
- //向Project中定义了一个名为dateAndTime的extension
- //并向其中加入了2个Property,分别为timeFormat和dateFormat
- class DateAndTimePluginExtension {
- String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS"
- String dateFormat = "yyyy-MM-dd"
- }
其实本质上就是对上面的自定义Plugin结构化拆解
Plugin目录创建
buildSrc
buildSrc/src/main/groovy/com/ray
和 buildSrc/src/main/resources/META-INF/gradle-plugins
创建buildSrc/build.gradle
, 配置如下
- apply plugin:'groovy'
-
- dependecies{
- compile gradleApi()
- compile localGroovy()
- }
Plugin
逻辑实现buildSrc/src/main/groovy/com/ray
下创建DateAndTimePlugin
- class DateAndTimePlugin implements Plugin<Project> {
- void apply(Project project) {
- //加载Extension
- project.extensions.create("dateAndTime", DateAndTimePluginExtension)
-
- //使用Extension中的配置信息
- project.task('showTime') << {
- println "Current time is " + new Date().format(project.dateAndTime.timeFormat)
- }
-
- project.tasks.create('showDate') << {
- println "Current date is " + new Date().format(project.dateAndTime.dateFormat)
- }
- }
- }
Extension
实现Extension
相当于Gradle配置信息(相当于实体类),然后主项目的build.gradle
通过Extension
传递配置(相当于赋值) 同样在buildSrc/src/main/groovy/com/ray
下创建DateAndTimePluginExtension
- //每个Gradle的Project都维护了一个ExtenionContainer,
- //我们可以通过project.extentions进行访问
- //比如读取额外的Property和定义额外的Property等。
- //向Project中定义了一个名为dateAndTime的extension
- //并向其中加入了2个Property,分别为timeFormat和dateFormat
- class DateAndTimePluginExtension {
- String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS"
- String dateFormat = "yyyy-MM-dd"
- }
Plugin命名
自定义外部引用时Plugin的名字
在buildSrc/src/main/resources/META-INF/gradle-plugins
下创建
timePlugin.properties
,内容只有一行代码 : implementation-class = com.ray.DateAndTimePlugin
Plugin的使用
主项目中apply plugin:'timePlugin'
可选:配置Extension
:
- timePlugin{
- //动态修改和配置Extension属性
- //这里修改了日期格式
- timeFormat = 'MM/dd/yyyy'
- }
ModulePlugin目录
buildSrc/build.gradle
的修改 如下:- apply plugin: 'groovy'
- //增加Maven的支持
- apply plugin: 'maven'
-
- version = 1.0
- group = 'com.ray.plugin'
- archivesBaseName = 'timeplugin'
- repositories.mavenCentral()
-
- dependencies {
- compile gradleApi()
- groovy localGroovy()
- }
-
- //将插件部署到repo目录下
- uploadArchives {
- repositories.mavenDeployer {
- repository(url: uri('../repo'))
- }
- }
- apply plugin: 'timePlugin'
-
- buildscript {
- repositories {
- maven {
- url uri('../repo')
- } }
- dependencies {
- classpath group: 'com.ray.plugin', name: 'timePlugin',
- version: '1.0'
- }
- }
以上就是总结的Gradle实用的进阶指南,让我们可以更加随心所欲地去管理我们的工程。以后如果有一些新的认识或者想法,我也会在这里实时更新的。也算是自己对Gradle认识和学习的总结整理吧。
正确使用Gradle的配置是为了让我们开发更加便捷、效率更高,千万不要本末倒置了。
文中哪里有错误的话,欢迎大家指出纠正
如果文章对你有用的话,请点赞鼓励一下哈O(∩_∩)O~~~~
参考和感谢以下博文和项目:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。