美文网首页
Gradle 相关

Gradle 相关

作者: 黄海佳 | 来源:发表于2017-10-17 16:08 被阅读94次
    一、Groovy脚本

    Groovy是一种动态语言,Groovy脚本基于Java且拓展了Java,都在Java虚拟机中运行。当运行Groovy脚本时它会先被编译成Java类字节码,然后通过JVM虚拟机执行这个Java字节码类。

    1、如何安装Groovy?

    安装Groovy在各种Bash下(bash 是一个为GNU计划编写的Unix shell)都是通用的,具体如下命令就可搞定:

    $ curl -s get.sdkman.io | bash
    $ source "$HOME/.sdkman/bin/sdkman-init.sh"
    $ sdk install groovy
    $ groovy -version
    //至此就可以享用了!
    

    我们在写Groovy代码时可以直接使用自己喜欢的文本编辑器编辑就可以(studio有内置的 Tools-->IDE Scripting Console-->Groovy)

    然后以.groovy后缀保存,然后在终端执行如下命令即可运行:

    $ groovy ./HaiJiaTest.groovy
    
    二、Gradle

    通俗的说:gradle是打包用的,Gradle核心是基于Groovy的领域特定语言(DSL)。Ant和Maven都是基于XML的构建工具,Gradle是用Groovy编写的构建工具。Gradle通过编写一个名为build.gradle的脚本文件对项目进行设置,再根据这个脚本对项目进行构建(复杂的项目也有其他文件)。当前其支持的语言限于Java、Groovy、Kotlin和Scala。

    Gradle里有两个基本概念:项目(projects)和任务(tasks)。

    项目由多个任务组成,一个项目可以理解为提供给不同设备的构建版本,如桌面版、网页版、安卓版、iOS版等等,也可以理解为一种行为,例如部署应用到生产环境。任务相当于Ant的target,可以理解成一个构建中原子性的工作,例如编译、打包、执行等。需要注意的是,Ant中他自己的命令例如javac、copy等也叫做task,但Ant的task远没有Gradle的task那么自由。

    为什么要用Gradle

    ant可以自动化打包逻辑。
    maven也可以自动化打包,相比于ant,它多做的事是帮你下载jar包。
    但是maven的打包逻辑太死板,定制起来太麻烦,不如ant好用。gradle就是又能自动下jar包,又能自己写脚本,并且脚本写起来还比ant好用的这么个东西。

    Gradle DSL基础

    Gradle的实质是配置脚本,执行一种类型的配置脚本时就会创建一个关联的对象,譬如执行Build script脚本就会创建一个Project对象,这个对象其实就是Gradle的代理对象。下面给出来各种类型Gradle对应的对象类型:

    脚本类型 关联对象类型
    Build script Project :每个build.gradle会转换成一个Project对象。
    Init script Gradle :构建初始化时创建,整个构建执行过程中只有这么一个对象,一般很少去修改这个默认配置脚本。
    Settings script Settings : 每个settings.gradle会转换成一个Settings对象。

    可以看见,当我们编写指定类型Gradle脚本时我们可以直接使用关联对象的属性和方法;当然了,每个脚本也都实现了Script接口,也就是说我们也可以直接使用Script接口的属性与方法。

    1、构建脚本Build script(Project)

    在Gradle中每个待编译的工程都是一个Project(每个工程的build.gradle对应一个Project对象),每个Project在构建的时候都包含一系列Task,这些Task中很多又是Gradle的插件默认支持的。

    PS:所谓的我们编写Gradle脚本,实质大多数时候都是在编写构建脚本Build script,所以说Project和Script对象的属性和方法等API非常重要。

    每一个Project对象和build.gradle一一对应,一个项目在构建时都具备如下流程:

    • 为当前项目创建一个Settings类型的实例。

    • 如果当前项目存在settings.gradle文件,则通过该文件配置刚才创建的Settings实例。

    • 通过Settings实例的配置创建项目层级结构的Project对象实例。

    • 最后通过上面创建的项目层级结构Project对象实例去执行每个Project对应的build.gradle脚本。

    2、初始化脚本Init script(Gradle)

    初始化脚本Init script(Gradle)类似于Gradle的其他类型脚本,这种脚本在构建开始之前运行,主要的用途是为接下来的Build script做一些准备工作。

    初始化脚本的Gradle对象代表了Gradle的调运,我们可以通过调用Project对象的getGradle()方法获得Gradle实例对象。

    3、设置脚本Settings script(Settings)

    在对工程进行配置(譬如多项目树构建)时Settings实例与settings.gradle文件一一对应,它用来进行一些项目设置的配置。这个文件一般放置在工程的根目录。譬如:

    4、Build生命周期

    Gradle的构建脚本生命周期具备三大步,如下:

    5、Gradle多项目构建:

    多项目构建总是需要指定一个树根,树中的每一个节点代表一个项目,每一个Project对象都指定有一个表示在树中位置的路径;在设置文件中我们还可以使用一套方法来自定义构建项目树。

    //分层布局的多项目构建settings.gradle文件
    include 'project1', 'project2:child', 'project3:child1'
    

    上面例子中把project的路径作为了include方法的参数,譬如上面的’project3:child1’参数就指定了物理路径的project3/child1(project3/child1是相对于多项目根路径的相对路径),这也同时意味着会创建’project3’和’project3:child1’两个project。

    //平面布局的多项目构建settings.gradle文件
    includeFlat 'project3', 'project4'
    

    上面例子中includeFlat方法接受目录名作为参数,但是特别注意,这些项目目录必须是根目录的兄弟目录。

    当然了,设置文件中创建的多项目树其实是由项目描述符来描述的,我们可以在设置文件中随时修改这些描述符。如下:

    //settings.gradle
    rootProject.name = 'main'
    project(':projectA').projectDir = new File(settingsDir, '../my-project-a')
    project(':projectA').buildFileName = 'projectA.gradle'
    

    可以看见,如上例子通过描述符更改名称和项目目录,并且建立了一个项目的文件。

    Gradle构建初始化Initialization:

    在初始化阶段如果我们在根路径下直接指明settings.gradle文件和相关配置则构建初始化就会直接按照我们的设置去构建项目,如果我们没指明settings.gradle文件则Gradle会以一定的规则去寻找settings.gradle文件,然后依据寻找结果的不同去决定如何构建项目。

    三、Gradle在android中的应用
    apply plugin: 'com.android.application'
    
    android {
    
        compileSdkVersion 26
        buildToolsVersion '26.0.2'
    
        defaultConfig {
            applicationId "com.project.haijia"
            // 配置生成的 BuildConfig 文件中的常量
            buildConfigField "String", "CHANNEL", '"PLAY_STORE"'
            minSdkVersion 16
            targetSdkVersion 25
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
    
         buildTypes {
    
            debug {
                buildConfigField "String", "API_URL", '"http://dev.goodev.org/"'
                buildConfigField "boolean"�, "SHOW_LOG"�, "true"
             }
    
       
            release {
                minifyEnabled false
                // 混淆文件的位置
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
          }
    
        // 移除lint检查的error
        lintOptions {
          abortOnError false
        }
    }
    
    //dependencies中配置的是这个项目中要用的类库
    dependencies {
        //表示包括之前在libs文件夹下的所有jar包
        compile fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'com.android.support:appcompat-v7:26.1.0'
    
         //表示测试代码的时候依赖junit这个库
        testCompile 'junit:junit:4.12'
    
        // 编译extras目录下的JiaAndroid模块
        compile project(':extras:JiaAndroid')
    
        //表示编译期依赖gson这个库
        compile 'com.google.code.gson:gson:2.8.0'
       
        androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:3.0.1'
    }
    
    
    
    productFlavors {  
        youmi {
          applicationId = "org.goodev.material.youmi"
                    buildConfigField "String", "CHANNEL", '"YOUMI"'
                    resValue "string", "app_name", "Material free"
        }
        pro {
          applicationId = "org.goodev.material.pro"
                    buildConfigField "String", "CHANNEL", '"OTHER"'
                    resValue "string", "app_name", "Material pro"
        }
      }
        
    signingConfigs {
        release {
            storeFile     "${System.env.PRIVATE_KEY}"
            keyAlias      "${System.env.ALIAS}"
            storePassword "${System.env.STORE_PW}"
            keyPassword   "${System.env.APP_PW}"
        }
    }
    

    两个配置项 android 和dependencies

    通过 System.env 来访问系统环境变量中的值,这样你就可以把一些私有的内容排除在代码外,这样当你提交代码的时候,就不会泄露这些内容。还可以把私有数据放到local.properties 文件中:

     signingConfigs {
            release {
                def Properties localProps = new Properties()
                localProps.load(new FileInputStream(file('local.properties')))
                def Properties keyProps = new Properties()
                if (localProps['keystore.props.file'] != null) {
                    keyProps.load(new FileInputStream(file(localProps['keystore.props.file'])))
                }
                storeFile keyProps["store"] != null ? file(keyProps["store"]) : null
                keyAlias keyProps["alias"] ?: ""
                storePassword keyProps["storePass"] ?: ""
                keyPassword keyProps["pass"] ?: ""
            }
        }
    
    
    动态生成 Android Manifest 中的内容

    为了让所有的 productFlavors 都可以安装到同一个手机上, 安卓系统要求不同的应用具有不同的 provider 等内容,如果冲突则其他应用无法安装,可以通过如下方式来解决该问题

    <permission android:name="${applicationId}.permission.XX"  
            android:protectionLevel="signature" />
    <uses-permission android:name="${applicationId}.permission.XX" />
     
    <provider   android:name=".MyProvider"  
                android:authorities="${applicationId}.provider"
                … />
    
    不同的 productFlavors 具有不同的 permission

    四、Gradle常用命令

    • ./gradlew -v 版本号

    • ./gradlew clean 清除9GAG/app目录下的build文件夹

    • ./gradlew installRelease Release模式打包并安装

    • ./gradlew uninstallRelease 卸载Release模式包

    • ./gradlew build 检查依赖并编译打包
      这里注意的是 ./gradlew build 命令把debug、release环境的包都打出来,如果正式发布只需要打Release的包,该怎么办呢,下面介绍一个很有用的命令 assemble, 如

    • ./gradlew assembleDebug 编译并打Debug包

    • ./gradlew assembleRelease 编译并打Release的包
      除此之外 assemble 还能和 Product Flavor 结合创建新的任务,其实 assemble 是和 Build Variants 一起结合使用的,而 Build Variants = Build Type + Product Flavor

    • ./gradlew assembleWandoujiaRelease 打包wandoujia渠道的release版本
      如果我们,则:

    • ./gradlew assembleWandoujia 只打wandoujia渠道版本,此命令会生成wandoujia渠道的Release和Debug版本

    • ./gradlew assembleRelease 这条命令会把Product Flavor下的所有渠道的Release版本都打出来。
    assemble 命令创建task有如下用法:
    • assemble: 允许直接构建一个Variant版本,例如assembleFlavor1Debug。
    • assemble: 允许构建指定Build Type的所有APK,例如assembleDebug将会构建Flavor1Debug和Flavor2Debug两个Variant版本。
    • assemble: 允许构建指定flavor的所有APK,例如assembleFlavor1将会构建Flavor1Debug和Flavor1Release两个Variant版本。
    五、多渠道打包

    这里举例友盟为主
    AndroidManifest.xml

    <meta-data
        android:name="UMENG_CHANNEL"
        android:value="${UMENG_CHANNEL_VALUE}" />
    

    在build.gradle设置productFlavors

    android {  
        productFlavors {
            xiaomi {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
            }
            _360 {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "_360"]
            }
            baidu {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
            }
            wandoujia {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
            }
        }  
    }
    

    或者批量修改

    android {  
        productFlavors {
            xiaomi {}
            _360 {}
            baidu {}
            wandoujia {}
        }  
    
        productFlavors.all { 
            flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] 
        }
    }
    

    一个完整的多渠道例子

    apply plugin: 'com.android.application'
    
    def releaseTime() {
        return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
    }
    
    android {
        compileSdkVersion 26
        buildToolsVersion '26.0.2'
    
        defaultConfig {
            applicationId "com.haijia.*"
            minSdkVersion 16
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
            
            // dex突破65535的限制
            multiDexEnabled true
            // 默认是umeng的渠道
            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng"]
        }
    
        lintOptions {
            abortOnError false
        }
    
        signingConfigs {
            debug {
                // No debug config
            }
    
            release {
                storeFile file("../yourapp.keystore")
                storePassword "your password"
                keyAlias "your alias"
                keyPassword "your password"
            }
        }
    
        buildTypes {
            debug {
                // 显示Log
                buildConfigField "boolean", "LOG_DEBUG", "true"
    
                versionNameSuffix "-debug"
                minifyEnabled false
                zipAlignEnabled false
                shrinkResources false
                signingConfig signingConfigs.debug
            }
    
            release {
                // 不显示Log
                buildConfigField "boolean", "LOG_DEBUG", "false"
    
                minifyEnabled true
                zipAlignEnabled true
                // 移除无用的resource文件
                shrinkResources true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.release
    
                applicationVariants.all { variant ->
                    variant.outputs.each { output ->
                        def outputFile = output.outputFile
                        if (outputFile != null && outputFile.name.endsWith('.apk')) {
                            // 输出apk名称为boohee_v1.0_2015-01-15_wandoujia.apk
                            def fileName = "boohee_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
                            output.outputFile = new File(outputFile.parent, fileName)
                        }
                    }
                }
            }
        }
    
        // 友盟多渠道打包
        productFlavors {
            wandoujia {}
            _360 {}
            baidu {}
            xiaomi {}
            tencent {}
            taobao {}
            ...
        }
    
        productFlavors.all { flavor ->
            flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
        }
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'com.android.support:appcompat-v7:26.1.0'
        ...
    }
    

    参考

    Gradle教程
    Gradle学习系列

    相关文章

      网友评论

          本文标题:Gradle 相关

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