【问题标题】:Auto increment version code in Android appAndroid 应用中的自动递增版本代码
【发布时间】:2011-10-09 04:15:01
【问题描述】:

有没有一种方法可以在您每次在 Eclipse 中构建 Android 应用程序时自动增加版本代码?

根据http://developer.android.com/guide/publishing/versioning.html,您必须手动增加AndroidManifest.xml中的版本代码。

我了解,您必须在每次构建之前运行一个脚本,例如解析 AndroidManifest.xml 文件,找到版本号,将其递增并在构建本身开始之前保存文件。但是,我不知道 Eclipse 如何以及是否支持在构建之前/之后运行脚本。

我找到了 this article 关于配置 ant builder 的信息,但这并不完全是关于 Android 的,我担心这会扰乱 Android 的预定义构建步骤?

应该是通病,你是怎么解决的?

嗯,可以手动执行此操作,但是一旦您忘记执行此杂务,您就会得到具有相同编号的不同版本,并且整个版本控制毫无意义。

【问题讨论】:

  • 只是想,可以使用版本控制系统中的提交挂钩来增加清单中的版本代码,甚至只是用修订号替换版本代码。这也可以接受
  • 看起来使用 SVN/CVS 修订号是唯一的方法。因为如果脚本只是增加清单中的数字,不同开发人员完成的构建将具有任意增加的、不同步的数字。修订号是全球性的。
  • 您可以使用 ant 中的 custom_rules.xml 来实现。请参阅stackoverflow.com/a/20584169/1523910 在 git 中进行操作

标签: android eclipse build version


【解决方案1】:

我做到了。下面是我为下一个人做的事情(使用 Eclipse):

1) 创建一个外部控制台可执行文件,它将向 AndroidManifest.xml 写入新版本代码:(我的是 C#)

using System.IO;
using System.Text.RegularExpressions;

namespace AndroidAutoIncrementVersionCode
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string FILE = @"AndroidManifest.xml";
                string text = File.ReadAllText(FILE);
                Regex regex = new Regex(@"(?<A>android:versionCode="")(?<VER>\d+)(?<B>"")", RegexOptions.IgnoreCase);
                Match match = regex.Match(text);
                int verCode = int.Parse(match.Groups["VER"].Value) + 1;
                string newText = regex.Replace(text, "${A}" + verCode + "${B}", 1);

                File.WriteAllText(FILE, newText);
            }
            catch { }
        }
    }
}

除此之外:任何 c-sharp 编译器都可以构建这个应用程序,您不需要 Visual Studio 甚至 Windows

  1. 如果您还没有,请安装 .NET 运行时 (Mono will work, link) (link to MS's .NET framework 2.0, 2.0 is the smallest download, any version >= 2.0 is fine)
  2. 将此代码复制到*.cs 文件(我的命名为:AndroidAutoIncrementVersionCode.cs
  3. 打开命令提示符并导航到您创建 *.cs 文件的位置
  4. 使用此命令构建文件(在 Windows 上,与 Mono 类似,但更改编译器路径): c:\Windows\Microsoft.NET\Framework\v2.0.50727\csc AndroidAutoIncrementVersionCode.cs (请参阅:.NETMono 了解更多信息)
  5. 恭喜,你刚刚构建了一个没有任何工具的 C# 应用程序,它应该在同一目录下自动生成 AndroidAutoIncrementVersionCode.exe

    **里程可能不同,路径可能不同,不需要购买,在禁止的地方无效,我添加这个是因为 C# 很棒,而且人们错误地认为它有 MS 锁定,你可以很容易地把它翻译成另一个语言(但我不会为你这样做;)。顺便说一句,任何 .NET 编译器的任何版本都可以工作,我将代码调整为最小公分母...*

结束

2) 在构建过程中运行可执行文件: a) 转到项目属性

b) 在属性中,转到“Builders” -> “New...”

c) 选择“程序”

d) 在“主”选项卡中选择程序位置(我还将工作目录设置为安全的)并根据需要为其命名。

e) 在“刷新”选项卡中选择“完成后刷新资源”和“选定资源”选项 - 这将在我们编写清单后刷新它。

f) 在“构建选项”选项卡中,您可以关闭“分配控制台”,因为您没有输入和输出,然后仅选择“在手动构建期间”和“在自动构建期间”,如果它取消选择“清理后”被检查。然后选择“指定相关资源的工作集”并单击“指定资源...”按钮。在“编辑工作集”对话框中,在对话框中找到您的“AndroidManifest.xml”文件并检查它,然后点击“完成”

f) 现在在“编辑配置对话框”和应用程序的属性中点击“确定”,选择新创建的构建器,然后继续点击“向上”直到它位于列表顶部,这样自动增量首先运行,并且不会触发意外的不同步状态或重建。一旦您创建的新构建器位于列表顶部,单击“确定”即可完成。

【讨论】:

  • 很好的答案!对我很有用。我对其进行了增强以增加“android:versionName”,并为不太熟悉 C# 的人上传了 executable itself
  • 注意版本名必须是X.Y.Z的形式,Z每次递增。将 exe 放在与清单相同的位置。
  • 谢谢,我可以在我的 Linux 机器上使用 mono
  • 像@thedrs 一样,我调整了这个例子来更新 versionName 值(以 X.Y.Z 的形式)。来源:gist.github.com/3131675
  • 这是一个很好的答案,但是我在 Eclipse 4.2.2 上遇到了问题。看起来每次更改 AndroidManifest.xml 时,都会重新构建项目。由于自动增量程序会导致不定式循环...我通过取消选中“在自动构建期间”来解决它....
【解决方案2】:

这个适用于 *nix 系统的 shell 脚本将 versionCode 和 versionName 的最后一个组件设置为当前的 subversion 版本。我将 Netbeans 与 NBAndroid 一起使用,并从 custom_rules.xml 中的目标 -pre-compile 调用此脚本。

将此脚本保存在与 AndroidManifest.xml 相同的目录中名为 incVersion 的文件中,使其可执行:chmod +x incVersion

manf=AndroidManifest.xml
newverfull=`svnversion`
newvers=`echo $newverfull | sed 's/[^0-9].*$//'`
vers=`sed -n '/versionCode=/s/.*"\([0-9][0-9]*\)".*/\1/p' $manf`
vername=`sed -n '/versionName=/s/.*"\([^"]*\)".*/\1/p' $manf`
verbase=`echo $vername | sed 's/\(.*\.\)\([0-9][0-9]*\).*$/\1/'`
newvername=$verbase$newverfull
sed /versionCode=/s/'"'$vers'"'/'"'$newvers'"'/ $manf | sed /versionName=/s/'"'$vername'"'/'"'$newvername'"'/  >new$manf && cp new$manf $manf && rm -f new$manf
echo versionCode=$newvers versionName=$newvername

创建或编辑 custom_rules.xml 并添加:

<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules">
    <xmlproperty file="AndroidManifest.xml" prefix="mymanifest" collapseAttributes="true"/>
    <target name="-pre-compile">
        <exec executable="./incVersion" failonerror="true"/>
    </target>
</project>

所以如果我当前的 svn 版本是 82,我最终会在 AndroidManifest.xml 中得到这个:

android:versionCode="82"
android:versionName="2.1.82">

当我想发布一个新版本时,我通常会更新 versionName 的第一部分,但即使我忘记了,versionName 的最后一部分(在我的 About 活动中公开)总是会告诉我它是什么 svn 修订版建于。另外,如果我没有签入更改,修订号将为 82M,versionName 将类似于 2.1.82M。

与每次构建完成时简单地增加版本号相比,优势在于版本号处于可控范围内,并且可以直接与特定的 svn 修订版相关。在调查最新版本以外的错误时非常有用。

【讨论】:

    【解决方案3】:

    FWIW,我能够在 6 行 python 中更新构建版本值:

    #!/bin/env python
    import os
    from xml.dom.minidom import parse
    dom1 = parse("AndroidManifest.xml")
    dom1.documentElement.setAttribute("android:versionName","%build.number%")
    f = os.open("AndroidManifest.xml", os.O_RDWR)
    os.write( f, dom1.toxml() )
    

    【讨论】:

    • 谢谢!对我来说,它比十几个屏幕截图更不言自明,更容易理解。
    • ... 但没有说明如何使其在 Eclipse 中工作,这是问题所在。
    • 我也想知道它是如何运行的。
    【解决方案4】:

    Charles' answer 为基础,增加现有的构建版本:

    #!/usr/bin/python
    from xml.dom.minidom import parse
    
    dom1 = parse("AndroidManifest.xml")
    oldVersion = dom1.documentElement.getAttribute("android:versionName")
    versionNumbers = oldVersion.split('.')
    
    versionNumbers[-1] = unicode(int(versionNumbers[-1]) + 1)
    dom1.documentElement.setAttribute("android:versionName", u'.'.join(versionNumbers))
    
    with open("AndroidManifest.xml", 'wb') as f:
        for line in dom1.toxml("utf-8"):
            f.write(line)
    

    【讨论】:

    • 这太好了,非常感谢!如果作为 shell 脚本运行,不要忘记在第一行添加 #!/usr/bin/python 并使其可执行:chmod +x buildincrease.py
    【解决方案5】:

    所以,我是这样看的:

    根据您展示的文章,将 ant 用于此任务(目标?)。

    1. 解析清单(解析 XML)
    2. 获取旧版本表单清单并增加它/从 repo 获取版本
    3. 在清单中存储新版本
    4. 构建安卓应用。

    但在我的情况下,我通常在部署或分发应用程序时根据 Tag 的修订按值填充此字段。

    【讨论】:

    • 是的,这些是脚本应该的步骤。不幸的是,我找不到脚本调用应该在哪里。从 Eclipse 构建 Android 应用程序不使用 build.xml 文件。所以,我的问题归结为:在哪里(在什么文件中?在什么 Eclipse 项目设置中?)放置脚本的调用
    • 实际上,如果使用 CVS,这样的脚本不会自动进行版本控制。 CVS 不支持项目范围的修订编号,这就是您在 Tag 上使用斜体的原因。但是您首先必须手动创建一个 CVS 标记。这意味着,如果您忘记创建 CVS 标签,您最终会得到不正确的内部版本号。这与手动更新 AndroidManifest.xml 中的 versionCode 基本相同,唯一的优点是内部版本号也与其他开发人员可见的标签同步(好吧,无论如何它都在代码中)。如果使用 CVS,似乎无法实现真正​​的自动化。
    • 对不起,我不直接使用 ant 来构建 android 应用程序。但是我看到的有很多代码。 Look at this link.
    • 你对 CVS 的看法是正确的。 Similar question
    【解决方案6】:

    收据:

    要在每次运行构建时自动将 AndroidManifest.xml 中 manifest 元素的 android:versionCode 属性设置为当前时间(以秒为单位,从 unix shell 获取),请将其添加到您的 -pre-build 目标中custom_rules.xml Android 文件。

    <target name="-pre-build">
      <exec executable="date" outputproperty="CURRENT_TIMESTAMP">
        <arg value="+%s"/>
      </exec>
      <replaceregex file="AndroidMainfest.xml" match="android:versionCode=.*"
        replace='android:versionCode="${CURRENT_TIMESTAMP}"' />
    </target>
    

    确认测试

    获取生成的apk文件的versionCode属性,在你的Android项目目录中使用以下shell命令:

    $ANDROID_SDK/build-tools/20.0.0/aapt dump badging bin/<YourProjectName>.apk | grep versionCode
    

    并将其与从 shell 命令返回的当前日期进行比较:date +%s 差值应等于上述两个确认步骤之间的时间间隔(以秒为单位)。

    这种方法的优点:

    1. 无论是从命令行还是 Eclipse 启动构建,都会更新 versionCode。
    2. 版本代码保证是唯一的,并且每次构建都会增加
    3. 如果需要,可以将 versionCode 反向工程为大致的构建时间
    4. 上述脚本替换了任何版本代码的当前值,甚至是 0,并且不需要宏占位符(例如 -build_id-)。
    5. 由于值在AndroidManifest.xml文件中更新,您可以将其签入到版本控制中,它将保留实际值,而不是某些宏(例如-build_id-)。

    【讨论】:

      【解决方案7】:

      Rocky's answer 的基础上,我稍微增强了python 脚本以增加版本代码,适用于我在Eclipse(根据ckozl great tutorial 集成)和Mac OSX 上

      #!/usr/bin/python
      from xml.dom.minidom import parse
      
      dom1 = parse("AndroidManifest.xml")
      oldVersion = dom1.documentElement.getAttribute("android:versionName")
      oldVersionCode = dom1.documentElement.getAttribute("android:versionCode")
      versionNumbers = oldVersion.split('.')
      
      versionNumbers[-1] = unicode(int(versionNumbers[-1]) + 1)
      dom1.documentElement.setAttribute("android:versionName", u'.'.join(versionNumbers))
      dom1.documentElement.setAttribute("android:versionCode", str(int(oldVersionCode)+1))
      with open("AndroidManifest.xml", 'wb') as f:
          for line in dom1.toxml("utf-8"):
              f.write(line)
      

      也不要忘记chmod +x autoincrement.py 并确保您在第一行有正确的python 路径(取决于您的环境),正如sulai 指出的那样

      【讨论】:

        【解决方案8】:

        我做过类似的事情,但将其编写为桌面 AIR 应用程序,而不是一些外部 C#(不觉得安装另一个构建系统)。构建此 Flex/ActionScript 应用程序并更改文件的路径,将其构建为独立的桌面应用程序。它会重写文件的 1.2.3 部分。

            <?xml version="1.0" encoding="utf-8"?>
        <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                               xmlns:s="library://ns.adobe.com/flex/spark"
                               xmlns:mx="library://ns.adobe.com/flex/mx"
                               width="371" height="255" applicationComplete="Init();">
            <fx:Declarations>
                <!-- Place non-visual elements (e.g., services, value objects) here -->
            </fx:Declarations>
        
            <fx:Script>
                <![CDATA[
        
                    public function Init():void
                    {
                        import flash.filesystem.File;
                        import flash.filesystem.FileMode;
                        import flash.filesystem.FileStream;
        
                        var myFile:File = new File("D:\\Dropbox\\Projects\\My App\\src\\Main-app.xml");
        
                        var fileStream:FileStream = new FileStream();
                        fileStream.open(myFile, FileMode.READ);
        
                        var fileContents:String = fileStream.readUTFBytes(fileStream.bytesAvailable);
        
                        var startIndex:Number = fileContents.indexOf("<versionNumber>");
                        var numberIndex:Number = startIndex + 15;
                        var endIndex:Number = fileContents.indexOf("</versionNumber>");
        
                        if (startIndex == -1 || endIndex == -1)
                            return;
        
                        var versionNumber:String = fileContents.substr(numberIndex, endIndex - numberIndex);
                        var versionArr:Array = versionNumber.split(".");
                        var newSub:Number = Number(versionArr[2]);
                        newSub++;
                        versionArr[2] = newSub.toString();
                        versionNumber = versionArr.join(".");
        
                        var newContents:String = fileContents.substr(0, startIndex) + "<versionNumber>" + versionNumber + "</versionNumber>" +
                                        fileContents.substr(endIndex + 16);
                        fileStream.close(); 
        
        
                        fileStream = new FileStream();
                        fileStream.open(myFile, FileMode.WRITE);
                        fileStream.writeUTFBytes(newContents);
                        fileStream.close(); 
        
                        close();
                    }
                ]]>
            </fx:Script>
            <s:Label x="10" y="116" width="351" height="20" fontSize="17"
                     text="Updating My App Version Number" textAlign="center"/>
        
        </s:WindowedApplication>
        

        【讨论】:

          【解决方案9】:

          我能够根据所提供的信息制定出自己的解决方案。如果它对某人有用,这里是我的 bash 脚本,用于在 Linux 上使用 GIT VCS 时更新 versionCode 和 versionName 属性。

          我编辑 AndroidManifest.xml 文件的脚本如下所示:

          #/bin/bash
          
          CODE=`git tag | grep -c ^v`
          NAME=`git describe --dirty`
          COMMITS=`echo ${NAME} | sed -e 's/v[0-9\.]*//'`
          
          if [ "x${COMMITS}x" = "xx" ] ; then
              VERSION="${NAME}"
          else
              BRANCH=" (`git branch | grep "^\*" | sed -e 's/^..//'`)"
              VERSION="${NAME}${BRANCH}"
          fi
          
          cat AndroidManifest.template.xml \\
              | sed -e "s/__CODE__/${CODE}/" \\
                    -e   "s/__VERSION__/${VERSION}/" > AndroidManifest.xml
          
          exit 0
          

          它解析模板文件(AndroidManifest.template.xml)并用更合适的值替换字符串“__VERSION__”和“__CODE__”:

          • "__CODE__" 被替换为 Git 存储库中的标签数,它以单个小写 V 开头,后跟一系列数字和点。这看起来像大多数版本字符串,例如:“v0.5”、“v1.1.4”等等。
          • “__VERSION__”替换为“git describe”命令的输出组合,如果不是“干净”构建,则替换为构建它的分支。

          我所说的“干净”构建是指所有组件都在版本控制之下,并且它们的最新提交被标记。 “git describe --dirty”将根据您在当前分支的最新提交中的最后一个可访问的注释标签报告版本号。如果自该标记以来有提交,则将报告这些提交的计数作为您上次提交的缩写对象名称。如果修改了版本控制下的任何文件,则“--dirty”选项将在上述信息后附加“-dirty”。

          因此 AndroidManifest.xml 不应再受版本控制,您应该只编辑 AndroidManifest.template.xml 文件。您的 AndroidManifest.template.xml 文件的开头如下所示:

          <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="com.example.packagename"
              android:versionCode="__CODE__"
              android:versionName="__VERSION__" >
          

          希望这对某人有用

          【讨论】:

          • 我在 windows 上使用 eclipse juno 进行 android 开发。我已将上面共享的脚本复制到我的项目文件夹中的“.bat”文件中,并按照上述步骤创建了一个构建器。还使用 android:versionCode="CODE" android:versionName="VERSION" 创建了一个 AndroidManifest.template.xml 我更改了清单中的一些值并运行应用程序,但是我没有看到清单中的版本号/版本代码有任何变化;请帮助我理解问题:(另外,我使用 Sourcetree 进行 GIT 操作。
          【解决方案10】:

          所有功劳归于 ckoz,但我用 C# 编写了自己的实现。我认为它更快一点并且不会出错,因为如果出现问题,可能是配置错误,我应该知道。

          namespace AndroidVersionCodeAutoIncrement
          {
              using System.IO;
              using System.Text.RegularExpressions;
          
              public class Program
              {
                  private static readonly Regex VersionCodeRegex = new Regex("android:versionCode=\"(?<version>.*)\"", RegexOptions.Compiled);
          
                  public static void Main()
                  {
                      using (var manifestFileStream = File.Open("AndroidManifest.xml", FileMode.Open, FileAccess.ReadWrite))
                      using (var streamReader = new StreamReader(manifestFileStream))
                      {
                          var manifestFileText = streamReader.ReadToEnd();
          
                          var firstMatch = VersionCodeRegex.Match(manifestFileText);
                          if (firstMatch.Success)
                          {
                              int versionCode;
                              var versionCodeValue = firstMatch.Groups["version"].Value;
                              if (int.TryParse(versionCodeValue, out versionCode))
                              {
                                  manifestFileText = VersionCodeRegex.Replace(manifestFileText, "android:versionCode=\"" + (versionCode + 1) + "\"", 1);
          
                                  using (var streamWriter = new StreamWriter(manifestFileStream))
                                  {
                                      manifestFileStream.Seek(0, SeekOrigin.Begin);
                                      streamWriter.Write(manifestFileText);
                                      manifestFileStream.SetLength(manifestFileText.Length);
                                  }
                              }
                          }
                      }
                  }
              }
          }
          

          【讨论】:

            【解决方案11】:

            对于那些在 OSX 上并且想要使用 Python,但不松散由 Python XML 解析器完成解析时发生的 XML 格式,这里是一个 Python 脚本,它将基于正则表达式进行增量,它保持格式:

            #!/usr/bin/python
            import re
            
            f = open('AndroidManifest.xml', 'r+')
            text = f.read()
            
            result = re.search(r'(?P<groupA>android:versionName=")(?P<version>.*)(?P<groupB>")',text)
            version = str(float(result.group("version")) + 0.01)
            newVersionString = result.group("groupA") + version + result.group("groupB")
            newText = re.sub(r'android:versionName=".*"', newVersionString, text);
            f.seek(0)
            f.write(newText)
            f.truncate()
            f.close()
            

            代码基于@ckozl 答案,只是在 python 中完成,因此您无需为此创建可执行文件。 只需将脚本命名为 autoincrement.py,将其与 manifest.xml 文件放在同一文件夹中,然后执行 ckozl 上面描述的步骤!

            【讨论】:

              【解决方案12】:

              如果您想更新 AndroidManifest.xml 以使用特定版本号,可能来自构建系统,那么您可以使用我刚刚推送到 GitHub 的项目:https://github.com/bluebirdtech/AndroidManifestVersioner

              这是一个基本的 .NET 命令行应用程序,用法:

              AndroidManifestVersioner <path> <versionCode> <versionName>.
              

              感谢其他发帖者的代码。

              【讨论】:

                【解决方案13】:

                这是物有所值的 Java 版本。还处理多个清单。

                String directory = "d:\\Android\\workspace\\";
                
                String[] manifests = new String[] 
                {
                        "app-free\\AndroidManifest.xml",
                        "app-donate\\AndroidManifest.xml",
                };
                
                public static void main(String[] args)
                {
                    new version_code().run();
                }
                
                public void run()
                {
                    int I = manifests.length;
                    for(int i = 0; i < I; i++)
                    {
                        String path = directory + manifests[i];
                
                        String content = readFile(path);
                        Pattern         versionPattern = Pattern.compile( "(.*android:versionCode=\")([0-9]+)(\".*)", Pattern.DOTALL );
                        Matcher m = versionPattern.matcher(content);
                
                        if (m.matches())
                        {
                            int code = Integer.parseInt( m.group(2) ) + 1;
                
                            System.out.println("Updating manifest " + path + " with versionCode=" + code);
                
                            String newContent = m.replaceFirst("$1" + code + "$3");
                
                            writeFile(path + ".original.txt", content);
                            writeFile(path, newContent);
                        }
                        else
                        {
                            System.out.println("No match to update manifest " + path);
                        }
                    }
                }
                

                【讨论】:

                  【解决方案14】:

                  如果您使用的是 gradle,那么您可以在 build.gradle 中非常容易地指定 versionNameversionCode。您可以使用 git commit count 作为递增的数字来识别构建。

                  你也可以使用这个库:https://github.com/rockerhieu/Versionberg

                  【讨论】:

                    【解决方案15】:

                    我非常喜欢两种解决方案。第一个依赖于 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 {
                        ...
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 2015-02-23
                      • 2013-05-01
                      • 1970-01-01
                      • 2011-06-23
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-08-27
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多