Gradle骚操作合集

写在前面;

Gradle是谷歌钦定的android项目构建工具。熟练使用gradle可以实现很多骚操作,比如多渠道打包,指定打包文件路径和文件名等,而且实现方式不止一种。Gradle工具的编程语言叫做 Groovy, Groovy的语法相对宽松,有点类似javascript,怎么写的人都有,但是最终目标可能是一致的。


本文收录一些Gradle骚操作供大家分享,经本人验证可行,会提供完整Demo(Gradle会存在新旧版本兼容问题,运行demo不要改动gradle版本配置):
Demo地址:https://github.com/18598925736/EnjoyGradleHank/commits/master

 

配置buildTypes{ xxx } 自定义"构建类型“

...
android {
    ...
    // 配置签名文件
    signingConfigs {
        debug {
            // 这里会使用默认签名
        }
        release {
            storeFile file("mykey.jks")
            storePassword "android"
            keyAlias "android"
            keyPassword "android"
            v2SigningEnabled true
        }
    }
    //level 1: 打包方式,默认有debug和release,当然可以自己加喜欢的
    buildTypes {

        //内置 debug 和 release,但是我们可以忽略不计
        uat {
            // 测试环境可调试
            debuggable true
            signingConfig signingConfigs.release
        }
        prd {
            // 正式环境 可调式
            debuggable true
            signingConfig signingConfigs.release
        }
        online {
            // 正式发布包 不可调试
            debuggable false
            signingConfig signingConfigs.release
        }

    }

}

dependencies {
    ...
}

关键点:

  • 配置buildTypes之前,必须 配signingConfigs,把签名文件配好,否则,就会出现 install Task缺失的情况

  • buildTypes里面默认就有 debug和release,这一点在as里面可以看到,下图中另外3个是我们自己加的type:

  • 通过指定的buildType打包出来的apk,可以读到 当前包的buildType,并且用于app程序内部,比如配置app的网络环境设置(测试环境和正式环境),还有 是否支持日志打印屏蔽所有日志和放开所有日志),获取的方式为BuildConfig.BUILD_TYPE
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        tvBuildType.text = BuildConfig.BUILD_TYPE
    }
}

 

配置productFlavors{ xxx } 自定义"多种打包风味“

Demo地址:https://github.com/18598925736/EnjoyGradleHank/commits/master(切换到时间点:78dc2ca

打包风味 flavor 是 谷歌提供的 多渠道打包的官方手段。

app module 的 build.gradle

...

android {
    ...

    //level 2: 通过productFlavors打包配置,实现多维度风味打包
    flavorDimensions "zone", "themeColor"
    // 定义多风味
    productFlavors {
        /**
         * 越南版  每一个风味选项都必须指定独立的风味值
         */
        vn {
            applicationId "com.global.vn.ftint"
            dimension "zone"
            manifestPlaceholders = [zone: "vn"]
        }

        /**
         * 国内版
         */
        cn {
            applicationId "com.global.cn.ftint"
            dimension "zone"
            manifestPlaceholders = [zone: "cn"]
        }

        /**
         * 主题风格为红色
         */
        red {
            dimension "themeColor"
            manifestPlaceholders = [themeColor: "red"]
        }

        /**
         * 主题风格为蓝色
         */
        blue {
            dimension "themeColor"
            manifestPlaceholders = [themeColor: "blue"]
        }
    }
}

dependencies {
    ...
}

同时必须与 manifest配合:

app module 的 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zhou.enjoygradle2">

    <application
        ...>
        ...
        <meta-data
            android:name="ZONE"
            android:value="${zone}" />
        <meta-data
            android:name="themeColor"
            android:value="${themeColor}" />
    </application>

</manifest>

关键点:

  • 多风味打包支持多维度,定义维度是在 flavorDimensions "zone", "themeColor" 这样代码中 , 此处定义了两个维度:“地区”,“主题颜色”
  • 配置 风味 productFlavor的时候必须指定维度:以下代码就是指定维度为 zone ,维度的值,则是 cn,同时还指定了包名applicationId.
   cn {
            applicationId "com.global.cn.ftint"
            dimension "zone"
            manifestPlaceholders = [zone: "cn"]
        }
  • 多种维度的笛卡尔积的数值,就是 打包方式的总个数,比如上方,zone维度2个风味,themeColor维度2个风味,所以风味的总数是 2x2=4,在as中也能看到对应的task,但是,此处的 维度总数 和 之前的buildType又形成了一个新的笛卡儿积,之前有5个(uat,prd,online,还有自带的 debug和release ),所以能看到的task一共是 4x5=20

改变apk输出路径和文件名

目前查到有三种写法:

  • 重写applicationVariants.all{} 参数闭包,支持绝对路径的写法
...

android {
    ...

    //level3
    applicationVariants.all { variant ->
        //这个修改输出的APK路径
        if (variant.buildType.name != "debug") {//防止AS无法安装debug包(apk)
            variant.getPackageApplication().outputDirectory = new File(myPackageDir())
        }
        variant.getPackageApplication().outputScope.apkDatas.forEach { apkData ->
            //这个修改输出APK的文件名
            apkData.outputFileName = "${appNamePrefix()}" +
                    variant.versionName + "_" +
                    "${releaseTime()}" + "_" +
                    variant.flavorName + "_" +
                    variant.buildType.name + "_" +
                    ".apk"
        }
    }
}

def myPackageDir(){
    return "C:\\Users\\adminstrator\\Desktop\\myPackage"
}

def appNamePrefix() {
    return "HankZhou"
}

String releaseTime() {
    Date date = new Date()
    String dates = date.format("yyyyMMdd_hhmmss", TimeZone.getTimeZone("UTC"))
    return dates
}

dependencies {
    ...
}

执行 gradlew assembleCnBlueOnline 之后在 电脑桌面上的 myPackage 文件夹中会存在一个apk:

  • 重写applicationVariants.all{} 参数闭包,支持相对路径的写法
...

android {
    ...

    // 或者,使用这种方式,只支持相对路径
    applicationVariants.all { variant ->
        String time = new Date().format('yyyyMMdd_HHmmss')
        String pkgName = "enjoy_${time}_v${defaultConfig.versionName}_${buildType.name}_${flavorName}.apk"
        outputs.first().outputFileName = "../../../${pkgName}" // 它不能使用绝对路径,也就是说可以使用项目的相对路径
    }
}

...

dependencies {
    ...
}

执行 gradlew assembleCnBLueUat 之后,app module的build目录内会出现 apk:

  • 通过干涉task的执行过程
...

android {
    ...
}

/**
 * 当有序任务图中加进去一个任务的时候,对它进行处理,doLast重新定义完成之后的行为
 */
tasks.whenTaskAdded { task ->
    if (task.name.equalsIgnoreCase("assembleCnBlueUat")) {
        println("=============发现任务 ${task.name}")

        // 如果是assembleRelease任务,在最后执行导出apk以及mapping目录到指定目录
        task.doFirst {
            println("=============doFirst")
        }
        task.doLast {
            outputReleaseFile()
        }
    }
}

void outputReleaseFile() {
    println("=============outputReleaseFile")
    android.applicationVariants.all { variant ->
        // 如果是正式版打包
        if (variant.name.contains("cnBlueUat")) {
            println("=============准备拷贝${variant.name}")
            File outputPath = new File(myPackageDir())
            println("==========把文件${variant.outputs[0].outputFile} \n 拷贝到 目标位置:$outputPath")
            outputPath.mkdir()
            // 但是很明显,这里的copy并没有成功
            copy {// 拷贝apk文件
                println("========开始拷贝...")
                from variant.outputs[0].outputFile
                into outputPath
                // 重命名导出名称
                rename {
                    appNamePrefix() + variant.name + '_' + android.defaultConfig.versionName + '-' + releaseTime() + ".apk"
                }
            }
        }
    }
}

def myPackageDir(){
    return "C:\\Users\\adminstrator\\Desktop\\myPackage"
}

def appNamePrefix() {
    return "HankZhou"
}

String releaseTime() {
    Date date = new Date()
    String dates = date.format("yyyyMMdd_hhmmss", TimeZone.getTimeZone("UTC"))
    return dates
}

dependencies {
    ...
}

执行 gradlew assembleCnBlueUat之后,电脑桌面上会出现 mypackage目录,内部有一个apk

方式对比

  • 重写applicationVariants.all{} 参数闭包的方式,修改输出的绝对路径,或者相对路径,分别对应两个api:
    • 相对 outputs.first().outputFileName
    • 绝对 variant.getPackageApplication().outputScope.apkDatas 和 apkData.outputFileName
  • 使用 干涉task任务的方式,和 上面的方式比起来,写法更加复杂,但是可控制的精度也更高,比如:它除了可以改变输出路径之外,还可以直接指定拦截哪些task,而不是像 重写applicationVariants.all闭包 一样,一律变更输出路径和文件名。

先写这么多。后续还有继续补上,欢迎关注,有问题一起交流学习~



 

发布了17 篇原创文章 · 获赞 0 · 访问量 176

猜你喜欢

转载自blog.csdn.net/chuhe1989/article/details/104267046