Android中Gradle常用配置

文/ZYRzyr
原文链接:http://www.jianshu.com/p/7ca459085b96

2017年08月15日更新,不知道简书出什么BUG了,重新发布更新一下,原来的目录标签全部失效变成文字了,无奈之下只有删掉,简直坑爹,待过几日再看能否重新启用目录。

前言

本文记录了一些常用的gradle配置,基本上都是平时开发中可能会使用到的,如果有新内容会不定时更新,附官网

1.依赖库版本写法
  • 不推荐写法:
dependencies {
    compile 'com.example.code.abc:def:2.+' // 不推荐的写法
}

这样写虽然可能保证每次都保持库是最新的,但同时会有以下更严重的问题
1)多人开发时,每个人可能会得到不同的最新版本,带来潜在的隐患;
2)库更新后,可能会由于库的内部代码的修改而引起不易发现的BUG;
3)每次build时会联网检查,增加build时间。

  • 推荐写法(固定版本):
dependencies {
    compile 'com.example.code.abc:def:2.0.1' // 固定版本,有需要时再进行修改
}
2.全局设置编码

在最顶层的build.gradle中添加:

allprojects {
    repositories {
        jcenter()
    }

    tasks.withType(JavaCompile){
        options.encoding = "UTF-8"
    }
}
3.设置Java编译版本

module中的build.gradle中:

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}

全局配置则在最顶层的build.gradle中:

allprojects {
    repositories {
        jcenter()
    }

    tasks.withType(JavaCompile) {
        sourceCompatibility = JavaVersion.VERSION_1_7
        targetCompatibility = JavaVersion.VERSION_1_7
    }
}
4.Release版本的密码配置

密码、签名等敏感信息可以统一进行存放,不进行硬编码。写在gradle.properies中,可以随意的定义key-value形式,此文件是gradle自动引入的。
例:gradle.properies文件如下

STORE_FILE_PATH ../test_key.jks
KEYSTORE_PASSWORD 123456
KEY_ALIAS abc
KEY_PASSWORD 654321
PACKAGE_NAME_SUFFIX .test
TENCENT_AUTHID aaa0123

modulebuild.gradle文件:

signingConfigs {
    release {
        try {
              storeFile file(STORE_FILE_PATH)
              storePassword STORE_PASSWORD
              keyAlias KEY_ALIAS
              keyPassword KEY_PASSWORD
        }
        catch (ex) {
            throw new InvalidUserDataException("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties.")
        }
    }
}
5.设置第三方maven仓库

在顶层的build.gradle中:

allprojects {
    repositories {
        maven {
            url 'http://repo.xxxx.net/nexus/'
            name 'maven name'
            credentials {
                username = 'username'
                password = 'password'
            }
        }
    }
}

其中name和credentials是可选项,视具体情况而定。如使用阿里云仓库如下:

allprojects {
    repositories {
        maven { 
           url "http://maven.aliyun.com/nexus/content/groups/public/" 
       }
    }
}
6.自动删除unaligned apk

每次打包后都会有一个unaligned的apk文件生成,这个文件对于开发来说无意义,可以配置一个task来删除它。
写在modulebuild.gradle中:

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
}
android.applicationVariants.all { variant ->
    variant.outputs.each { output ->
        // 删除unaligned apk
        if (output.zipAlign != null) {
            output.zipAlign.doLast {
                output.zipAlign.inputFile.delete()
            }
        }
    }
}
7.lint选项开关

lint默认会做严格检查,遇到包错误会终止构建过程。可以用如下开关关掉这个选项,非特殊情况不最好不配置。
写在modulebuild.gradle中:

android {
    lintOptions {
        disable 'InvalidPackage'
        checkReleaseBuilds false
        abortOnError false
    }
}
8.依赖项目中的module和jar

工程可以依赖自身的module和jar文件,如下:

dependencies {
    compile project(':myExamplelibraryModule')
    compile files('libs/sdk-1.1.jar')
}
9.根据buildType设置包名

写在modulebuild.gradle中:

android {
    defaultConfig {
        applicationId "example" // 这里设置了example作为默认包名
    }

    buildTypes {
        release {
            applicationIdSuffix '.abc.gradle' // 设置release时的包名为example.abc.gradle
        }
        debug{
            applicationIdSuffix '.abc.debug' // 设置debug时的包名为example.abc.debug
        }
    }
10.替换AndroidManifest中的占位符

在manifest中可以有类似{appName}这样的占位符,在modulebuild.gradle中可以将其进行赋值。

android{
    defaultConfig{
        manifestPlaceholders = [appName:"@string/app_name"]
    }
}
11.定义全局变量

先在顶层的build.gradle定义全局变量:

ext {
    minSdkVersion = 19
    targetSdkVersion = 24
}

然后在各modulebuild.gradle中可以通过rootProject.ext来引用:

android {
    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
    }
}

如果是在当前文件中定义的,就不用添加rootProject.ext

12.定义局部变量

有时候一个库会被引用多次,或者一个库有多个依赖,但这些依赖的版本都是统一的。通过ext来定义一些变量,这样在用到的时候就可以统一使用了:

ext {
    rxandroidVersion = '2.0.1'
    rxjavaVersion = "2.1.0"
}

dependencies {
      ...
      compile "io.reactivex.rxjava2:rxandroid:$rxandroidVersion"
      compile "io.reactivex.rxjava2:rxjava:$rxjavaVersion"
}
13.动态设置额外信息

假如想把当前的编译时间、编译的机器、最新的commit版本添加到apk中,动态设置如下:

android {
    defaultConfig {
        resValue "string", "build_time", buildTime()
        resValue "string", "build_host", hostName()
        resValue "string", "build_revision", revision()
    }
}
def buildTime() {
    return new Date().format("yyyy-MM-dd HH:mm:ss")
}
def hostName() {
    return System.getProperty("user.name") + "@" + InetAddress.localHost.hostName
}
def revision() {
    def code = new ByteArrayOutputStream()
    exec {
        commandLine 'git', 'rev-parse', '--short', 'HEAD'
        standardOutput = code
    }
    return code.toString()
}

上述代码实现了动态添加了3个字符串资源: build_timebuild_hostbuild_revision, 在其他地方可像引用strings.xml中的字符串一样使用:

getString(R.string.build_time)  // 输出2017-06-12 15:05:05
getString(R.string.build_host)  // 输出zyr@example,这是我的电脑的用户名和PC名
getString(R.string.build_revision) // 输出5ff4x89, 这是最后一次commit的sha值

上面讲到的是植入资源文件,同样可以在BuildConfig.class中增加自己的静态变量:

defaultConfig {
    applicationId "zyr.gradle.demo"
    minSdkVersion 19
    targetSdkVersion 24

    buildConfigField("boolean", "IS_TEST", "true") // 定义一个bool变量

    resValue "string", "build_time", "2017.06.12" // 上面讲到的植入资源文件
}

syncBuildConfig中就有定义的这个变量了:

public final class BuildConfig {
      public static final boolean DEBUG = Boolean.parseBoolean("true");
      public static final String APPLICATION_ID = "zyr.gradle.test";
      public static final String BUILD_TYPE = "debug";
      public static final String FLAVOR = "";
      public static final int VERSION_CODE = 1;
      public static final String VERSION_NAME = "1.0.0";

      // Fields from default config.
      public static final boolean IS_TEST = true;
}

如果有带引号的string,需要转义:

 buildConfigField "String", "URL_ENDPOINT", "\"http://your.development.example.com/\""
14.使用init.with继承配置

如果想新增一个buildType,又想新的buildType继承之前配置好的参数,使用init.with()

buildTypes {
        release {
            zipAlignEnabled true
            minifyEnabled true
            shrinkResources true // 是否去除无效的资源文件
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
            signingConfig signingConfigs.release
        }

        abcd.initWith(buildTypes.release) // 继承release的配置
        abcd {
             ...
        }
    }
15.exlude关键字

遇到库冲突的问题时,将冲突的库通过exclude来剔除:

  • 剔除整个组织的库:
compile ('com.facebook.fresco:animated-webp:0.13.0') {
       exclude group: 'com.android.support' // 仅仅写组织名称
}
  • 剔除某个库:
compile('com.android.support:appcompat-v7:23.2.0') {
    exclude group: 'com.android.support', module: 'support-annotations' // 写全称
    exclude group: 'com.android.support', module: 'support-compat'
    exclude group: 'com.android.support', module: 'support-v4'
    exclude group: 'com.android.support', module: 'support-vector-drawable'
}
16.聚合依赖多个库

有时候一些库是一并依赖的,剔除也是要一并剔除的,此时进行统一引入:

compile(['io.reactivex.rxjava2:rxandroid:2.0.1',
           'io.reactivex.rxjava2:rxjava:2.1.0'])

这样别的开发者就知道哪些库是有相关性的,在删除库的时候也更方便。

17.跳过task

Gradle每次构建时都执行了许多task,其中有一些task是不需要的,可以把它们都屏蔽掉,如下:

tasks.whenTaskAdded { task ->
    if (task.name.contains('AndroidTest') || task.name.contains('Test')) {
         task.enabled = false
    }
}

这样在build时就会跳过包含AndroidTest和Test关键字的task了。
NOTE:有时候自己也会写一些task或者引入一些gradle插件和task,通过这种方式可以简单的进行选择性的执行。

18.通过逻辑判断来跳过task

上面有提到过动态获得字段,但有些东西是在打包发布的时候用,有些则是在调试时用,需要区分不同的场景,定义不同的task。下面以通过“以gitcommit号当做版本号”这个需求做例子:

def cmd = 'git rev-list HEAD --first-parent --count'
def gitVersion = cmd.execute().text.trim().toInteger()
android {
    defaultConfig {
      versionCode gitVersion
    }
}

因为上面的操作可能比较慢,或者在debug时没必要,所以就做了如下判断:

def gitVersion() {
      if (!System.getenv('CI_BUILD')) { // 不通过CI进行build的时候返回1
          return 1    //1无实际意义,return用于中断后续操作
      }
      def cmd = 'git rev-list HEAD --first-parent --count'
      cmd.execute().text.trim().toInteger()
}
android {
      defaultConfig {
        versionCode gitVersion()
      }
}

这里用到了System.getenv()方法,可以参考JavaSystem下的getenv(),就是得到当前的环境。

19.引用全局的配置文件

在根目录中建立一个config.gradle文件:

ext {
    android = [
            compileSdkVersion: 23,
            applicationId    : "com.zyr.gradle",
    ]

    dependencies = [
            "support-v7": "com.android.support:appcompat-v7:24.2.1",
    ]
}

然后在顶层的build.gradle中引入apply from: "config.gradle",即:

// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply from: "config.gradle" // 引入该文件
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'
    }
    // ...
}

之后就可以在其余的build.gradle中读取变量了:

defaultConfig {
      applicationId rootProject.ext.android.applicationId // 引用applicationId
      minSdkVersion 14
      targetSdkVersion 20
}
dependencies {
      compile rootProject.ext.dependencide["support-v7"] // 引用dependencide
}
20.区分不同环境下的不同依赖

除了通过buildtype来定义不同的依赖外,还可以通过写逻辑判断来做:
先在gradle.properties中写上:
needExample = true
然后在modulebuild.gradle中使用:

dependencies {
    //根据是不同情形进行判断
    if (!needExample) {
        provided fileTree(dir: 'libs', include: ['*.jar'])
    } else {
        compile 'com.android.support:example:1.0.0'
    }
    // ...
}
21.动态改变module种类

插件化有可能会要根据环境更改当前module是app还是lib:
先在gradle.properties中写上:
isDebug = false
然后在modulebuild.gradle中使用:

if (isDebug.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
22.定义库的私有混淆(针对库的开发)

有很多库是需要进行混淆配置的,但让库的使用者配置混淆文件的方式总是不太友好,consumerProguardFiles可以让库的作者在库中定义混淆参数,让使用者无需了解其混淆:

apply plugin: 'com.android.library'
android {
    compileSdkVersion 24
    buildToolsVersion '24.0.2'

    defaultConfig {
        minSdkVersion 9
        targetSdkVersion 24
        consumerProguardFiles 'consumer-proguard-rules.pro' //  自定义混淆配置
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

其中的consumer-proguard-rules.pro文件位置如下图:

consumerProguardFiles.jpg

打包工具会将*.pro文件打包进入aar中,库混淆的时候会自动使用此混淆配置文件。
consumerProguardFiles方式加入的混淆具有以下特性:

  • *.pro文件会包含在aar文件中
  • 这些pro配置会在混淆的时候被使用
  • 此配置针对此aar进行混淆配置
  • 此配置只对库文件(apply plugin: 'com.android.library)有效,对应用程序(apply plugin: 'com.android.application')无效
23.指定资源目录

modulebuild.gradle中通过下面的配置,可以自定义java代码和res资源的目录,一个和多个都可以,更加灵活:

android {
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            assets.srcDirs = ['assets']
            if (!IS_USE_DATABINDING) { // 如果用了databinding
                jniLibs.srcDirs = ['libs']
                res.srcDirs = ['res', 'res-vm'] // 多加了databinding的资源目录
            } else {
                res.srcDirs = ['res']
            }
        }

        test {
            java.srcDirs = ['test']
        }

        androidTest {
            java.srcDirs = ['androidTest']
        }
    }
}
24.定义多个Manifest

modulebuild.gradle中:

sourceSets {
    main {
        if (isDebug.toBoolean()) {
            manifest.srcFile 'src/debug/AndroidManifest.xml'
        } else {
            manifest.srcFile 'src/release/AndroidManifest.xml'
        }
    }
}

根据flavor也可以进行定义:

productFlavors {
    abc {
        manifest.srcFile 'abc/AndroidManifest.xml'
    }

    main {
        manifest.srcFile '/AndroidManifest.xml'
    }
}
最后

gradle可以做很多事,也非常灵活。希望上面提到的一些配置对大家有所帮助。有空去官网了解最好。
吐糟一句,简书的markdown真™难用!!

原文作者/ZYRzyr
原文链接:http://www.jianshu.com/p/7ca459085b96

请进入这里获取授权:https://101709080007647.bqy.mobi

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

推荐阅读更多精彩内容