gradle自动打包

gradle自动打包

其实思路还是挺清晰的,就是将原来手工完成的工作交给脚本去做

预备知识

需要先阅读前文

gradle使用的groovy基础知识

build.gradl概念

build.gradle会自动对应一个org.gradle.api.Project实例,大多数人情况下,构建脚本中调用的属性和方法都自动委托给了这个Project实例。
也就是说,如果build.gradle没有使用插件的话,在这个文件里直接调用的外界属性和方法都是Project实例内的,一般我们见到的代码都是方法的调用,不过是去掉了括号,使用了闭包

Project下的build.gradle是没有应用插件的,Moudle下的build.gradle使用了插件(一般为)
apply plugin: 'com.android.application'
或者为
apply plugin: 'com.android.library'


使用android studio自带的变种版本(Build Variant)

“构建类型 + 定制产品 = 构建变种版本”

构建类型(Build Type)

moudle底下的build.gradle的android域下的一个代码块,默认会有两个build type,release和debug。

//默认
buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

定制产品(productFlavors)

也是在android代码块里

productFlavors {
        //生产环境
        production {
        }
        //准生产环境
        preproduction {
        }
        //外网测试环境
        outTest {
        }
    }

变种版本(Build Variant)

设置完上述选项并编译gradle文件后在android studio左下角查看:
gradle自动打包_第1张图片
选择我们需要打包的环境。


根据不同的变种版本(Build Variant)执行不同的groovy代码

下一步就需要我们写的groovy代码能自己识别出当前选择的Build Variant并执行相关的文件操作
选择不同的Build Variant时,gradle在执行编译过程中编译的任务会出现差别,在android studio右侧的面板可以查看:
gradle自动打包_第2张图片
由此我们可以在编译(安装)任务执行图(taskGraph)准备好的时候检测需要执行任务的名字从而判断出此次编译(安装)的Build Variant。

任务(Task)是指不可分的最小工作单元,代表一个逻辑上较为独立的执行过程,比如:编译、复制、打包.
任务执行图(taskGraph):task互相之间存在着依赖关系,比如要编译的时候要先进行语法检查,所以编译任务依赖语法检查任务,当这一系列任务要开始执行的时候就会先构建好一个执行的图谱,就是任务执行图(taskGraph)

//与Build Variant对应,默认为error
def buildVariant = "error"
//gradle是Project实例中的属性
gradle.taskGraph.whenReady { TaskExecutionGraph taskExecutionGraph ->
    //不同的Build Variant对应不同的任务
    def variantMap = ['outTest'      : "task ':RDWork:checkOutTestDebugManifest'",
                      'production'   : "task ':RDWork:checkProductionDebugManifest'",
                      'preproduction': "task ':RDWork:checkPreproductionDebugManifest'"]

    //遍历每一个task
    taskExecutionGraph.allTasks.each { output ->
        switch (output.toString().trim()) {
            case variantMap.outTest:
                buildVariant = "outTest"
                println('外网测试环境')
                break
            case variantMap.production:
                buildVariant = "production"
                println('生产环境')
                break
            case variantMap.preproduction:
                buildVariant = "preproduction"
                println('准生产环境')
                break
        }
    }
    //如果没有包含备选的任务,抛出异常
    //groovy使用“==”时会自动调用equal()方法
    if (buildVariant == 'error') {
        throw new Exception("无配置的task,请重新配置")
    }
}

复制文件

我们打包时主要做的事情就是改变assets文件夹中配置文件的配置,例如不同的环境需要用不同的ip地址。
自动打包时将每种环境的配置文件都备份在不同的文件夹下,地址是:
"$rootDir/assets/$moudleName/$buildVariant"

rootDir是Project实例中的属性,代表项目的根目录

gradle给了我们很方便的复制文件的方式,copy代码块(方法)

/**
 * 放置文件到对应moudle的assets文件夹
 * @param moudleNameList 需要放置的moudle名字的集合
 * @param buildVariant  当前的变种版本(Build Variant)
 */
def putFile(moudleNameList,buildVariant) {
    moudleNameList.each { moudleName ->
        copy {
            from "$rootDir/assets/$moudleName/$buildVariant"
            into "$rootDir/$moudleName/src/main/assets"
            println("${moudleName} copy 成功")
        }

    }
}

gradle自动打包_第3张图片


改变文件

有的时候我们希望能在每次构建时都动态更改,例如版本号,我们assets文件夹下的ClientVer.xml文件需要在每次打包时写入当前的版本号,那么做法其实也很简单,在需要改变的文件中需要动态改变的部分填充为占位符,每次重新put文件后就将占位符替换为有意义的字符

/**
 * 将文件的源字符串替换为目标字符串
 * @param path  文件路径
 * @param srcString 源字符串
 * @param dstString 目标字符串
 */
def overWriteString(String path, String srcString, dstString) {
    def file = new File(path)
    def string = file.text.replaceAll(srcString, dstString)
    def printWriter = new File(file.parent, file.name).newPrintWriter()
    printWriter.write(string)
    printWriter.flush()
    printWriter.close()
}

自动从svn上获取最新的版本号

其实就是通过groovy执行命令行获取svn最近一次提交的信息并通过正则表达式截取出最近的版本号信息

/**
 * 获取最近的svn版本号
 * @param path  svn文件或文件夹路径
 * @return  最近的版本号
 */
String getSvnLastVersion(String path){
    //先更新,避免获取版本号错误
    //execute()即是执行命令行
    "svn update $path".execute()
    //获取最近一次提交信息的命令
    def cmd = "svn log -l 1 $path"
    def cmdResult = cmd.execute().text
    def pattern = ~/r\d+/
    def matcher = pattern.matcher(cmdResult)
    if (matcher.getCount()!=1){
        println(cmdResult)
        throw new Exception("获取svn最近的版本号时匹配错误,尝试使用svn工具手动获取版本号:\n$cmdResult")
    }else {
        //获取的信息其会在版本号前加r,将其去掉
        String lastVersion = matcher[0].replace('r','')
        println("最近一次svn版本号为:$lastVersion")
        return lastVersion
    }
}

将版本号设为变量

将版本号设为变量,这样就不用到处修改版本号了,只用修改一处,别的地方引用这个变量就可以了

//调用刚才的获取svn版本号的函数
ext.appVersionName = "1.0.0.${getSvnLastVersion(rootDir.absolutePath)}"

ext:上面的程序地址都是位于project的build.gradle文件中,而我们的版本号需要在moudle的build.gradle文件中引用,前面也讲了,这些文件其实相当于Project类的不同对象,而ext关键字就相当于给类中定义了一个静态变量,这样在不同的对象中都可以直接使用

则在moudle的build.gradle文件中就可以这样使用:

versionCode new Integer(appVersionName.replace('.', ''))
versionName "$appVersionName"

如果appVersionName = 1.0.0.3008,则相当于:

//其实这些都是没加圆括号的方法
versionCode 1003008
versionName 1.0.0.3008

使生成的apk名字加上版本号

在application的build.gradle文件加入下列代码:

//使生成的apk名字加上版本号
android.applicationVariants.all { variant ->
    variant.outputs.each { output ->
        def outputFile = output.outputFile
        if (outputFile != null && outputFile.name.endsWith('.apk')) {
            def fileName = outputFile.name.replace(".apk", "-${appVersionName}.apk")
            output.outputFile = new File(outputFile.parent, fileName)
        }
    }
}

其它

不同环境引用不同的jar包

在dependencies中通过

${buildVariant}Complie引用
//例如
outTestCompile 'org.xutils:xutils:3.0'

不同的环境使用不同的applicationId

不同的环境使用不同的applicationId可以让同一个手机装上不同的版本:

    productFlavors {
        //生产环境
        production {
            applicationId com.xdja.xxx.production
        }
    }

引用util.gradle文件

build.gradle文件中不宜写太多代码,一些工具方法可以放在其它的文件中

build.gradle引用时需要使用apply

//是一个方法,功能比较像java中的import
apply(from: rootProject.getRootDir().getAbsolutePath() + "/utils.gradle")

工具文件中的方法需要设置到ext属性中

上文中已经出现过的两个方法其实就是在util.gradle文件中

def overWriteString(String path, String srcString, dstString) {
    ...
}

String getSvnLastVersion(String path){
    ...
}

//将函数设置为extra属性中去,这样,加载utils.gradle的Project就能调用此文件中定义的函数了
ext {
    overWriteString = this.&overWriteString
    getSvnLastVersion = this.&getSvnLastVersion
}

注:根据不同productFlavors复制文件这部分上述方法仅做参考,推荐使用官方的直接建立对应文件夹的方式实现

你可能感兴趣的:(gradle)