使用 Xcode 和 Android Studio 管理 iOS 和 Android 项目版本
在移动应用开发和运营的过程中,版本管理是一个老生常谈的基础问题,一些版本的基本概念也常常会困扰我们的研发和运营人员。同时,手动管理软件版本,也常常会因为不小心导致后续的发布和更新问题。
这里,我准备了一些 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
中的版本信息:打开工程,选择编译目标,点击
Build Phases
选项卡。img_03.png点击左上角的 + 添加,选择
New Run Script Phase
添加一个编译脚本。img_04.pngimg_05.png在脚本编辑其中输入下面的脚本:
#!/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
中。我们需要让该段代码在应用打包前执行,以便将修改应用到App包内,因此我们将该编译过程重命名为
Increase Version Code
并移动到Copy Bundle Resources
之前。img_06.png这样,当我们每次编译该 Target 时 ,就会执行改脚本将
CFBundleVersion
值增加1,但没次调试运行时,该值都会加1,这并不是我们想要的,我们只需要在每次打包发布时增加1就行,因此,我们将Run script only wehn installing
前的勾打上,这样就只会在打包应用时执行改脚本。img_07.png
Android Studio
Android Studio 的 versionCode 自增原理和 Xcode 类似,不同的是我们不能让编译脚本修改自身,而是通过一个额外的 Java Properties 来文件存储版本信息:打开工程,选择
Module: app
的编译脚本build.gradle
。img_08.png找到
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
。此时,点击 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 了,说明该脚本被执行了两次,但这并不符合我们的预期。
同 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 项目版本的更多相关文章
- 【转】如何使用Android Studio把自己的Android library分发到jCenter和Maven Central
转自:http://www.devtf.cn/?p=760&utm_source=tuicool 如何使用Android Studio把自己的Android library分发到jCenter ...
- Android Studio之同一窗口打开项目
Android Studio默认新打开的项目都是重新打开一个窗口,和原项目窗口同时存在,如果打开多个项目,则有很多窗口同时打开,怎么根据需要决定自己以何种方式打开呢? 1.设置打开新项目的方式 第一项 ...
- Android Studio安装以及Fetching android sdk component information超时的解决方案
转载:http://www.cnblogs.com/sonyi/p/4154797.html 在经过两年的开发之本后,Google 公司终于发布了 Android Studio 1.0,喜欢折腾的童鞋 ...
- 利用Android Studio、MAT对Android进行内存泄漏检测
利用Android Studio.MAT对Android进行内存泄漏检测 Android开发中难免会遇到各种内存泄漏,如果不及时发现处理,会导致出现内存越用越大,可能会因为内存泄漏导致出现各种奇怪的c ...
- (mac)Android Studio安装以及Fetching android sdk component information超时的解决方案
解决Mac下面Fetching android sdk component information加载过久问题, 关于windows中可以参考前面一篇文章 关于安装和下载可以百度一下地址.安装完成后, ...
- (window)Android Studio安装以及Fetching android sdk component information超时的解决方案
转自:http://www.cnblogs.com/sonyi/p/4154797.html 在经过两年的开发之本后,Google 公司终于发布了 Android Studio 1.0,喜欢折腾的童鞋 ...
- 解决Android Studio启动速度慢的问题。避免每次启动Android Studio都要fetching Android sdk compoment information。
Android Studio每次启动都要去fetching sdk,由于Android sdk 官网在大陆连不上,所以每次启动时界面都会停在那里很久. 解决办法就是设置取消每次fetching sdk ...
- Android Studio 快速实现上传项目到Github(详细步骤)
前言: 本文主要讲解如何将Android Studio项目上传至GitHub,在此之前,先介绍几个概念. Android Studio:是谷歌推出一个Android集成开发工具,基于IntelliJ ...
- Android studio 使用心得(六)—android studio 如何加载.so文件
之前一直没怎么注意,以为.so文件android为像eclipse一样直接加载,但是直到昨天我在android studio上调试公司项目推送消息的时候,才发现,.so文件原来没有加载成功. 可能之前 ...
随机推荐
- java 转html为pdf
最近有个需求转html为pdf . 用过itext . pd4ml ,都不理想,不是样式有问题,就是页面大小有问题. 或字体有问题. 解决办法是通过wkhtmltopdf工具 , 下载地址为:htt ...
- tornado远远不止
大家的回答都有点片面,更多的关注web框架成,其实tornado远远不止这些,且听我慢慢到来1.高性能的网络库,这可以和gevent,twisted,libevent等做对.提供了异步io支持,超时事 ...
- JS获取终端屏幕、浏览窗口的相关信息
查看终端屏幕相关信息,在windows系统的控制面板可以查到分辨率且可以设置,更具体的浏览器可视窗口等信息则需要借助其他工具.而在程序里需要动态获取时该怎么做呢? 琢磨的一个js方法,供大家参考.如下 ...
- Uva_11762 Race to 1
题目链接 题意: 给一个数n, 每次从小于等于n的素数里选一个P, 如果能被n整除, 那么就n就变成n / P. 问: n 变成1的期望. 思路: 设小于等于n的素数有p 个, 其中是n的约数的有g个 ...
- java简单字符串处理
在实际的开发工作中,对字符串的处理是最常见的编程任务. 本题目即是要求程序对用户输入的串进行处理.具体规则如下: 1. 把每个单词的首字母变为大写. 2. 把数字与字母之间用下划线字符(_)分开,使得 ...
- bootstrap datepicker时间插件显示位置不对
bppystrap-datetimepicker.min.js中,修改如下:将原来的 if(!b(this.element)){l=l+document.body.scrollTop}改 ...
- BZOJ1699: [Usaco2007 Jan]Balanced Lineup排队
1699: [Usaco2007 Jan]Balanced Lineup排队 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 933 Solved: 56 ...
- outlook2010怎么老提示IMAP服务器已关闭连接啊
最近在Outlook中使用 网易 163邮件的IMAP功能时,发现接收邮件的速度非常慢,才几封邮件就花了至少15分钟才停止了同步邮件服务器的过程,然后提示一个对话框:"IMAP 服务器已 ...
- 关于 all-delete-orphan
当关联双方存在父子关系,就可以在 set 处设定 cascade 为 all-delete-orphan 所谓父子关系,即指由父方控制子方的持久化圣明周期,子方对象必须和一个父方对象关联.如果删除父方 ...
- 宁波Uber优步司机奖励政策(1月25日~1月31日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...