• 【Gradle-11】动态修改VersionName和VersionCode


    前言

    有个读者问了一个比较有意思又很常见的问题,怎么修改VersionName和VersionCode?
    读者.png
    这位读者提问的方式也挺好,报错信息和尝试过的方式都提到了。
    关于报错以及解决方案,正如上图我评论回复所说。

    静态修改直接在build.gradle文件改就好了,动态修改还是有点意思的。
    但是这种需求也比较常见,比如加上git commit id啊,时间戳啊等等,以便于区分,省的测试同学提了bug搞半天是包不对…
    那么本文就来带大家实践一下,以及探索有没有其他方式。

    配置BuildType

    我们可以通过配置不同的BuildType,然后使用versionNameSuffix,分别设置对应的版本名称后缀。

        buildTypes {
            debug{
                versionNameSuffix "-测试包"
            }
            release {
                versionNameSuffix "-正式包"
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    现在的版本名称和版本号:

        defaultConfig {
            versionCode 1
            versionName "1.0"
        }
    
    • 1
    • 2
    • 3
    • 4

    调用看看有没有生效:

        override fun onResume() {
            super.onResume()
    
            val packageInfo = applicationContext.packageManager.getPackageInfo(applicationContext.packageName, 0)
            Log.wtf("yechaoa", "versionName = " + packageInfo.versionName)
            Log.wtf("yechaoa", "versionCode = " + packageInfo.versionCode)
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出:

    E  versionName = 1.0-测试包
    E  versionCode = 1
    
    • 1
    • 2

    生效是生效了,但是这种方式可以改versionName,改不了versionCode,有一定局限性。

    动态Property

    这种呢,就是通过占位符的方式,原有的versionName和versionCode不是写死的,通过动态获取的方式来设置,我们就可以打包脚本里加上VersionName的配置,以达到编译期动态修改的效果,这种方式在CI/CD中比较常见。

    先加两个方法来获取versionName和versionCode:

    def getVersionNameByProperty() {
        def name
        if (hasProperty("VersionName") && getProperty('VersionName') != null) {
            name = getProperties().get('VersionName')
        } else {
            name = "2.0-default"
        }
        return name
    }
    
    def getVersionCodeByProperty() {
        def code
        if (hasProperty("VersionCode") && getProperties().get('VersionCode') != null) {
            code = getProperties().get('VersionCode')
        } else {
            code = 2
        }
        return code
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    defaultConfig引用方法:

        defaultConfig {
            versionCode getVersionCodeByProperty()
            versionName getVersionNameByProperty()
        }
    
    • 1
    • 2
    • 3
    • 4

    如上,正常点击按钮编译versionName应该是"2.0-default",versionCode应该是的2。
    我们编译看下是不是符合预期:

     E  versionName = 2.0-default-测试包
     E  versionCode = 2
    
    • 1
    • 2

    符合预期,而且加上了我们在buildType里面设置的后缀“测试包”。

    那么接下来再用命令执行试试:

    ./gradlew installDebug -PVersionName=2.0-property
    
    • 1

    这里只是加了-PVersionName,VerisonCode同理。

    输出:

     E  versionName = 2.0-property-测试包
     E  versionCode = 2
    
    • 1
    • 2

    可以看到,versionName由「2.0-default-测试包」变成「2.0-property-测试包」了,我们的效果达到了。

    Variants

    variants是变体的意思,在构建中通常用来配置不同的版本、不同的环境等,比如多渠道打包,上面刚介绍的buildType也是。
    但这里要介绍的variants方式,跟buildType的时间要晚一些,是在Configuration阶段,也就是拿到所有的variants之后再去改。
    variants能力是Gradle提供的,在Android中对应的是applicationVariants

    首先遍历所有variants,然后通过Flavor拿到versionName和versionCode,然后再赋值。

        applicationVariants.configureEach { variant ->
            def flavor = variant.mergedFlavor
            flavor.versionName = '3.0'
            flavor.versionCode = 3
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果编译报错:

    Caused by: java.lang.RuntimeException: com.android.build.gradle.internal.crash.ExternalApiUsageException: com.android.builder.errors.EvalIssueException: versionName cannot be set on a mergedFlavor directly.
    
    • 1

    然后瞅了下源码,是MergedFlavor里面抛出来的:

    class MergedFlavor(
        name: String,
        private val _applicationId: Property,
        private val services: VariantServices
    ) : AbstractProductFlavor(name), InternalBaseVariant.MergedFlavor {
    
      	//...
    
        override var versionCode: Int?
            get() = super.versionCode
            set(value) {
                // calling setVersionCode results in a sync Error because the manifest merger doesn't pick
                // up the change.
                reportErrorWithWorkaround("versionCode", "versionCodeOverride", value)
            }
    
        override var versionName: String?
            get() = super.versionName
            set(value) {
                // calling setVersionName results in a sync Error because the manifest merger doesn't pick
                // up the change.
                reportErrorWithWorkaround("versionName", "versionNameOverride", value)
            }
    
        private fun reportErrorWithWorkaround(
            fieldName: String,
            outputFieldName: String,
            fieldValue: Any?
        ) {
            val formattedFieldValue = if (fieldValue is String) {
                "\"" + fieldValue + "\""
            } else {
                fieldValue.toString()
            }
    
            val message = """$fieldName cannot be set on a mergedFlavor directly.
                    |$outputFieldName can instead be set for variant outputs using the following syntax:
                    |android {
                    |    applicationVariants.all { variant ->
                    |        variant.outputs.each { output ->
                    |            output.$outputFieldName = $formattedFieldValue
                    |        }
                    |    }
                    |}""".trimMargin()
    
            services.issueReporter.reportError(IssueReporter.Type.GENERIC, message)
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    调用setVersionName会导致同步错误,清单文件接收不到更新。

    当然异常里面也给了解决方案,使用versionNameOverride

        applicationVariants.configureEach { variant ->
            variant.outputs.each { output ->
                if (variant.buildType.name == "debug") {
                    output.versionNameOverride = "3.0"
                    output.versionCodeOverride = 3
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后我们这里还把versionName和versionCode升级了一下,再来试试。
    输出:

     E  versionName = 3.0
     E  versionCode = 3
    
    • 1
    • 2

    ok,这达到我们想要效果了。

    总结

    本文通过BuildType、Property、Variants三种方式介绍了动态修改VersionCode和VersionName的方法,但是他们的时机却有不同。

    • BuildType:有局限性,仅能修改VersionName,无法修改VersionCode;
    • Property:使用比较方便,在云编译场景比较常见,本地的话可以写在打包脚本里面;
    • Variants:比较彻底,能完全覆盖,而且也可以抽成Plugin,但是如果有云编译的话,会导致云编译的更改失效;

    最后

    以上即是本文介绍内容,学废了吗,写作不易,记得三连~

    GitHub

    https://github.com/yechaoa/GradleX

  • 相关阅读:
    docker 容器的常用操作
    以创新抵御经济环境压力:自动化测试ReadyAPI帮助企业平衡软件质量与成本控制
    你的 Sleep 服务会梦到服务网格外的 bookinfo 吗
    java中的数据类型
    李书福为何要亲自挂帅造手机?
    使用Jenkins执行TestNg+Selenium+Jsoup自动化测试和生成ExtentReport测试报告
    Java格式化类Format
    Strtok函数切割字符串(附代码演示)
    [附源码]java毕业设计基于web的球类体育馆预定系统
    算法--排序算法效率比较
  • 原文地址:https://blog.csdn.net/yechaoa/article/details/134067681