Gradle


目录

1)Gradle简介
2)Android中的gradle

  • 2.1)gradle文件
  • 2.2)gradle wrapper
  • 2.3)配置输出文件格式
    • 2.3.1)基本用法
    • 2.3.2)使用签名文件进行签名
    • 2.3.3)多渠道包配置
  • 2.4)配置Gradle 编译速度
    3)Groovy基本语法
    4)Gradle插件开发

1)Gradle简介

  • mac在~/.bash_profile中添加如下代码
//此处是gradle路径,我本机在AS时已下载过4.1版本,此处我使用已下载好的地址
export GRADLE_HOME=~/.gradle/wrapper/dists/gradle-4.1-all/bzyivzo6n839fup2jbap0tjew/gradle-4.1
export PATH=${PATH}:${GRADLE_HOME}/bin
  • 保存配置
    source ~/.bash_profile
  • gradle -v
------------------------------------------------------------
Gradle 4.1
------------------------------------------------------------
Build time:   2017-08-07 14:38:48 UTC
Revision:     941559e020f6c357ebb08d5c67acdb858a3defc2
Groovy:       2.4.11
Ant:          Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM:          1.8.0_91 (Oracle Corporation 25.91-b14)
OS:           Mac OS X 10.12.4 x86_64

2)Android中的gradle

2.1)gradle文件

gradle文件
文件 说明
setting.gradle 文件定义了哪些module 应该被加入到编译过程
Project-build.gradle 此处的配置会被应用到所有项目中
Module-build.gradle 每个module的配置,会覆盖Project-build.gradle的相同部分
// ~/Project-build.gradle
buildscript {
    repositories {
        google()
        //一般用共有的jCenter仓库
        jcenter()
    }
    dependencies {
        //gradle-wrapper.properties中配置的是的Gradle的版本.
        //build.gradle中的依赖指定的是Gradle插件的版本.
        //对应关系请自查询
        classpath 'com.android.tools.build:gradle:3.0.0'
    }
}
//此处配置会应用至所有module中
allprojects {
    repositories {
        google()
        jcenter()

        //有一些项目,可能在公司的私有的仓库中
        maven {
           url "http://maven.aliyun.com/nexus/content/repositories/releases"
           //若有密码 则加上credentials
           credentials {
              username "xxx"
              password "xxx" 
          }
        }

        //也可以使用相对路径配置本地仓库
        flatDir {
          dirs "xxx"
        }
    }
}
// ~/Module-build.gradle
//android应用程序的gradle插件
apply plugin: 'com.android.application'
//apply plugin: 'com.android.library' //library项目
//apply plugin: 'java-library'

//关于android的配置项
android {
    compileSdkVersion 26
    //默认配置
    defaultConfig {
        applicationId "com.tgf.studygradle"
        minSdkVersion 21
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    //编译类型配置 默认的有debug、release 的类型
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
//依赖配置,定义了项目需要依赖的其他库
dependencies {
    //可以使用文件依赖
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //最新版的gradle已经使用api替代了compile。 api和implementation的依赖层次是有区别的,请查资料
    //一定要指名依赖版本,尽量不要使用通配符+,可以避免检查是否有新版本,也能做到工程化开发都统一依赖库版本
    //对于特定的buildTypes不同依赖的,可以使用debugImplementation或debugApi
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

2.2)gradle wrapper

gradle wrapper的诞生是为了兼容不断发展的Gradle版本。

//通过此命令可以手动创建wrapper脚本,AS创建工程时会自动创建
gradle wrapper
gradle wrapper  --gradle-version 4.1 //也可以加入版本号

下载目录

cd ~/.gradle/wrapper/dists
创建gradle
  • gradle-wrapper.properties中配置的是的Gradle的版本.
...
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
  • 通过wrapper可以使用命令行编译:
命令 说明
./gradlew ... mac系统
gradle.bat win系统
  • 使用 ./gradlew tasks可列出所有所有task


    gradlew tasks

    gradlew tasks
常用task 说明
./gradlew assemble 对所有的 buildType 生成 apk 包,会编译debug和release
./gradlew assembleDebug 只编译debug版本
./gradlew assembleRelease 只编译release版本
./gradlew clean 删除所有编译输出文件
./gradlew check lint检测, Wrote HTML report to file:///Users/tugaofeng/tgf/study/Learn-Android/studyGradle/app/build/reports/lint-results.html
./gradlew build 相当于执行assemble -> check

2.3)配置输出文件格式

2.3.1)基本用法
  • gradle build后会根据配置文件生成BuildConfig.java。因为gradle基于grovvy语言,其是一种JVM语言,可以生成Java字节码文件。
BuildConfig.java
//我们在代码中可以使用BuildConfig 判断当前环境是否debug
if (BuildConfig.DEBUG){
  ...
}
  • 可以在buildTypes中对不同的编译环境定义不同的键值对,这些值在不同的编译包apk中对应的值不一样,来实现一些需求,比如API服务器环境配置
// ~/build.gradle
buildTypes {
        debug {
            buildConfigField "String", "API_URL", "\"http://www.baidu.com/api\""
            buildConfigField "boolean", "LOG_SHOW", "true"

            // 相当于在res/strings.xml 下增加一个名为 app_name,debugAPP 的资源
            // 注意,这里是添加,在 string.xml 不能有这个字段,会重名!!!
            resValue "string", "app_name", "debugAPP"
        }
        release {
            buildConfigField "String", "API_URL", "\"http://www.google.com/api\""
            buildConfigField "boolean", "LOG_SHOW", "false"

            resValue "string", "app_name", "releaseAPP"
            ...
        }
    }
//debug环境编译后的 BuildConfig.java
public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "com.tgf.studygradle";
  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";
  // Fields from build type: debug
  public static final String API_URL = "http://www.baidu.com/api";
  public static final boolean LOG_SHOW = true;
}
Log.d("MainActivity","API环境: "+ BuildConfig.API_URL);
Log.d("MainActivity","是否打印日志: "+ BuildConfig.LOG_SHOW);
Log.d("MainActivity","app_name="+getString(R.string.app_name));

2.3.2)使用签名文件进行签名
//   ~/build.gradle
apply plugin: 'com.android.application'

...

//获取local.properties的内容
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())

android {
    ...
    //第一种:使用gradle直接签名打包
//    signingConfigs {
//        release {
//            storeFile file("/Users/tugaofeng/tgf/study/Learn-Android/studyGradle/studyGradle_keystore")
//            storePassword "123456"
//            keyAlias "studyGradle_keystore"
//            keyPassword "123456"
//        }
//    }
    //第二种:为了保护签名文件,把它放在local.properties中并在版本库中排除
    //不把这些信息写入到版本库中(注意,此种方式签名文件中不能有中文)
    signingConfigs {
        release {
            storeFile file(properties.getProperty("keystroe_storeFile"))
            storePassword properties.getProperty("keystroe_storePassword")
            keyAlias properties.getProperty("keystroe_keyAlias")
            keyPassword properties.getProperty("keystroe_keyPassword")
        }
    }
    buildTypes {
        ...
        release {
           ...
            signingConfig signingConfigs.release
        }
    }
}
//  ~/local.properties
#对应自己实际的证书路径和名字
keystroe_storeFile=/Users/tugaofeng/tgf/study/Learn-Android/studyGradle/studyGradle_keystore
keystroe_storePassword=123456
keystroe_keyAlias=studyGradle_keystore
keystroe_keyPassword=123456

2.3.3)多渠道包配置
多渠道包配置
apply plugin: 'com.android.application'

//打包时间
def releaseTime() {
    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}

//获取local.properties的内容
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())

android {
    compileSdkVersion 26
    // 默认配置
    defaultConfig {
        applicationId "com.tgf.studygradle"
        minSdkVersion 21
        targetSdkVersion 26
        versionCode 1
        versionName "1.0.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    //为了保护签名文件,把它放在local.properties中并在版本库中排除
    //不把这些信息写入到版本库中(注意,此种方式签名文件中不能有中文)
    signingConfigs {
        release {
            storeFile file(properties.getProperty("keystroe_storeFile"))
            storePassword properties.getProperty("keystroe_storePassword")
            keyAlias properties.getProperty("keystroe_keyAlias")
            keyPassword properties.getProperty("keystroe_keyPassword")
        }
    }
    //维度,gradle3.0后此处需要有个默认,名字随便取。此属性可用于不同维度的组合
    flavorDimensions "default"
    // 多渠道配置
    productFlavors {
        baidu{
            // 每个环境包名不同
            applicationId "com.tgf.studygradle.multichannel.baidu"
            // 动态添加 string.xml 字段;
            // 注意,这里是添加,在 string.xml 不能有这个字段,会重名!!!
            resValue "string", "app_name", "studyGradle百度版本"
            // 动态修改 常量 字段
            buildConfigField "String", "ENVIRONMENT", '"我是百度首页"'
            // 修改 AndroidManifest.xml 里渠道变量
            //manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
        }
        qq{
            applicationId "com.tgf.studygradle.multichannel.qq"

            resValue "string", "app_name", "studyGradle腾讯版本"

            buildConfigField "String", "ENVIRONMENT", '"我是腾讯首页"'
            //manifestPlaceholders = [UMENG_CHANNEL_VALUE: "qq"]
        }
    }
    buildTypes {
        debug {
            // debug模式,API服务地址
            buildConfigField "String", "API_URL", "\"http://www.baidu.com/api\""
            // debug模式,显示log
            buildConfigField("boolean", "LOG_SHOW", "true")

            //为已经存在的applicationId添加后缀
            applicationIdSuffix ".debug"
            // 为版本名添加后缀
            versionNameSuffix "-debug"
            // 不开启混淆
            minifyEnabled false
            // 不开启ZipAlign优化
            zipAlignEnabled false
            // 不移除无用的resource文件
            shrinkResources false

        }
        release {
            // release模式,API服务地址
            buildConfigField "String", "API_URL", "\"http://www.google.com/api\""
            // release模式,不显示log
            buildConfigField "boolean", "LOG_SHOW", "false"
            // 为版本名添加后缀
            versionNameSuffix "-release"
            // 开启ZipAlign优化
            zipAlignEnabled true
            // 移除无用的resource文件
            shrinkResources true
            // 使用config签名
            signingConfig signingConfigs.release
            // 开启混淆
            minifyEnabled true
            // 混淆文件位置
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            // 批量打包
            android.applicationVariants.all { variant ->
                variant.outputs.all {
                    outputFileName = "${variant.productFlavors[0].name}_v${defaultConfig.versionName}_${variant.buildType.name}_${releaseTime()}.apk"
                }
            }
       }
    }

    //忽略lint检测的error [lint配置说明](http://blog.csdn.net/berber78/article/details/60766091)
    lintOptions {
        // true--错误发生后停止gradle构建
        abortOnError false
    }
}

dependencies {
    //可以使用文件依赖
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //最新版的gradle已经使用api替代了compile。 api和implementation的依赖层次是有区别的,请查资料
    //一定要指名依赖版本,尽量不要使用通配符+,可以避免检查是否有新版本,也能做到工程化开发都统一依赖库版本
    //对于特定的buildTypes不同依赖的,可以使用debugImplementation或debugApi
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

productFlavors和buildTypes一样,可以拥有自己的sourceset文件夹,且productFlavors和buildTypes可以结合拥有一个优先级高于productFlavors和buildTypes单独设置的sourceset,比如想要qq的release版本拥有一个单独的图标,可以建立qqRelease的sourceset。注意:顺序必须是flavor+buildType形式。

不同的productFlavors和buildTypes都可以拥有自己的sourceset
屏幕快照 2018-02-01 上午10.58.26.png

2.4)配置Gradle 编译速度

  • ./gradle.properties
#加大可用编译内存
org.gradle.jvmargs=-Xmx1536m

# 开启并行编译
org.gradle.parallel=true

#开启守护进程,该进程在第一次启动后一直存在,后续的编译可以重用该进程
org.gradle.daemon=true
  • 执行所有task的时候我们都可以通过添加--profile生成一份执行报告


    build/reports/profile

    可以通过这份报告看出哪个项目耗费的时间最多,哪个环节耗费的时间最多

3)Groovy基本语法

  • 新建一个文件夹,并在文件夹内创建build.gradle
//变量:使用def关键字  单引号只是一串字符串
def name1 = 'tgf'
//变量:插值占位符 需要使用双引号 
def greeting = "hello,$name1"

//方法:如果不指定返回值 则返回最后一行代码
def add(def num) {
    num + num
}
//类:类中声明的属性会自动生成get和set方法 
//myClass.name = 'xxx'相当于 myClass.setName('xxx')
//myClass.name 相当于 myClass.getName()
class MyClass{
    String name
}

//列表
List list = [1,2,3]

//Map
Map map = [id:1,name:'玄策']

//闭包写法,如果只有一个参数 可以忽略此参数,使用it代替
//闭包写法在Android build.gradle中大量使用
/*def multi = { num ->
    num * num
}*/
def multi = {
    it * it
}

//任务-打印
task myTask << {
    println '========打印变量&方法==========='
    //测试打印
    println "hello world"
    //测试打印变量
    println greeting
    //测试打印方法
    println add(2)

    println '========打印类属性值==========='

    //创建类的实例对象
    def myClass = new MyClass()
    myClass.name = '大家好'
    //测试打印类属性值
    println myClass.name

    println '=========打印列表数据=========='

    //测试打印列表
    list.each(){element ->
        println element
    }

    println '=========打印Map数据=========='

    println map['id']
    println map.get('name')

    println '=========打印闭包写法=========='
    println multi(4)
}

//任务-从文件夹拷贝至另一个
task copyFile(type: Copy){
    from 'src'
    into 'dst'
}

//task的依赖关系 dependsOn
//如果是Muliti-Project的模式,依赖关系要带着所属的Project,如taskA.dependsOn ':other-project:taskC' 其中taskC位于和taskA不同的Project中,相对于AndroidStudio来说,就是位于不同的Module下的build.gradle中,而other-project为Module名字。
task taskA <<{
    println "这是taskA"
}
task taskB <<{
    println "这是taskB"
}
taskA.dependsOn taskB
Groovy

4)Gradle插件开发简介

  • 新建Android-library的Module,删除非必要的文件,保留如下格式


    mygradleplugin
  • com.tgf.mygradleplugin.properties代表了我们这个插件的id就是com.tgf.mygradleplugin

#指向我们新建的类
implementation-class=com.tgf.mygradleplugin.MyGradlePlugin
  • build.gradle
apply plugin: 'java'//导入java插件用于,编译打包我们的插件, 也可以使用apply plugin: 'groovy'
apply plugin: 'maven'//maven插件,用于上传插件到仓库

//uploadArchives 类型是upload,这个task不是'maven'创建的
// 使用命令./gradlew uploadArchives 可以在指定路径生成jar
uploadArchives{
    //本地仓库的一种
    repositories{
        flatDir{
            name "localRepository"
            dir "localRepository/libs"
        }
    }

    //提交到远程服务器:
    // repository(url: "http://xxx") {
    //    authentication(userName: "xxx", password: "xxx")
    // }
    //本地的Maven地址设置为D:/repos
//    repository(url: uri('/Users/tugaofeng/xxx'))
}
group = "com.tgf.mygradleplugin"//project属性
version = "1.0"//project属性
dependencies {
    //导入Gradle的api,要写插件,肯定要使用Gradle的api
    compile gradleApi()
}
  • 举例:我们使用这个插件来在指定路径里生成一个文件,并写入作者信息
  • AuthorBean.java
public class AuthorBean {
    //姓名
    private String name;
    //年龄
    private int age;

    public AuthorBean(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "作者:"+name+" ,年龄:"+age;
    }

    @Input
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Input
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
  • WriteAuthorTask.java
public class WriteAuthorTask extends DefaultTask{

    private AuthorBean authorBean; //作者实体
    private String fileName;
    private File targetDict;

    @Nested
    public AuthorBean getAuthorBean() {
        return authorBean;
    }

    @Input
    public String getFileName() {
        return fileName;
    }

    @InputDirectory
    public File getTargetDict() {
        return targetDict;
    }

    @TaskAction
    public void writeObject(){
        File targetFile = new File(targetDict,fileName);
        try {
            FileOutputStream fos = new FileOutputStream(targetFile);
            fos.write(authorBean.toString().getBytes());
            fos.flush();
            fos.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public void setAuthorBean(AuthorBean authorBean) {
        this.authorBean = authorBean;
    }
    public void setTargetDict(File targetDict) {
        this.targetDict = targetDict;
    }
    public void setFileName(String fileName) {
        this.fileName = fileName;
    }
}
  • MyGradlePlugin.java
public class MyGradlePlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        WriteAuthorTask task = project.getTasks().create("writeAuthor", WriteAuthorTask.class);

        task.setTargetDict(new File("" + "/Users/tugaofeng/Desktop"));
        task.setFileName("author.txt");
        task.setAuthorBean(new AuthorBean("涂高峰",30));
        task.writeObject();
    }
}
  • Project-build.gradle

buildscript {
    
    repositories {
        google()
        jcenter()

        flatDir  name:'localRepository',dir:'mygradleplugin/localRepository/libs'
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0'
        //命名是我们的groupId:moduleName:version
        classpath 'com.tgf.mygradleplugin:mygradleplugin:1.0'
    }
}
  • app的Module-build.gradle
//新增apply插件id
apply plugin: 'com.tgf.mygradleplugin'
  • 运行命令或点击AS的命令,会生成对应路径的jar,并执行插件代码,写入txt文件


    ./gradlew uploadArchives
./gradlew uploadArchives
jar包生成

文件生成

简单介绍了如何自定义gradle插件,详细的学习在插件化的时候进行学习


参考资料

Gradle 完整指南(Android)
《Gradle for Android》
Groovy 基本语法


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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