Android Gradle实用技巧(二) | 自动生成版本信息

Android Gradle必备使用技巧,未完待续,欢迎关注公众号flysnow_org,第一时间看后续技巧

每一个App都会有一个版本号,这样用户就知道自己安装的应用是哪个版本,是不是最新版,有了问题,也可以找客服报上自己的版本,让客服有针对性的帮用户解决问题。

一般的版本有三部分构成:major.minor.patch,第一个是主版本号,第二个是副版本号,第三位补丁号,这种我们常见的见识1.0.0这样的,当然也有两位的1.0,对应major.minor,这里我们以三位为例。

原始的版本信息配置方式

最开始的时候我们都是配置在build文件里的,如下:

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"


    defaultConfig {
        applicationId "org.flysnow.app"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 1
        versionName "1.0.0"
    }
}

这种方式我们直接写在versionName的后面,比较直观。但是这种方式有个很大的问题就是修改不方便,特别当我们的build文件中有很多代码时,不容易找,而且修改容易出错,代码版本管理时也容易产生冲突。

使用Gradle模块化的机制剥离版本信息

既然最原始的方式,修改不方便,那么我们可不可以把版本号的配置单独的抽取出来的,放在单独的文件里,供build引用,就像我们在Android里,单独新建一个存放常量的Java类一样,供其他类调用,幸运的是,android是支持基于文件的模块化的,它就是apply from。

这和应用一个Gradle是一样的,我们不光可以应用一个插件,也可以把另一个gradle文件引用进来。我们新建一个version.gradle文件,用于专门存放我们的版本。

version.gradle

ext {
    appVersionCode =1
    appVersionName = "1.0.0"
}

ext{}块表明我们要为当前project创建扩展属性,以供其他脚本引用,他就像我们java里的变量一样。创建好之后,我们在build.gradle中引用它。

build.gradle

apply from: 'version.gradle'
android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"


    defaultConfig {
        applicationId "org.flysnow.app"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode appVersionCode
        versionName appVersionName
    }
}

从示例中可以看到,我们先使用apply from加载我们的version.gradle脚本文件,这样它里面定义的扩展属性就可以使用了。然后我们为versionCodeversionName配置我们定义好的属性变量。

这种方式,我们每次只用修改version.gradle里的版本号就好了,方便,容易,也比较清晰,在团队协作的过程中,大家看到这个文件,就能猜测出来它大概是做什么的,而且只会负责发版的人才会修改这样文件,代码冲突少。

从git的tag中获取版本号

一般Jenkins打包发布的时候,我们都会从我们已经打好的一个tag打包发布,而tag的名字一般就是我们的版本名称,这时候我们就可以动态的获取我们的tag名称作为我们应用的名称,可能你用的不是git版本控制系统,但是大同小异,这里以git为例。

想获取当前的tag名称,在git下非常简单,使用如下命令即可,前提是你已经打了tag。

git describe --abbrev=0 --tags

知道了命令,那么我们如何在gradle中动态获取呢,这就需要gradle的exec了,gradle为我们提供了执行shell命令非常简便的方法,这就是Exec,它是一个Task任务,我们可以创建一个继承Exec的任务来执行我们的shell命令,但是比较麻烦,还好Gradle已经为我们想到了这个问题,为我们在Project对象里提供了exec方法。

ExecResult exec(Closure closure);

ExecResult exec(Action<? super ExecSpec> action);

其参数接受闭包和Action两种方式,一般我们都是采用闭包的方式,其闭包的配置是通过ExecSpec对象来配置的,我们从源代码的文档中也可以看到说明。

public interface ExecSpec extends BaseExecSpec {

    void setCommandLine(Object... args);

    void setCommandLine(Iterable<?> args);

    ExecSpec commandLine(Object... args);

    ExecSpec commandLine(Iterable<?> args);

    ExecSpec args(Object... args);

    ExecSpec args(Iterable<?> args);

    ExecSpec setArgs(Iterable<?> args);

    List<String> getArgs();
}

从ExecSpec源代码中我们可以看出,Project的exec方法的闭包可以有commandLine属性、commandLine方法、args属性以及args方法等配置供我们使用,我们这里只需要commandLine方法就可以达到目的了。

/**
 * 从git tag中获取应用的版本名称
 * @return git tag的名称
 */
def getAppVersionName(){
    def stdout = new ByteArrayOutputStream()
    exec {
        commandLine 'git','describe','--abbrev=0','--tags'
        standardOutput = stdout
    }
    return stdout.toString()
}

以上示例定义了一个getAppVersionName方法来获取我们的tag名称,exec执行后的输出可以用standardOutput获得,它是BaseExecSpec的一个属性,ExecSpec继承了BaseExecSpec,所以我们可以在exec{}闭包中使用。

通过该方法我们获取了git tag的名称后,就可以把它作为我们应用的版本名称了,使用非常简单,只用把我们的versionName配置成这个方法就好了,刚刚我们演示的时候是一个名为appVersionName的扩展属性。

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "org.flysnow.app"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode appVersionCode
        versionName getAppVersionName()
    }
}

以上我们通过git tag动态获取了版本名称,那么版本号我们如何动态获取呢?版本号作为我们内部开发的标识,主要用于控制应用进行生成,一般它是+1递增的,每一次发版,其值就+1,而每一次发版我们就会打一个tag,tag的数量也会增加1个,和我们版本号的递增逻辑是符合的,那么我们是不是可以把git tag的数量作为我们的版本号呢?答案是肯定的,这样打包发版之前,我们只需打个tag,tag数量+1,版本号也会跟着+1,达到了我们的目的。

/**
 * 以git tag的数量作为其版本号
 * @return tag的数量
 */
def getAppVersionCode(){
    def stdout = new ByteArrayOutputStream()
    exec {
        commandLine 'git','tag','--list'
        standardOutput = stdout
    }
    return stdout.toString().split("\n").size()
}

以上示例我们定义一个getAppVersionCode方法来获取git tag的数量,用于我们的版本号,然后我们在defaultConfig里使用这个方法即可,替换掉我们的appVersionCode变量。

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"


    defaultConfig {
        applicationId "org.flysnow.app"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode getAppVersionCode()
        versionName getAppVersionName()
    }
}

大功告成,这样我们在发版打包之前,只需要打一个tag,然后Android Gradle打包的时候就会自动帮我们生成应用的版本名称和版本号,非常方便,再也不用为维护应用的版本信息担心了,这也是我们使用Gradle构建的灵活之处,如果使用Ant,会麻烦的多,有兴趣的同学可以思考一下。

从属性文件中动态获取和递增版本信息

其实上一小结已经可以满足我们大部分的情况了,如果大家不想用,或者想自己更灵活的控制版本信息,可以采用Properties属性文件的方式,这里我不给出示例代码了,仅给出思路,以供参考。

大致思路如下:

  1. 在项目目录下新建一个version.properties的属性文件。
  2. 把版本名称分成三部分major.minor.patch,版本号分成一部分number,然后在version.properties中新增四个KV键值对,其key就是我们上面分好的major,minor,patch以及number,value是对应的值。
  3. 然后在build.gradle里新建两个方法,用于读取该属性文件,获取对应Key的值,然后把major.minor.patch这三个key拼接成版本名称,number用于版本号。
  4. 以上就达到了获取版本信息的目的,获取使用之后,我们还要更新我们存放在version.properties文件中的信息,这样就可以达到版本自增的目的,以供下次使用。
  5. 在更新版本名称三部分的时候,你可以自定义自己的逻辑,是逢10高位+1呢,还是其他算法,都可以自己灵活定义。
  6. 使用版本信息,更新version.properties文件的时机,记得doLast这个方法。
  7. 记得不会在自己运行调试的时候让你的版本信息自增哦,如何控制呢?就是要区分是真正的打包发版,还是平时的调试、测试,有很多办法来区分的。

动态获取生成版本信息的思路都大同小异,只是信息来源不一样,比如git tag,比如version配置等等,你自己的业务项目中还可以从其他更多的渠道来生成,这也是因为gradle的灵活,我们才可以随心所欲的做到这么多。

Android Gradle必备使用技巧,未完待续,欢迎关注公众号flysnow_org,第一时间看后续技巧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 这一章主要针对项目中可以用到的一些实用功能来介绍Android Gradle,比如如何隐藏我们的证书文件,降低风险...
    acc8226阅读 7,552评论 3 25
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,400评论 25 707
  • 这又是一座山 翻翻手机看看照片 这么多美好的回忆 这应该是在2015年的老君山 一年在市区就下了这一场雪 然而进山...
    露遥说阅读 371评论 1 2
  • 假设demo表结构如下: 1,查找数组里面所有条目,id字段不是数组。查找所有id是1,2,3的数据。 2,查找数...
    real勾玉阅读 206评论 0 0