Gradle骚操作合集

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缺失的情况

    image-20200117112348582.png
  • buildTypes里面默认就有 debug和release,这一点在as里面可以看到,下图中另外3个是我们自己加的type:
image-20200117112654199.png
  • 通过指定的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

image.png

改变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:

image-20200117145635162.png
  • 调用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:

image-20200117145810555.png
  • 通过干涉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

    image-20200117150006820.png

方式对比

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

先写这么多。后续还有继续补上

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容