在移动应用开发和运营的过程中,版本管理是一个老生常谈的基础问题,一些版本的基本概念也常常会困扰我们的研发和运营人员。同时,手动管理软件版本,也常常会因为不小心导致后续的发布和更新问题。

这里,我准备了一些 iOS 和 Android 版本的基础知识,以及如何在应用中获取版本信息和如何使用 Xcode 和 Android Studio 自动管理版本号。

版本号解释

无论是 iOS 还是 Android 都定义了两个版本属性:

  • iOS
    在 Info.plist 中定义

    • CFBundleShortVersionString
      在Xcode中解释为 Version ,这个就是我们常说的版本号,一般用户可见,通常由 <主版本号>.<次版本号>.<维护号> 三部分组成,主要用来识别不同时期不同功能的产品。

    • CFBundleVersion
      在Xcode中解释为 Build ,一般用于应用市场和程序内部识别版本,作为更新判断的依据,通常是一个递增的 INT 类型。

      这两个值可以在 Xcode 的项目信息里面进行管理,

      img_01.png

      当然,你也可以直接编辑 Info.plist

  • Android
    在 AndroidManifest.xml 中定义

    • android:versionName
      对应 iOS 中的 CFBundleShortVersionString 版本号,用作产品管理。

    • android:versionCode
      对应 iOS 中的 CFBundleVersion 编译号,作为内部识别。

      在使用 Eclipse 开发时,可以通过直接编辑 AndroidManifest.xml 文件修改,在使用 Android Studio 时,这些信息由 Gradle 脚本管理,找到并打开项目目录下 app 目录内的 build.gradle 文件,版本信息在 defaultConfig 段,

      img_02.png

程序内获取版本信息

一般在应用的关于页面,我们都会显示应用的版本,方便客服定位问题,在应用检查更新时,也常常需要用到版本信息。其实,无论是在 iOS 上,还是 Android 平台,获取笨笨信息都比较简便:

  • iOS
    索引 Bundle 信息中的相关字段:

      NSBundle *bundle = [NSBundle mainBundle];
    NSString *name = [[bundle localizedInfoDictionary] objectForKey:@"CFBundleDisplayName"];
    NSString *version = [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
    NSString *build = [bundle objectForInfoDictionaryKey:@"CFBundleVersion"]; NSString *fullVersion = [NSString stringWithFormat:@"version: %@ (%@)", version, build];

    上述的代码中,name 为本地化的程序名称,version 为版本号,build 为编译号,'fullVersion' 则将版本号和编译号组成一个完整的版本信息。

  • Android
    获取 PackageInfo 中的相关信息:

      PackageInfo pi = sContext.getPackageManager().getPackageInfo(sContext.getPackageName(), 0);
    String versionName = pi.versionName;
    int versionCode = pi.versionCode; String fullVersion = String.format("version: %s (%d)", versionName, versionCode);

    同样,versionName 为版本号,versionCode 为编译号,不同的是,在 Android 中 versionCode 使用 int 类型存储。

Xcode 和 Android Studio 编译号自增

一般应用的 版本号 都会由 产品经理 或 项目经理 决定,根据产品所处的阶段和功能决定修改版本号的哪一个段。但 编译号 更多时候是根据每次发布递增,有时候还会遇到同一个版本号对应多个编译号的情况,如,紧急修复了一个关键 Bug 并更新发布一个版本,但如果每次发布都要手动去修改编译号回很繁琐,也很容易忘记和出错,而不管是 Xcode 还是 Android Studio 都没有提供自增编译号的功能,我们只有手动添加编译脚本来达到自增的目的。

  • Xcode
    添加编译过程,读取并修改 Info.plist 中的版本信息:

    1. 打开工程,选择编译目标,点击 Build Phases 选项卡。

      img_03.png
    2. 点击左上角的 + 添加,选择 New Run Script Phase 添加一个编译脚本。

      img_04.png

      img_05.png
    3. 在脚本编辑其中输入下面的脚本:

       #!/bin/bash
      buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
      buildNumber=$(($buildNumber + 1))
      /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"

      上述脚本使用 PlistBuddy工具,读取 Info.plist 中的 CFBundleVersion 值,+1 后写回 Info.plist 中。

    4. 我们需要让该段代码在应用打包前执行,以便将修改应用到App包内,因此我们将该编译过程重命名为 Increase Version Code 并移动到 Copy Bundle Resources之前。

      img_06.png
    5. 这样,当我们每次编译该 Target 时 ,就会执行改脚本将 CFBundleVersion 值增加1,但没次调试运行时,该值都会加1,这并不是我们想要的,我们只需要在每次打包发布时增加1就行,因此,我们将 Run script only wehn installing 前的勾打上,这样就只会在打包应用时执行改脚本。

      img_07.png
  • Android Studio
    Android Studio 的 versionCode 自增原理和 Xcode 类似,不同的是我们不能让编译脚本修改自身,而是通过一个额外的 Java Properties 来文件存储版本信息:

    1. 打开工程,选择 Module: app 的编译脚本 build.gradle

      img_08.png
    2. 找到 defaultConfig 段,替换为下面的脚本:

       def versionPropsFile = file('version.properties')

      if (versionPropsFile.canRead()) {

      Properties versionProps = new Properties()

      versionProps.load(new FileInputStream(versionPropsFile))

 def verCode = versionProps['VERSION_CODE'].toInteger()

      versionProps['VERSION_CODE'] = (++verCode).toString()

      versionProps.store(versionPropsFile.newWriter(), null)

 defaultConfig {

      applicationId "com.example.versionexample"

      minSdkVersion 19

      targetSdkVersion 23

      versionCode verCode

      versionName "1.0.1"

      }

      } else {

      throw new GradleException("Could not read version.properties!")

      }

      这段脚本会打开 app 目录下的 version.properties 配置文件,读取 VERSION_CODE 字段并增加1后写回,同时将值赋给 defaultConfig 中的 versionCode 。

    3. 此时,点击 Sync 会报错 Could not read version.properties!,这是我们刚刚在脚本中抛出的,原因是找不到 version.properties 文件,我们需要新建一个。打开命令行定为到项目目录下:

       $ cd app/
      $ echo "VERSION_CODE=1" > version.properties

      再次点击 Sync 就不会报错了。查看 version.properties 文件,

       $ cat version.properties
      #Mon Nov 02 15:18:49 CST 2015
      VERSION_CODE=3

      发现 VERSION_CODE 已经增加到 3 了,说明该脚本被执行了两次,但这并不符合我们的预期。

    4. 同 Xcode 中一样,我们期望仅仅在应用打包时将 versionCode 增加 1 。因此我们需要获取编译参数,仅当 release 时才将 versionCode 增加 1 。修改后的脚本如下:

       def versionPropsFile = file('version.properties')

      if (versionPropsFile.canRead()) {

      Properties versionProps = new Properties()

      versionProps.load(new FileInputStream(versionPropsFile))

 def verCode = versionProps['VERSION_CODE'].toInteger()
 def runTasks = gradle.startParameter.taskNames
      if (':app:assembleRelease' in runTasks) {
      versionProps['VERSION_CODE'] = (++verCode).toString()
      versionProps.store(versionPropsFile.newWriter(), null)
      } defaultConfig {

      applicationId "com.example.versionexample"

      minSdkVersion 19

      targetSdkVersion 23

      versionCode verCode

      versionName "1.0.1"

      }

      } else {

      throw new GradleException("Could not read version.properties!")

      }

      这里获取当前task的名称,仅当task包含 :app:assembleRelease 时才会将 versionCode 加 1 ,否则直接使用读取到的值。

小结

版本管理是产品和项目管理中非常重要的一环,但在开发初期也常常容易被忽略,等到遇到问题时候才感到头痛。其实,产品经理和开发人员在产品的立项阶段就应该对产品版本有一个一致的理解,我个人通常在项目框架建立之初就将这些规则脚本添加到项目文件中,以期降低后续版本维护的成本。

使用 Xcode 和 Android Studio 管理 iOS 和 Android 项目版本的更多相关文章

  1. 【转】如何使用Android Studio把自己的Android library分发到jCenter和Maven Central

    转自:http://www.devtf.cn/?p=760&utm_source=tuicool 如何使用Android Studio把自己的Android library分发到jCenter ...

  2. Android Studio之同一窗口打开项目

    Android Studio默认新打开的项目都是重新打开一个窗口,和原项目窗口同时存在,如果打开多个项目,则有很多窗口同时打开,怎么根据需要决定自己以何种方式打开呢? 1.设置打开新项目的方式 第一项 ...

  3. Android Studio安装以及Fetching android sdk component information超时的解决方案

    转载:http://www.cnblogs.com/sonyi/p/4154797.html 在经过两年的开发之本后,Google 公司终于发布了 Android Studio 1.0,喜欢折腾的童鞋 ...

  4. 利用Android Studio、MAT对Android进行内存泄漏检测

    利用Android Studio.MAT对Android进行内存泄漏检测 Android开发中难免会遇到各种内存泄漏,如果不及时发现处理,会导致出现内存越用越大,可能会因为内存泄漏导致出现各种奇怪的c ...

  5. (mac)Android Studio安装以及Fetching android sdk component information超时的解决方案

    解决Mac下面Fetching android sdk component information加载过久问题, 关于windows中可以参考前面一篇文章 关于安装和下载可以百度一下地址.安装完成后, ...

  6. (window)Android Studio安装以及Fetching android sdk component information超时的解决方案

    转自:http://www.cnblogs.com/sonyi/p/4154797.html 在经过两年的开发之本后,Google 公司终于发布了 Android Studio 1.0,喜欢折腾的童鞋 ...

  7. 解决Android Studio启动速度慢的问题。避免每次启动Android Studio都要fetching Android sdk compoment information。

    Android Studio每次启动都要去fetching sdk,由于Android sdk 官网在大陆连不上,所以每次启动时界面都会停在那里很久. 解决办法就是设置取消每次fetching sdk ...

  8. Android Studio 快速实现上传项目到Github(详细步骤)

    前言: 本文主要讲解如何将Android Studio项目上传至GitHub,在此之前,先介绍几个概念. Android Studio:是谷歌推出一个Android集成开发工具,基于IntelliJ ...

  9. Android studio 使用心得(六)—android studio 如何加载.so文件

    之前一直没怎么注意,以为.so文件android为像eclipse一样直接加载,但是直到昨天我在android studio上调试公司项目推送消息的时候,才发现,.so文件原来没有加载成功. 可能之前 ...

随机推荐

  1. CocoaPods ADD private Spec Repo

    Private Pods CocoaPods is a great tool not only for adding open source code to your project, but als ...

  2. ASP.NET 获取来源网站的网址,获取上一网页的网址,获取来源网页的URL,获取上一网页的URL

    ASP.NET 获取来源网站的网址,获取上一网页的网址,获取来源网页的URL, 获取上一网页的URL Uri Url = HttpContext.Current.Request.UrlReferrer ...

  3. Linux 安装java

    Linux安装Java之后,不用像Windows那样设置环境变量,直接就可在命令行当中输入java或者javac看到效果

  4. "Cannot convert value '0000-00-00' from column 2 to TIMESTAMP"mysql时间转换bug

    今天在项目中遇到这样的一个bug,Cannot convert value '0000-00-00' from column 2 to TIMESTAMP 仔细一查,经过http://blog.csd ...

  5. bzoj 3744: Gty的妹子序列 主席树+分块

    3744: Gty的妹子序列 Time Limit: 15 Sec  Memory Limit: 128 MBSubmit: 101  Solved: 34[Submit][Status] Descr ...

  6. 【UVA11294】Wedding (2-SAT)

    题意: 有N-1对夫妻参加一个婚宴,所有人都坐在一个长长的餐桌左侧或者右侧,新郎和新娘面做面坐在桌子的两侧.由于新娘的头饰很复杂,她无法看到和她坐在同一侧餐桌的人,只能看到对面餐桌的人.任意一对夫妻不 ...

  7. win7电脑自动关机怎么设置

    WIN7系统自带了关机工具的,下面是步骤 1.“开始”-右键点击“计算机”选择“管理”,在左侧界面中选择“任务计划程序”. 2.在右侧界面中选择“创建基本任务”(向导式创建任务,推荐新手使用)或者“创 ...

  8. ExecutorService介绍

    转自: http://victorzhzh.iteye.com/blog/1010359 下面是excutor相关的类结果: ExecutorService接口继承了Executor接口,定义了一些生 ...

  9. 如何在sourcetree 下提交代码到gerrit上

    gerrit的审核机制决定了提交到远程到代码并非远程master分支,而是/refs/for/master 分支,所以需要解决怎么在sourcetree下提交代码到/refs/for/master分支 ...

  10. Delphi常用排序

    1.冒泡排序 Delphi/Pascal code   ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 procedure BubbleSort(var x:a ...