繁体   English   中英

Android 应用程序中的自动递增版本代码

[英]Auto increment version code in Android app

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

According to http://developer.android.com/guide/publishing/versioning.html , you have to manually increment your version code in AndroidManifest.xml .

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

我发现这篇关于配置 ant 构建器的文章,但这并不完全是关于 Android,我担心这会弄乱 Android 的预定义构建步骤太多?

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

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

我做到了这一点。 下面是我为下一个人做的事情(使用 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 可以工作,链接)(链接到 MS 的 .NET 框架 2.0,2.0 是最小的版本=任何下载,2.0 是最好的)。
  2. 将此代码复制到*.cs文件(我命名为: AndroidAutoIncrementVersionCode.cs
  3. 打开命令提示符并导航到创建*.cs文件的位置
  4. build the file using this command (on Windows, similar for Mono but change path to compiler): c:\Windows\Microsoft.NET\Framework\v2.0.50727\csc AndroidAutoIncrementVersionCode.cs (see: .NET or Mono for more info)
  5. 恭喜,您刚刚构建了一个 C# 应用程序,没有任何工具,它应该在同一目录下自动生成AndroidAutoIncrementVersionCode.exe

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

放在一边

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

转到项目属性

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

Eclipse 属性屏幕

c) 选择“程序”

选择程序

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

编辑配置 - 主要

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

编辑配置 - 刷新

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

编辑配置 - 构建选项编辑工作集

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

编辑配置 - 点击确定在此处输入图像描述

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

将此脚本保存在与 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 修订版直接相关。 在调查除最新版本以外的错误时非常有用。

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() )

基于Charles 的回答,以下内容增加了现有的构建版本:

#!/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)

所以,我是这样看的:

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

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

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

收据:

To automatically have the android:versionCode attribute of manifest element in AndroidManifest.xml set to the current time (from epoch in seconds, obtained from unix shell) everytime you run a build, add this to your -pre-build target in 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. versionCode 保证是唯一的,并且每次构建都会增加
  3. 如果需要,可以将 versionCode 逆向工程为大致的构建时间
  4. 上面的脚本替换了 versionCode 的任何当前值,甚至是 0,并且不需要宏占位符(例如 -build_id-)。
  5. 因为值在 AndroidManifest.xml 文件中更新,您可以将其签入版本控制,它将保留实际值,而不是某些宏(例如 -build_id-)。

基于Rocky 的回答,我增强了 python 脚本以增加版本代码,适用于 Eclipse(根据ckozl 伟大教程集成)和 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指出的那样

我做了类似的事情,但将其编写为桌面 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>

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

我所说的“干净”构建是指所有组件都处于版本控制之下,并且它们的最新提交被标记。 “git describe --dirty”将根据您在当前分支的最新提交中的最后一个可访问的注释标签报告版本号。 如果自该标记以来有提交,则会报告这些提交的计数,以及您上次提交的缩写 object 名称。 如果修改了版本控制下的任何文件,则“--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__" >

希望这对某人有用

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

For those that are on OSX and want to use Python, but not loose the XML formatting which when parsing is done by the python XML parser happens, here is a python script that will do the incremental based on regular expression, which keeps the formatting:

#!/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 上面描述的步骤!

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

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

AndroidManifestVersioner <path> <versionCode> <versionName>.

感谢其他海报的代码。

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

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

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

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

使用 Play 商店,您可以通过查看可用的最高上传版本代码来增加版本代码。 此解决方案的好处是,APK 上传永远不会失败,因为您的版本代码始终比 Play 商店中的版本代码高一个。 缺点是在 Play 商店之外分发您的 APK 变得更加困难。 您可以按照快速入门指南并告诉插件自动解析版本代码,使用 Gradle Play Publisher 进行设置:

plugins {
    id 'com.android.application'
    id 'com.github.triplet.play' version 'x.x.x'
}

android {
    ...
}

play {
    serviceAccountCredentials = file("your-credentials.json")
    resolutionStrategy = "auto"
}

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

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

android {
    ...
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM