Gradle

作者: 玄策 | 来源:发表于2018-02-01 15:14 被阅读124次

    目录

    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 基本语法


    相关文章

      网友评论

          本文标题:Gradle

          本文链接:https://www.haomeiwen.com/subject/utbqzxtx.html