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

前言

有个读者问了一个比较有意思又很常见的问题,怎么修改VersionName和VersionCode?

这位读者提问的方式也挺好,报错信息和尝试过的方式都提到了。

关于报错以及解决方案,正如上图我评论回复所说。

静态修改直接在build.gradle文件改就好了,动态修改还是有点意思的。

但是这种需求也比较常见,比如加上git commit id啊,时间戳啊等等,以便于区分,省的测试同学提了bug搞半天是包不对...

那么本文就来带大家实践一下,以及探索有没有其他方式。

配置BuildType

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

arduino 复制代码
    buildTypes {
        debug{
            versionNameSuffix "-测试包"
        }
        release {
            versionNameSuffix "-正式包"
        }
    }

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

arduino 复制代码
    defaultConfig {
        versionCode 1
        versionName "1.0"
    }

调用看看有没有生效:

kotlin 复制代码
    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)
    }

输出:

ini 复制代码
E  versionName = 1.0-测试包
E  versionCode = 1

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

动态Property

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

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

csharp 复制代码
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
}

defaultConfig引用方法:

scss 复制代码
    defaultConfig {
        versionCode getVersionCodeByProperty()
        versionName getVersionNameByProperty()
    }

如上,正常点击按钮编译versionName应该是"2.0-default",versionCode应该是的2。

我们编译看下是不是符合预期:

ini 复制代码
 E  versionName = 2.0-default-测试包
 E  versionCode = 2

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

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

ini 复制代码
./gradlew installDebug -PVersionName=2.0-property

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

输出:

ini 复制代码
 E  versionName = 2.0-property-测试包
 E  versionCode = 2

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

Variants

variants是变体的意思,在构建中通常用来配置不同的版本、不同的环境等,比如多渠道打包,上面刚介绍的buildType也是。

但这里要介绍的variants方式,跟buildType的时间要晚一些,是在Configuration阶段,也就是拿到所有的variants之后再去改。

variants能力是Gradle提供的,在Android中对应的是applicationVariants

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

ini 复制代码
    applicationVariants.configureEach { variant ->
        def flavor = variant.mergedFlavor
        flavor.versionName = '3.0'
        flavor.versionCode = 3
    }

结果编译报错:

csharp 复制代码
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.

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

kotlin 复制代码
class MergedFlavor(
    name: String,
    private val _applicationId: Property<String>,
    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)
    }
}

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

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

ini 复制代码
    applicationVariants.configureEach { variant ->
        variant.outputs.each { output ->
            if (variant.buildType.name == "debug") {
                output.versionNameOverride = "3.0"
                output.versionCodeOverride = 3
            }
        }
    }

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

输出:

ini 复制代码
 E  versionName = 3.0
 E  versionCode = 3

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

总结

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

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

最后

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

GitHub

github.com/yechaoa/Gra...

相关推荐
Sapphire~1 天前
【前端基础】02-命令式组件系统 | 声明式组件系统 | 响应式组件系统
前端
成都大菠萝1 天前
1-2-3 Kotlin与C++基础-JNI原理与使用
android
TimeFine1 天前
Android AI解放生产力(六)实战:解放页面开发前的繁琐工作
android·架构
心静财富之门1 天前
a.py打包加密
android
骑驴看星星a1 天前
【回顾React的一些小细节】render里不可包含的东西
前端·javascript·react.js
小白阿龙1 天前
浮动元素导致父元素高度塌陷
前端
惜.己1 天前
前端笔记(三)
前端·笔记
妮妮喔妮1 天前
Nextjs的SSR服务器端渲染为什么优化了首屏加载速度?
开发语言·前端·javascript
专注于找bug的wgwgwg21 天前
标准答案,无论采用哪种实现方式,本质都是在安全性
前端
LYFlied1 天前
【每日算法】131. 分割回文串
前端·数据结构·算法·leetcode·面试·职场和发展