Android应用中的自动增量版本代码

时间:2011-07-20 07:51:24

标签: android eclipse build version

有没有办法在每次在Eclipse中构建Android应用程序时自动增加版本代码?

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

据我所知,你必须在每次构建之前运行一个脚本,例如解析AndroidManifest.xml文件,找到版本号,增加它并在构建本身启动之前保存文件。但是,我无法了解Eclipse是否支持在构建之前/之后运行脚本。

我找到this article关于配置ant builder的问题,但这并不完全与Android有关,我担心这会给Android的预定义构建步骤带来太多麻烦?

应该是一个常见的问题,你是如何解决的?

好吧,人们可以手动执行此操作,但只要您忘记执行此项工作,就会获得具有相同编号的不同版本,并且整个版本控制毫无意义。

15 个答案:

答案 0 :(得分:112)

我完成了这个。以下是我为下一个人(使用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文件(我名为mine:AndroidAutoIncrementVersionCode.cs
  3. 打开命令提示符并导航到您创建*.cs文件
  4. 的位置
  5. 使用此命令构建文件(在Windows上,类似于Mono但更改编译器的路径): c:\Windows\Microsoft.NET\Framework\v2.0.50727\csc AndroidAutoIncrementVersionCode.cs (有关详细信息,请参阅:.NETMono
  6. 恭喜,您刚刚构建了一个没有任何工具的C#应用​​程序,它应该在同一目录中自动生成AndroidAutoIncrementVersionCode.exe

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

  7. 结束

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

    go to project properties

    b)在属性中,转到“Builders” - &gt; “新建...”

    Eclipse properties screen

    c)选择“程序”

    choose program

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

    edit configuration - main

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

    edit configuration - refresh

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

    edit configuration - build options edit working set

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

    edit configuration - hit ok enter image description here

答案 1 :(得分:9)

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

将此脚本保存在名为incVersion的文件中,与AndroidManifest.xml位于同一目录中,使其可执行: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修订版相关。在调查最新版本以外的错误时非常有用。

答案 2 :(得分:7)

FWIW,我能够在六行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() )

答案 3 :(得分:6)

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)

答案 4 :(得分:4)

所以,我认为是这样的:

根据您提供的文章,使用 ant 执行此任务(目标?)。

  1. 解析清单(解析XML)
  2. 获取旧版本表单清单并从repo增加/获取版本
  3. 在清单中存储新版本
  4. 构建Android应用程序。
  5. 但在我的情况下,我通常在部署或分发应用程序时根据 Tag 的修订版按值填写此字段。

答案 5 :(得分:3)

<强> Receipe:

要自动将AndroidManifest.xml中的manifest元素的android:versionCode属性设置为每次运行构建时的当前时间(从以秒为单位的epoch,从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>

确认测试

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

$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. 如果需要,可以将versionCode反向设计为大致构建时间
  5. 上面的脚本替换了versionCode的任何现有值,甚至是0,并且不需要宏占位符(例如-build_id-)。
  6. 因为AndroidManifest.xml文件中的值已更新,您可以将其检入版本控制,它将保留实际值,而不是某些宏(例如-build_id - )。

答案 6 :(得分:2)

构建于Rocky's answer我增强了那个python脚本以增加了versionCode,在Eclipse上工作(按照ckozl great tutorial集成)&amp; 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指出

答案 7 :(得分:1)

我做过类似的事情,但把它写成桌面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>

答案 8 :(得分:1)

我能够从给出的信息中找出自己的解决方案。如果它对某人有用,这里是我的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”命令的输出组合,如果不是“clean”构建,则替换为构建它的分支。

通过“干净”构建,我的意思是所有组件都受版本控制,并且它们的最新提交被标记。 “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__" >

希望这对某人有用

答案 9 :(得分:1)

所有功劳都归功于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);
                        }
                    }
                }
            }
        }
    }
}

答案 10 :(得分:1)

对于那些在OSX上并且想要使用Python的人,但是没有松开XML格式,当解析由python 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上面描述的步骤!

答案 11 :(得分:0)

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

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

AndroidManifestVersioner <path> <versionCode> <versionName>.

感谢其他海报的代码。

答案 12 :(得分:0)

这是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);
        }
    }
}

答案 13 :(得分:0)

如果您正在使用gradle,那么您可以在versionName中非常轻松地具体versionCodebuild.gradle。您可以使用git commit count作为增加的数字来识别构建。

您也可以使用此库:https://github.com/rockerhieu/Versionberg

答案 14 :(得分:0)

我真的很喜欢两种解决方案。第一个取决于Play商店,另一个取决于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 {
    ...
}