【问题标题】:Autoincrement VersionCode with gradle extra properties具有 gradle 额外属性的自动增量版本代码
【发布时间】:2014-02-19 17:50:39
【问题描述】:

我正在使用 gradle 构建一个 Android 应用。到目前为止,我使用 Manifest 文件来增加 versionCode,但我想从外部文件中读取 versionCode,并根据它是发布风格还是调试风格来增加 versionCode。我尝试了额外的属性,但你无法保存它们,这意味着下次我构建它时,我会得到相同的 versionCode。 任何帮助将不胜感激!

project.ext{
    devVersionCode = 13
    releaseVersionCode = 1
}

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.6.+'
    }
}

apply plugin: 'android'

repositories {
    mavenCentral()
}

dependencies {
    compile project(':Cropper')
    compile "com.android.support:appcompat-v7:18.0.+"
    compile "com.android.support:support-v4:18.0.+"
    compile fileTree(dir: 'libs', include: '*.jar')
}

def getReleaseVersionCode() {
    def version = project.releaseVersionCode + 1
    project.releaseVersionCode = version
    println sprintf("Returning version %d", version)
    return version
}

def getDevVersionCode() {
    def version = project.devVersionCode + 1
    project.devVersionCode = version
    println sprintf("Returning version %d", version)
    return version
}


def getLastVersioName(versionCode) {
    return "0.0." + versionCode
}

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.0"

    defaultConfig {
        minSdkVersion 9
        targetSdkVersion 19
    }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }
    }

    buildTypes {
        release {
            runProguard true
            proguardFile getDefaultProguardFile('proguard-android-optimize.txt')
            proguardFile 'proguard.cfg'
            debuggable false
            signingConfig null
            zipAlign false
        }
        debug {
            versionNameSuffix "-DEBUG"
        }
    }
    productFlavors {
        dev {
            packageName = 'com.swisscom.docsafe.debug'
            versionCode getDevVersionCode()
            versionName getLastVersioName(project.devVersionCode)
        }
        prod {
            packageName = 'com.swisscom.docsafe'
            versionCode getReleaseVersionCode()
            versionName getLastVersioName(project.releaseVersionCode)
        }
    }
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.8'
}

【问题讨论】:

标签: android gradle android-gradle-plugin build.gradle


【解决方案1】:

<yourProjectLocation>/app/version.properties中创建新文件

MAJOR=0
MINOR=0
PATCH=1
VERSION_CODE=1

build.gradle(模块文件)中添加以下行:

android {
    // other properties....
    // add following lines...

    def _versionCode=0
    def _major=0
    def _minor=0
    def _patch=0

    def versionPropsFile = file('version.properties')

    if (versionPropsFile.canRead()) {
        def Properties versionProps = new Properties()

        versionProps.load(new FileInputStream(versionPropsFile))

        _patch = versionProps['PATCH'].toInteger() + 1
        _major = versionProps['MAJOR'].toInteger()
        _minor = versionProps['MINOR'].toInteger() 
        _versionCode= versionProps['VERSION_CODE'].toInteger()+1
        
        if(_patch==100) {
            _patch=0
            _minor=_minor+1
        }
        if(_minor == 10){
            _minor = 0
            _major =_major + 1
        }

        versionProps['MAJOR']=_major.toString()
        versionProps['MINOR']=_minor.toString()
        versionProps['PATCH']=_patch.toString()
        versionProps['VERSION_CODE']=_versionCode.toString()
        versionProps.store(versionPropsFile.newWriter(), null)
    }
    else {
        throw new GradleException("Could not read version.properties!")
    }

    def _versionName = "${_major}.${_minor}.${_patch}(${_versionCode})"

    defaultConfig {
        // other properties...
        // change only these two lines

        versionCode _versionCode
        versionName _versionName
    }
}

输出:0.0.1(1)

【讨论】:

  • 谢谢。为什么要在 versionName 中插入 versionCode?甚至排在第二位。
  • 但它看起来像 1.71.3.76,例如。我认为,最好是 1.3.76,将其与版本代码分开。
  • 是的。您可以更改为“${_major}..${_minor}.${_patch}.${_versionCode}”或删除补丁
  • if(_major==99) 应该是 if(_minor==99) ??
【解决方案2】:

我没有在属性文件中指定新版本,而是创建了一个 Gradle 任务,它可以自动更新当前的 versionNameversionCode,还可以从命令行获取新版本字符串(通过向任务传递参数-P 后跟 =)。

应用程序build.gradle.kts

project.version = "1.2.3"

tasks.create("incrementVersion") {
    group = "versioning"
    description = "Increments the version to make the app ready for next release."
    doLast {
        var (major, minor, patch) = project.version.toString().split(".")
        val mode = project.properties["mode"]?.toString()?.toLowerCaseAsciiOnly()
        if (mode == "major") {
            major = (major.toInt() + 1).toString()
            minor = "0"
            patch = "0"
        } else if (mode == "minor") {
            minor = (minor.toInt() + 1).toString()
            patch = "0"
        } else {
            patch = (patch.toInt() + 1).toString()
        }
        var newVersion = "$major.$minor.$patch"

        val overrideVersion = project.properties["overrideVersion"]?.toString()?.toLowerCaseAsciiOnly()
        overrideVersion?.let { newVersion = it }

        val newBuild = buildFile
            .readText()
            .replaceFirst(Regex("version = .+"), "version = \"$newVersion\"")
            .replaceFirst(Regex("versionName = .+\""), "versionName = \"$newVersion\"")
            .replaceFirst(Regex("versionCode = \\d+"), "versionCode = ${(android.defaultConfig.versionCode ?: 0) + 1}")
        buildFile.writeText(newBuild)
    }
}

用法:

gradlew incrementVersion [-P[mode=major|minor|patch]|[overrideVersion=x.y.z]]

例子:

gradlew :app:incrementVersion -Pmode=major
gradlew :app:incrementVersion -PoverrideVersion=4.5.6

【讨论】:

    【解决方案3】:

    我非常喜欢两种解决方案。第一个依赖于 Play Store,另一个依赖于 Git。

    使用 Play 商店,您可以通过查看可用的最高上传版本代码来增加版本代码。此解决方案的好处是,APK 上传永远不会失败,因为您的版本代码始终比 Play 商店中的版本代码高一个。缺点是在 Play 商店之外分发您的 APK 变得更加困难。您可以使用 Gradle Play Publisher 进行设置,方法是按照 quickstart guide 并将插件告知 resolve version codes automatically

    plugins {
        id 'com.android.application'
        id 'com.github.triplet.play' version 'x.x.x'
    }
    
    android {
        ...
    }
    
    play {
        serviceAccountCredentials = file("your-credentials.json")
        resolutionStrategy = "auto"
    }
    

    使用 Git,您可以根据您的存储库有多少提交和标签来增加版本代码。这里的好处是您的输出是可重现的,并且不依赖于您的存储库之外的任何内容。不利的一面是您必须进行新的提交或标记才能影响您的版本代码。您可以通过添加Version Master Gradle plugin 来设置:

    plugins {
        id 'com.android.application'
        id 'com.supercilex.gradle.versions' version 'x.x.x'
    }
    
    android {
        ...
    }
    

    【讨论】:

      【解决方案4】:

      上面显示的示例由于不同的原因不起作用

      这是基于本文想法的即用型变体:

      android {
          compileSdkVersion 28
      
          // https://stackoverflow.com/questions/21405457
      
          def propsFile = file("version.properties")
          // Default values would be used if no file exist or no value defined
          def customAlias = "Alpha"
          def customMajor = "0"
          def customMinor = "1"
          def customBuild = "1" // To be incremented on release
      
          Properties props = new Properties()
          if (propsFile .exists())
              props.load(new FileInputStream(propsFile ))
      
          if (props['ALIAS'] == null) props['ALIAS'] = customAlias else customAlias = props['ALIAS']
          if (props['MAJOR'] == null) props['MAJOR'] = customMajor else customMajor = props['MAJOR']
          if (props['MINOR'] == null) props['MINOR'] = customMinor else customMinor = props['MINOR']
          if (props['BUILD'] == null) props['BUILD'] = customBuild else customBuild = props['BUILD']
      
          if (gradle.startParameter.taskNames.join(",").contains('assembleRelease')) {
              customBuild = "${customBuild.toInteger() + 1}"
              props['BUILD'] = "" + customBuild
      
              applicationVariants.all { variant ->
                  variant.outputs.all { output ->
                      if (output.outputFile != null && (output.outputFile.name == "app-release.apk"))
                          outputFileName = "app-${customMajor}-${customMinor}-${customBuild}.apk"
                  }
              }
          }
      
          props.store(propsFile.newWriter(), "Incremental Build Version")
      
          defaultConfig {
              applicationId "org.example.app"
              minSdkVersion 21
              targetSdkVersion 28
              versionCode customBuild.toInteger()
              versionName "$customAlias $customMajor.$customMinor ($customBuild)"
      
              ...
          }
      ...
      }
      

      【讨论】:

        【解决方案5】:

        在 Mac 上的 Gradle 5.1.1 版本中,我更改了任务名称的检索方式,我想尝试从构建中获取构建风格/类型,但懒得拆分任务名称:

        def versionPropsFile = file('version.properties')
        if (versionPropsFile.canRead()) {
            def Properties versionProps = new Properties()
        
            versionProps.load(new FileInputStream(versionPropsFile))
        
            def value = 0
        
            def runTasks = gradle.getStartParameter().getTaskRequests().toString()
        
            if (runTasks.contains('assemble') || runTasks.contains('assembleRelease') || runTasks.contains('aR')) {
                value = 1
            }
        
            def versionMajor = 1
            def versionMinor = 0
            def versionPatch = versionProps['VERSION_PATCH'].toInteger() + value
            def versionBuild = versionProps['VERSION_BUILD'].toInteger() + 1
            def versionNumber = versionProps['VERSION_NUMBER'].toInteger() + value
        
            versionProps['VERSION_PATCH'] = versionPatch.toString()
            versionProps['VERSION_BUILD'] = versionBuild.toString()
            versionProps['VERSION_NUMBER'] = versionNumber.toString()
        
            versionProps.store(versionPropsFile.newWriter(), null)
        
            defaultConfig {
                applicationId "de.evomotion.ms10"
                minSdkVersion 21
                targetSdkVersion 28
                versionCode versionNumber
                versionName "${versionMajor}.${versionMinor}.${versionPatch} (${versionBuild})"
                testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
                signingConfig signingConfigs.debug
            }
        
        } else {
            throw new GradleException("Could not read version.properties!")
        }
        

        代码来自@just_user this one

        【讨论】:

          【解决方案6】:

          第一个注释的代码将在每个“重建项目”时增加数字并将值保存在“版本属性”文件中。

          第二条注释代码会在“Build APKs”时生成新版本的APK文件。

          android {
              compileSdkVersion 28
              buildToolsVersion "29.0.0"
              //==========================START==================================
              def Properties versionProps = new Properties()
              def versionPropsFile = file('version.properties')
              if(versionPropsFile.exists())
                  versionProps.load(new FileInputStream(versionPropsFile))
              def code = (versionProps['VERSION_CODE'] ?: "0").toInteger() + 1
              versionProps['VERSION_CODE'] = code.toString()
              versionProps.store(versionPropsFile.newWriter(), null)
              //===========================END===================================
              defaultConfig {
                  applicationId "com.example.myapp"
                  minSdkVersion 15
                  targetSdkVersion 28
                  versionCode 1
                  versionName "0.19"
                  testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
              }
              buildTypes {
                  release {
                      minifyEnabled false
                      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
                      //=======================================START===============================================
                      android.applicationVariants.all { variant ->
                          variant.outputs.all {
                              def appName = "MyAppSampleName"
                              outputFileName = appName+"_v${variant.versionName}.${versionProps['VERSION_CODE']}.apk"
                          }
                      }
                      //=======================================END===============================================
                  }
              }
          }
          

          【讨论】:

          • 请添加一些文本来显示您所做的更改。以及为什么
          【解决方案7】:

          我想从外部文件中读取版本代码

          我确信有许多可能的解决方案;这是一个:

          android {
              compileSdkVersion 18
              buildToolsVersion "18.1.0"
          
              def versionPropsFile = file('version.properties')
          
              if (versionPropsFile.canRead()) {
                  def Properties versionProps = new Properties()
          
                  versionProps.load(new FileInputStream(versionPropsFile))
          
                  def code = versionProps['VERSION_CODE'].toInteger() + 1
          
                  versionProps['VERSION_CODE']=code.toString()
                  versionProps.store(versionPropsFile.newWriter(), null)
          
                  defaultConfig {
                      versionCode code
                      versionName "1.1"
                      minSdkVersion 14
                      targetSdkVersion 18
                  }
              }
              else {
                  throw new GradleException("Could not read version.properties!")
              }
          
              // rest of android block goes here
          }
          

          此代码需要一个现有的version.properties 文件,您将在第一次构建之前手动创建该文件以具有VERSION_CODE=8

          此代码只是在每次构建时增加版本代码 - 您需要扩展该技术以处理每个风味的版本代码。

          您可以看到演示此代码的the Versioning sample project

          【讨论】:

          • 如何在发布构建时启用此版本递增?
          • @Piotr:如果您的意思是“仅在发布版本中增加数字”,那应该是可能的,尽管我不知道细节。就个人而言,由于有大约 20 亿个版本代码可用,我假设我不会用完。 :-)
          • @Piotr 您将创建一个单独增加版本代码的任务,然后执行assembleRelease.finalizedBy incrementVersion 或类似的操作。整理后我会发布我的代码。
          • 使用自定义任务,您可以执行类似./gradlew incrementVersionCode build 的操作。以这种方式顺序调用的任务将在任何任务失败时立即停止。
          • 因为@chris.jenkins 仍在更新他的代码:p 这是上面的任务和方法形式gist.github.com/doridori/544c24509be236c11fd5,可以在android DSL 中使用versionCode getIncrementingVersionCode()
          【解决方案8】:

          这是我之前答案的现代化,如下所示。这个是与 Gradle 4.4Android Studio 3.1.1 一起运行的。

          这个脚本的作用:

          • 如果不存在 version.properties 文件,则创建一个文件(在下面投票 Paul Cantrell 的答案,如果你喜欢这个答案,我就是从那里得到这个想法的)
          • 对于每个构建、调试版本或您在 Android Studio 中按下运行按钮的任何时候,VERSION_BUILD 编号都会增加。
          • 每次您为 Play 商店组装一个版本时,您的 Android versionCode 都会增加,您的 补丁号 也会增加。
          • 奖励:构建完成后,将您的 apk 复制到 projectDir/apk 以使其更易于访问。

          此脚本将创建一个类似于 v1.3.4 (123) 的版本号并构建一个类似于 AppName-v1.3.4.apk 的 apk 文件。

          Major version ⌄       ⌄ Build version
                       v1.3.4 (123)
            Minor version ⌃|⌃ Patch version
          

          主要版本:必须手动更改以进行更大的更改。

          次要版本:必须手动更改以进行稍微不太大的更改。

          补丁版本:运行时增加gradle assembleRelease

          构建版本:增加每个构建

          版本号:补丁版本相同,这是 Play Store 需要为每次新​​的 apk 上传增加的版本代码。

          只需更改下面标记为 1 - 3 的 cmets 中的内容,其余的由脚本完成。 :)

          android {
              compileSdkVersion 27
              buildToolsVersion '27.0.3'
          
              def versionPropsFile = file('version.properties')
              def value = 0
              Properties versionProps = new Properties()
              if (!versionPropsFile.exists()) {
                  versionProps['VERSION_PATCH'] = "0"
                  versionProps['VERSION_NUMBER'] = "0"
                  versionProps['VERSION_BUILD'] = "-1" // I set it to minus one so the first build is 0 which isn't super important. 
                  versionProps.store(versionPropsFile.newWriter(), null)
              }
          
              def runTasks = gradle.startParameter.taskNames
              if ('assembleRelease' in runTasks) {
                  value = 1
              }
          
              def mVersionName = ""
              def mFileName = ""
          
              if (versionPropsFile.canRead()) {
                  versionProps.load(new FileInputStream(versionPropsFile))
          
                  versionProps['VERSION_PATCH'] = (versionProps['VERSION_PATCH'].toInteger() + value).toString()
                  versionProps['VERSION_NUMBER'] = (versionProps['VERSION_NUMBER'].toInteger() + value).toString()
                  versionProps['VERSION_BUILD'] = (versionProps['VERSION_BUILD'].toInteger() + 1).toString()
          
                  versionProps.store(versionPropsFile.newWriter(), null)
          
                  // 1: change major and minor version here
                  mVersionName = "v1.0.${versionProps['VERSION_PATCH']}"
                  // 2: change AppName for your app name
                  mFileName = "AppName-${mVersionName}.apk"
          
                  defaultConfig {
                      minSdkVersion 21
                      targetSdkVersion 27
                      applicationId "com.example.appname" // 3: change to your package name
                      versionCode versionProps['VERSION_NUMBER'].toInteger()
                      versionName "${mVersionName} Build: ${versionProps['VERSION_BUILD']}"
                  }
          
              } else {
                  throw new FileNotFoundException("Could not read version.properties!")
              }
          
              if ('assembleRelease' in runTasks) {
                  applicationVariants.all { variant ->
                      variant.outputs.all { output ->
                          if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
                              outputFileName = mFileName
                          }
                      }
                  }
              }
          
              task copyApkFiles(type: Copy){
                  from 'build/outputs/apk/release'
                  into '../apk'
                  include mFileName
              }
          
              afterEvaluate {
                  assembleRelease.doLast {
                      tasks.copyApkFiles.execute()
                  }
              }
          
              signingConfigs {
                  ...
              }
          
              buildTypes {
                  ...
              }
          }
          

          ================================================= =====

          初步回答:

          我希望 versionName 也自动增加。所以这只是对 CommonsWare 答案的补充,它对我来说非常有效。这对我有用

          defaultConfig {
              versionCode code
              versionName "1.1." + code
              minSdkVersion 14
              targetSdkVersion 18
          }
          

          编辑:

          由于我有点懒,我希望我的版本控制尽可能自动地工作。我想要的是有一个随着每次构建而增加的Build Version,而Version NumberVersion Name只有在我发布时才会增加建造。

          这是我过去一年一直在使用的,基本来自 CommonsWare 的答案和我之前的答案,还有更多。这会产生以下版本控制:

          版本名称:1.0.5 (123) --> Major.Minor.Patch (Build),Major 和 Minor 是手动更改的。

          在 build.gradle 中:

          ...
          android {
              compileSdkVersion 23
              buildToolsVersion '23.0.1'
              def versionPropsFile = file('version.properties')
              if (versionPropsFile.canRead()) {
                  def Properties versionProps = new Properties()
          
                  versionProps.load(new FileInputStream(versionPropsFile))
          
                  def value = 0
          
                  def runTasks = gradle.startParameter.taskNames
                  if ('assemble' in runTasks || 'assembleRelease' in runTasks || 'aR' in runTasks) {
                      value = 1;
                  }
          
                  def versionMajor = 1
                  def versionMinor = 0
                  def versionPatch = versionProps['VERSION_PATCH'].toInteger() + value
                  def versionBuild = versionProps['VERSION_BUILD'].toInteger() + 1
                  def versionNumber = versionProps['VERSION_NUMBER'].toInteger() + value
          
                  versionProps['VERSION_PATCH'] = versionPatch.toString()
                  versionProps['VERSION_BUILD'] = versionBuild.toString()
                  versionProps['VERSION_NUMBER'] = versionNumber.toString()
          
                  versionProps.store(versionPropsFile.newWriter(), null)
          
                  defaultConfig {
                      versionCode versionNumber
                      versionName "${versionMajor}.${versionMinor}.${versionPatch} (${versionBuild}) Release"
                      minSdkVersion 14
                      targetSdkVersion 23
                  }
          
                  applicationVariants.all { variant ->
                      variant.outputs.each { output ->
                          def fileNaming = "apk/RELEASES"
                          variant.outputs.each { output ->
                              def outputFile = output.outputFile
                              if (outputFile != null && outputFile.name.endsWith('.apk')) {
                                  output.outputFile = new File(getProject().getRootDir(), "${fileNaming}-${versionMajor}.${versionMinor}.${versionPatch}-${outputFile.name}")
                              }
                          }
                      }
                  }
          
              } else {
                  throw new GradleException("Could not read version.properties!")
              }
          
              ...
          }
          
          ...
          

          如果您通过终端使用 'assemble''assembleRelease''aR' 组装项目,则增加补丁和版本代码在您的项目根目录中创建一个名为 apk/RELEASE 的新文件夹,这样您就不必查看 build/outputs/more/more/more 来找到您的 apk。

          您的版本属性需要如下所示:

          VERSION_NUMBER=1
          VERSION_BUILD=645
          VERSION_PATCH=1
          

          显然从 0 开始。:)

          【讨论】:

          • 第二个'variant.outputs.each { output ->'可以去掉,对应的'}'。
          • 这导致我所有的构建都只在 x86_64 本机代码中
          • 我现在可以确认这不是根本原因。我很抱歉。
          • 我似乎需要在 runTasks 中找到 ':app:assembleRelease' 而不仅仅是 'assembleRelease'
          • @AlexanderGavriliuk 它是 Play 商店使用的版本代码,每次上传都需要不断增加。如果您增加主要或次要版本,补丁号可能会被重置。如果您将应用程序上传到具有相同包名的应用商店,则版本号永远不会被重置。
          【解决方案9】:

          使用Gradle Task Graph,我们可以检查/切换构建类型。

          基本思想是在每次构建时增加 versionCode。在每个构建一个存储在 version.properties 文件中的计数器。它将在每个新的 APK 构建时保持更新,并用这个递增的计数器值替换 build.gradle 文件中的 versionCode 字符串。

          apply plugin: 'com.android.application'
          
          android {
          compileSdkVersion 25
          buildToolsVersion '25.0.2'
          
          def versionPropsFile = file('version.properties')
          def versionBuild
          
          /*Setting default value for versionBuild which is the last incremented value stored in the file */
          if (versionPropsFile.canRead()) {
              def Properties versionProps = new Properties()
              versionProps.load(new FileInputStream(versionPropsFile))
              versionBuild = versionProps['VERSION_BUILD'].toInteger()
          } else {
              throw new FileNotFoundException("Could not read version.properties!")
          }
          
          
          /*Wrapping inside a method avoids auto incrementing on every gradle task run. Now it runs only when we build apk*/
          ext.autoIncrementBuildNumber = {
          
              if (versionPropsFile.canRead()) {
                  def Properties versionProps = new Properties()
                  versionProps.load(new FileInputStream(versionPropsFile))
                  versionBuild = versionProps['VERSION_BUILD'].toInteger() + 1
                  versionProps['VERSION_BUILD'] = versionBuild.toString()
                  versionProps.store(versionPropsFile.nminSdkVersion 14
                  targetSdkVersion 21
                  versionCode 1ewWriter(), null)
              } else {
                  throw new FileNotFoundException("Could not read version.properties!")
              }
          }
          
          
          defaultConfig {
              minSdkVersion 16
              targetSdkVersion 21
              versionCode 1
              versionName "1.0.0." + versionBuild
          }
          
          buildTypes {
              release {
                  minifyEnabled false
                  proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
              }
          }
          
          // Hook to check if the release/debug task is among the tasks to be executed.
          //Let's make use of it
          gradle.taskGraph.whenReady {taskGraph ->
              if (taskGraph.hasTask(assembleDebug)) {  /* when run debug task */
                  autoIncrementBuildNumber()
              } else if (taskGraph.hasTask(assembleRelease)) { /* when run release task */
                  autoIncrementBuildNumber()
              }
            }
          }
          
          dependencies {
            compile fileTree(dir: 'libs', include: ['*.jar'])
            compile 'com.android.support:appcompat-v7:25.3.1'
          }
          

          将上述脚本放在主模块的 build.gradle 文件中。

          参考网站: http://devdeeds.com/auto-increment-build-number-using-gradle-in-android/

          感谢和问候!

          【讨论】:

            【解决方案10】:

            另一种自动获取versionCode 的方法是将versionCode 设置为签出git 分支中的提交数。它实现了以下目标:

            1. versionCode 在任何机器(包括Continuous Integration 和/或Continuous Deployment 服务器)上自动且一致地生成。
            2. 带有此versionCode 的应用可提交到 GooglePlay。
            3. 不依赖 repo 之外的任何文件。
            4. 不向 repo 推送任何内容
            5. 如果需要,可以手动覆盖

            使用gradle-git 库来完成上述目标。将以下代码添加到您的build.gradle 文件/app 目录:

            import org.ajoberstar.grgit.Grgit
            
            repositories {
                mavenCentral()
            }
            
            buildscript {
                repositories {
                    mavenCentral()
                }
            
                dependencies {
                    classpath 'org.ajoberstar:grgit:1.5.0'
                }
            }
            
            android {
            /*
                if you need a build with a custom version, just add it here, but don't commit to repo,
                unless you'd like to disable versionCode to be the number of commits in the current branch.
            
                ex. project.ext.set("versionCodeManualOverride", 123)
            */
                project.ext.set("versionCodeManualOverride", null)
            
                defaultConfig {
                    versionCode getCustomVersionCode()
                }
            }
            
            def getCustomVersionCode() {
            
                if (project.versionCodeManualOverride != null) {
                    return project.versionCodeManualOverride
                }
            
                // current dir is <your proj>/app, so it's likely that all your git repo files are in the dir
                // above.
                ext.repo = Grgit.open(project.file('..'))
            
                // should result in the same value as running
                // git rev-list <checked out branch name> | wc -l
                def numOfCommits = ext.repo.log().size()
                return numOfCommits
            }
            

            注意:要使此方法起作用,最好仅从同一分支(例如master)部署到 Google Play 商店。

            【讨论】:

            • 虽然本质上是一个优雅的解决方案,但我可以想象这会大大减慢构建时间,具体取决于 2 条 git 行中到底发生了什么。有这方面的经验吗?
            • 如果禁用此步骤,我不会注意到任何性能改进。一年多来一直在本地和我们的构建机器上使用方法,性能没有任何问题。如果您发现任何性能问题,请告诉我!
            • 虽然您的解决方案很优雅,但它可能会带来一些令人讨厌的意外惊喜。 versionCodes 总是比以前的版本大,这一点很重要。如果您有一个包含 50 个提交的分支,然后创建另一个更新但只有 40 个提交的分支,这可能是由于从某个合并的功能中压缩了许多提交,该怎么办。我可以看到很多原因,您的提交历史并不总是增量提交的线性流。
            • @JHH 这些结果并不意外。正如我在注释中提到的,这种方法在从同一分支部署时效果最佳。
            • 我会说,为什么不将分支添加到“只是为了确定”的版本......所以你有版本“master-123”。
            【解决方案11】:

            我查看了一些选项来执行此操作,最终决定将当前时间用于 versionCode 而不是尝试自动增加 versionCode 并将其签入我的修订控制系统更简单。

            将以下内容添加到您的build.gradle

            /**
             * Use the number of seconds/10 since Jan 1 2016 as the versionCode.
             * This lets us upload a new build at most every 10 seconds for the
             * next 680 years.
             */
            def vcode = (int)(((new Date().getTime()/1000) - 1451606400) / 10)
            
            android {
                defaultConfig {
                    ...
                    versionCode vcode
                }
            }
            

            但是,如果您希望上传 2696 年之后的版本,您可能需要使用不同的解决方案。

            【讨论】:

            • 如果你现在正在阅读这篇文章,并且从一个新应用开始,你可以减去1510351294 :))
            • 我不明白!您不使用分析或崩溃分析或任何其他提供带有版本代码和名称的数据的服务吗?你这么容易玩这些吗?
            • 如果你有合理的版本名称,我认为没问题。 Crashalytics 也提供版本名称。
            • @emmby 你从哪里得到“1451606400”号码?或来自#Entreco 的“1510351294”,我一直试图徒劳地计算!
            • @JosephWambura 这只是当前的 unix 时间戳。
            【解决方案12】:

            归功于 CommonsWare(接受的答案) Paul Cantrell(如果文件不存在则创建文件) ahmad aghazadeh(版本名称和代码)

            所以我把他们所有的想法混合在一起,然后想出了这个。这正是第一篇文章所要求的拖放解决方案。

            它会根据发布状态自动更新versionCode和versionName。当然,您可以移动变量以满足您的需求。

            def _versionCode=0
            def versionPropsFile = file('version.properties')
            def Properties versionProps = new Properties()
            if(versionPropsFile.exists())
                versionProps.load(new FileInputStream(versionPropsFile))
                def _patch = (versionProps['PATCH'] ?: "0").toInteger() + 1
                def _major = (versionProps['MAJOR'] ?: "0").toInteger()
                def _minor = (versionProps['MINOR'] ?: "0").toInteger()
                List<String> runTasks = gradle.startParameter.getTaskNames();
                def value = 0
                for (String item : runTasks)
                    if ( item.contains("assembleRelease")) {
                        value = 1;
                    }
                _versionCode = (versionProps['VERSION_CODE'] ?: "0").toInteger() + value
                if(_patch==99)
                {
                    _patch=0
                    _minor=_minor+1
                }
                if(_major==99){
                    _major=0
                    _major=_major+1
                }
            
            versionProps['MAJOR']=_major.toString()
            versionProps['MINOR']=_minor.toString()
            versionProps['PATCH']=_patch.toString()
            versionProps['VERSION_CODE']=_versionCode.toString()
            versionProps.store(versionPropsFile.newWriter(), null)
            def _versionName = "${_major}.${_versionCode}.${_minor}.${_patch}"
            
            compileSdkVersion 24
            buildToolsVersion "24.0.0"
            
            defaultConfig {
                applicationId "com.yourhost.yourapp"
                minSdkVersion 16
                targetSdkVersion 24
                versionCode _versionCode
                versionName _versionName
            }
            

            【讨论】:

              【解决方案13】:

              AndroidManifest.xml中定义versionName

              android:versionName="5.1.5"
              

              在应用级别的build.gradle 中的android{...} 块内:

              defaultConfig {
                      applicationId "com.example.autoincrement"
                      minSdkVersion 18
                      targetSdkVersion 23
                      multiDexEnabled true
                      def version = getIncrementationVersionName()
                      versionName version
              }
              

              在应用级别的build.gradle 之外的android{...} 块:

              def getIncrementedVersionName() {
                  List<String> runTasks = gradle.startParameter.getTaskNames();
              
                  //find version name in manifest
                  def manifestFile = file('src/main/AndroidManifest.xml')
                  def matcher = Pattern.compile('versionName=\"(\\d+)\\.(\\d+)\\.(\\d+)\"').matcher(manifestFile.getText())
                  matcher.find()
              
                  //extract versionName parts
                  def firstPart = Integer.parseInt(matcher.group(1))
                  def secondPart = Integer.parseInt(matcher.group(2))
                  def thirdPart = Integer.parseInt(matcher.group(3))
              
                  //check is runTask release or not
                  // if release - increment version
                  for (String item : runTasks) {
                      if (item.contains("assemble") && item.contains("Release")) {
                          thirdPart++
                          if (thirdPart == 10) {
                              thirdPart = 0;
                              secondPart++
                              if (secondPart == 10) {
                                  secondPart = 0;
                                  firstPart++
                              }
                          }
                      }
                  }
              
                  def versionName = firstPart + "." + secondPart + "." + thirdPart
              
                  // update manifest
                  def manifestContent = matcher.replaceAll('versionName=\"' + versionName + '\"')
                  manifestFile.write(manifestContent)
              
                  println "incrementVersionName = " + versionName
              
                  return versionName
              }
              

              创建 singed APK 后:

              android:versionName="5.1.6"
              

              注意:如果您的 versionName 与我的不同,则需要更改 regex提取部分逻辑

              【讨论】:

                【解决方案14】:

                仅在发布版本中增加 versionCode:

                android {
                    compileSdkVersion 21
                    buildToolsVersion "21.1.2"
                
                    def versionPropsFile = file('version.properties')
                    def code = 1;
                    if (versionPropsFile.canRead()) {
                        def Properties versionProps = new Properties()
                
                        versionProps.load(new FileInputStream(versionPropsFile))
                        List<String> runTasks = gradle.startParameter.getTaskNames();
                        def value = 0
                        for (String item : runTasks)
                        if ( item.contains("assembleRelease")) {
                            value = 1;
                        }
                        code = Integer.parseInt(versionProps['VERSION_CODE']).intValue() + value
                        versionProps['VERSION_CODE']=code.toString()
                        versionProps.store(versionPropsFile.newWriter(), null)
                    }
                    else {
                        throw new GradleException("Could not read version.properties!")
                    }
                
                    defaultConfig {
                        applicationId "com.pack"
                        minSdkVersion 14
                        targetSdkVersion 21
                        versionName "1.0."+ code
                        versionCode code
                    }
                

                期望现有的c://YourProject/app/version.properties 文件,您将在第一次构建之前手动创建该文件以具有VERSION_CODE=8

                文件 version.properties:

                VERSION_CODE=8

                【讨论】:

                • 不如把它放在一个任务中,让 generateReleaseBuildConfig 依赖于那个任务。
                • versionName "1.0."+ getSvnRevision() 导致错误。 getSvnRevision() 方法在哪里引用?你确定它不应该是 versionName "1.0."+ 代码(版本名称会随着你的版本代码增加)?
                • @portfoliobuilder,将 getSvnRevision() 替换为代码
                【解决方案15】:

                增加versionCodeversionName 的另一种选择是使用时间戳。

                defaultConfig {
                   versionName "${getVersionNameTimestamp()}"
                   versionCode getVersionCodeTimestamp()
                }
                
                
                def getVersionNameTimestamp() {
                    return new Date().format('yy.MM.ddHHmm')
                }
                
                def getVersionCodeTimestamp() {
                    def date = new Date()
                    def formattedDate = date.format('yyMMddHHmm')
                    def code = formattedDate.toInteger()
                    println sprintf("VersionCode: %d", code)
                    return code
                }
                

                从 2022 年 1 月 1 日开始 formattedDate = date.format('yyMMddHHmm') 超过整数的容量

                【讨论】:

                • 这个人想要一个自动递增的内部版本号
                • @peter_pilgrim Caro 是 OP。
                • 我认为这是一个优雅的解决方案,它不依赖于可能会消失或不会消失或会不断出现在 git 中的文件。它还可以帮助您在至少接下来的 12 年 22 年内唯一地识别每个构建。
                • 引用 developer.android.com:"警告:Google Play 允许 versionCode 的最大值是 2100000000。" 所以,截止日期实际上是 2021。
                • 修复int限制:省略分钟并使用日期格式'yyMMddHH',版本代码将至少包含小时。
                【解决方案16】:

                最近我正在为 Android 开发一个 gradle 插件,它可以自动生成 versionCodeversionName。有很多定制。在这里你可以找到更多关于它的信息 https://github.com/moallemi/gradle-advanced-build-version

                【讨论】:

                • 这是一个很棒的插件!感谢分享?
                【解决方案17】:

                如果版本文件不存在,CommonsWare 的优秀答案的略微收紧版本会创建版本文件:

                def Properties versionProps = new Properties()
                def versionPropsFile = file('version.properties')
                if(versionPropsFile.exists())
                    versionProps.load(new FileInputStream(versionPropsFile))
                def code = (versionProps['VERSION_CODE'] ?: "0").toInteger() + 1
                versionProps['VERSION_CODE'] = code.toString()
                versionProps.store(versionPropsFile.newWriter(), null)
                
                defaultConfig {
                    versionCode code
                    versionName "1.1"
                    minSdkVersion 14
                    targetSdkVersion 18
                }
                

                【讨论】:

                • 如果版本文件不存在,创建版本文件的部分在哪里?
                • if(versionPropsFile.exists()) 确保文件不存在时不会爆炸。 versionProps.store(versionPropsFile.newWriter(), null) 覆盖文件,无论它是否已经存在。
                • 必须检查 ?: 在 Groovy 中的含义。 Elvis operator 是三元运算符的缩写。
                猜你喜欢
                • 1970-01-01
                • 2015-02-23
                • 1970-01-01
                • 1970-01-01
                • 2012-08-31
                • 1970-01-01
                • 1970-01-01
                • 2014-06-24
                • 1970-01-01
                相关资源
                最近更新 更多