Android APP基于Android Studio多版本构建实战
2017-05-21 20:24
519 查看
关于多版本构建,我们可以通过buildTypes来新增构建类型,一般而言这里也不需要自行定义,默认会生成debug和release两种类型。
重点在于使用productFlavors生成不同“风味”的版本,我们可以构建标准版和中性版APP,这在企业应用中非常普遍,中性版本不含有logo信息,可再次贴牌等。同时应该在release版本中关闭log输出。
我这里的区别为中性版assets/defaultLogo/logo_default.png和标准版的资源是不一样的,所以需要创建对应的productFlavor文件夹,放入需要替换的logo_default.png资源。同时不论是中性版还是标准版还有四种机型需要适配(F600P、F600、F200P、F200),也就是说我们需要生成8个版本的app。
使用以下函数可以判断是否debug版本,非debug版本也就是正式版关闭log即可。
我在自定义的Application(MainApp.java)中根据判断是否开启log开关。
这段代码很好理解,通过在android闭包下配置signingConfigs,并在buildTypes的release下引用即可。
通过在android闭包下配置8个版本的flavor,并给中性版创建对应的文件夹,放入需要替换的logo_default.png资源。Flavor对应的文件夹位于src下,和main平级。
src->
main
neutralF600P
neutralF600
neutralF200P
neutralF200
再来看getVersionNameMap(key)函数。
解释一下:manifestPlaceholders是android gradle dsl中定义的属性,用来替换其中的一些字段,使用groovy GString在AndroidMainifest中占位,通过给manifestPlaceholders赋值Map,将Map中的值替换到AndroidMainifest中。
AndroidMainfest.xml中定义如下:
Java代码中通过meta-data的对应value去配置为不同的版本,如F600P或F600,当然这部分代码需要在Java中实现对应的逻辑。
接下来再看转化为版本Version的代码,则部分代码并非必须的,相信看到这里已经明白了,我们是间接的用AndroidMainfest中的meta-data标签传递在build.gradle配置的值,从而达到让java代码加载不同版本的目的。
这一大段就是重命名后的apk文件名,最终生成的文件名为:
xxx-neutralF600P-v1.0.5-2455-signed-20170502.apk
很好懂,用GString的写法占位。其中后面还有一段的逻辑为把同一型号下标准版和中性版的app放到同一目录下,如F600P文件夹下,放入neutralF600P 和standardF600P app。
最后来看一下releaseTime()函数,作用为按照yyyyMMdd格式化当前时间,即格式化为年月日的形式。
回车后任务开始进行,全部finish后到xx工程\xx\build\outputs\apk下取出apk即可。
详解:
之所以可以使用gradle aR命令是因为gradle支持这种简短的驼峰式写法,实际上和gradle assembleRelease命令是等价的。
另外,我们使用这个命令生成的apk全部为release版本,虽然通过gradle build和gradle assemble都可以生成我们想要的release版本,但gradle build同时生成了debug版本,这对于我们来说是多余的。
重点在于使用productFlavors生成不同“风味”的版本,我们可以构建标准版和中性版APP,这在企业应用中非常普遍,中性版本不含有logo信息,可再次贴牌等。同时应该在release版本中关闭log输出。
我这里的区别为中性版assets/defaultLogo/logo_default.png和标准版的资源是不一样的,所以需要创建对应的productFlavor文件夹,放入需要替换的logo_default.png资源。同时不论是中性版还是标准版还有四种机型需要适配(F600P、F600、F200P、F200),也就是说我们需要生成8个版本的app。
1.目标
生成多个不同“风味”的版本很容易,只要定义productFlavors即可。但目标应该更进一步,通过只敲一次命令行生成所有的标准版和中性版release app,并且按照我们的命令格式重命名apk文件,最后只需要到build/outputs/apk拿出对应的安装包。2.使用Gradle命令行
(1).查看gradle命令是否可用
默认情况下打开Android Studio的Terminal面板,键入gradle -version会提示找不到这个命令,无法使用,这需要配置环境变量。(2).查看Android Studio自带Gradle安装位置
在File-Settings中查看Gradle home(Android Studio安装后会自带一个Gradle,就在其安装目录gradle下)所在位置。(3).配置Gradle环境变量
建议使用用户变量,只有当前用户可用。添加一个变量名为GRADLE_HOME、变量值为E:/as/gradle/gradle-2.14.1的环境变量。然后编辑path环境变量,新建一行,并填入%GRADLE_HOME%/bin/即可。(4).验证
再次在Terminal中输入gradle –version命令后回车。出现下面的gradle版本信息,就说明gradle构建工具在命令行方式下已经正常工作。3.关闭log开关
当然前提是log可以全局通过一个boolean值控制。使用以下函数可以判断是否debug版本,非debug版本也就是正式版关闭log即可。
/** * 判断当前应用是否debug状态 * * @param context * @return */ public static boolean isApkDebug(Context context) { ApplicationInfo info = context.getApplicationInfo(); return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; }
我在自定义的Application(MainApp.java)中根据判断是否开启log开关。
// Log开关 Logger.DEBUG = AppUtils.isApkDebug(this);
4.配置正式签名
android{ … signingConfigs { releaseSign { storeFile file("E:\\xx\\xx\\xx.keystore") storePassword "xxxxx" keyAlias "xx" keyPassword "xxxxx" } } buildTypes { release { signingConfig signingConfigs.releaseSign … } } … }
这段代码很好理解,通过在android闭包下配置signingConfigs,并在buildTypes的release下引用即可。
5.配置productFlavors
productFlavors { // 中性版本F600P neutralF600P { // 这里替换了assets内logo_default.png资源 manifestPlaceholders = getVersionNameMap('F600P') } // 标准版本F600P standardF600P { manifestPlaceholders = getVersionNameMap("F600P") } // 中性版本F600 neutralF600 { manifestPlaceholders = getVersionNameMap("F600") } // 标准版本F600 standardF600 { manifestPlaceholders = getVersionNameMap("F600") } // 中性版本F200P neutralF200P { manifestPlaceholders = getVersionNameMap("F200P") } // 标准版本F200P standardF200P { manifestPlaceholders = getVersionNameMap("F200P") } // 中性版本F200 neutralF200 { manifestPlaceholders = getVersionNameMap("F200") } // 标准版本F200 standardF200 { manifestPlaceholders = getVersionNameMap("F200") } }
通过在android闭包下配置8个版本的flavor,并给中性版创建对应的文件夹,放入需要替换的logo_default.png资源。Flavor对应的文件夹位于src下,和main平级。
src->
main
neutralF600P
neutralF600
neutralF200P
neutralF200
再来看getVersionNameMap(key)函数。
/** * 根据key获取version Map * @param key * @return */ def getVersionNameMap(key) { def versionNameMap = [:] if (key == "F600P") { versionNameMap.D_VERSION_NAME = "F600P" } else if (key == "F600") { versionNameMap.D_VERSION_NAME = "F600" } else if (key == "F200P") { versionNameMap.D_VERSION_NAME = "F200P" } else if (key == "F200") { versionNameMap.D_VERSION_NAME = "F200" } else { versionNameMap.D_VERSION_NAME = "F600P" } return versionNameMap }
解释一下:manifestPlaceholders是android gradle dsl中定义的属性,用来替换其中的一些字段,使用groovy GString在AndroidMainifest中占位,通过给manifestPlaceholders赋值Map,将Map中的值替换到AndroidMainifest中。
AndroidMainfest.xml中定义如下:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="xxx" > <application android:name="xx.MainApp" android:allowBackup="true" android:hardwareAccelerated="false" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"> … <meta-data android:name="D_VERSION" android:value="${D_VERSION_NAME}" /> … </application> </manifest>
Java代码中通过meta-data的对应value去配置为不同的版本,如F600P或F600,当然这部分代码需要在Java中实现对应的逻辑。
/** * 通过key获取Application标签下meta-data的值 * * @param context * @param key * @return */ public static String getApplicationMetaDataVal(Context context, String key) { ApplicationInfo appInfo = null; String msg = null; try { appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); msg = appInfo.metaData.getString(key); } catch (NameNotFoundException e) { e.printStackTrace(); } return msg; }
接下来再看转化为版本Version的代码,则部分代码并非必须的,相信看到这里已经明白了,我们是间接的用AndroidMainfest中的meta-data标签传递在build.gradle配置的值,从而达到让java代码加载不同版本的目的。
// 获取不同版本的对应配置参数 Version version = Version.F600P; String versionName = AppUtils.getApplicationMetaDataVal(this, "D_VERSION"); if (versionName.equals(Version.F600P.getName())) { version = Version.F600P; } else if (versionName.equals(Version.F600.getName())) { version = Version.F600; } else if (versionName.equals(Version.F200P.getName())) { version = Version.F200P; } else if (versionName.equals(Version.F200.getName())) { version = Version.F200; } Log.d("version", "v:" + version.getName()); versionConfig = VersionFactory.getVersionConfig(version);
6.定制重命名逻辑
android { … android.applicationVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk')) { String flavorName = variant.productFlavors[0].name //修改apk文件名 def fileName = "xxx-${flavorName}-${defaultConfig.versionName}-${defaultConfig.versionCode}-signed-${releaseTime()}.apk" def fileParentDir if (flavorName.startsWith("standard")) { fileParentDir = flavorName.substring("standard".length(), flavorName.length()); } else if (flavorName.startsWith("neutral")) { fileParentDir = flavorName.substring("neutral".length(), flavorName.length()); } File dir = new File(outputFile.parent + File.separator + fileParentDir + File.separator) if (!dir.exists()) { dir.mkdirs() } output.outputFile = new File(dir, fileName) } } } … }
xxx-${flavorName}-${defaultConfig.versionName}-${defaultConfig.versionCode}-signed-${releaseTime()}.apk
这一大段就是重命名后的apk文件名,最终生成的文件名为:
xxx-neutralF600P-v1.0.5-2455-signed-20170502.apk
很好懂,用GString的写法占位。其中后面还有一段的逻辑为把同一型号下标准版和中性版的app放到同一目录下,如F600P文件夹下,放入neutralF600P 和standardF600P app。
最后来看一下releaseTime()函数,作用为按照yyyyMMdd格式化当前时间,即格式化为年月日的形式。
def releaseTime() { return new Date().format("yyyyMMdd",TimeZone.getTimeZone("UTC")) }
7.最后一步
以上步骤配置好了生成签名版程序的所有要素,最后在android studio命令行中敲入:gradle aR
回车后任务开始进行,全部finish后到xx工程\xx\build\outputs\apk下取出apk即可。
详解:
之所以可以使用gradle aR命令是因为gradle支持这种简短的驼峰式写法,实际上和gradle assembleRelease命令是等价的。
另外,我们使用这个命令生成的apk全部为release版本,虽然通过gradle build和gradle assemble都可以生成我们想要的release版本,但gradle build同时生成了debug版本,这对于我们来说是多余的。
相关文章推荐
- Android实战简易教程<二十九>基于Face++实现年龄识别APP(一))
- 《ArcGIS Runtime SDK for Android开发笔记》——(4)、基于Android Studio构建ArcGIS Android开发环境
- Android实战之app版本更新升级全文章(二)
- Android studio报错:找不到匹配的任何版本。Android。支持:appcompat-v7:27。+。
- 《ArcGIS Runtime SDK for Android开发笔记》——(4)、基于Android Studio构建ArcGIS Android开发环境
- 《ArcGIS Runtime SDK for Android开发笔记》——(5)、基于Android Studio构建ArcGIS Android开发环境(离线部署)
- MAC下Android Studio 1.4编译构建Cocos2d-x 3.9之Android版本程序
- 【基于zxing的编解码实战】zxing项目源码解读(2.3.0版本,Android部分)
- 《ArcGIS Runtime SDK for Android开发笔记》——(5)、基于Android Studio构建ArcGIS Android开发环境(离线部署)
- 《ArcGIS Runtime SDK for Android开发笔记》——(5)、基于Android Studio构建ArcGIS Android开发环境(离线部署)(转)
- 《ArcGIS Runtime SDK for Android开发笔记》——(4)、基于Android Studio构建ArcGIS Android开发环境
- Android实战之app版本更新升级全文章(三)
- Android导入开源库及jar等文件到基于Android Studio构建的项目中
- Ionic实战 自动升级APP(Android版) 基于ngcordova
- 《ArcGIS Runtime SDK for Android开发笔记》——(5)、基于Android Studio构建ArcGIS Android开发环境(离线部署)
- 【基于zxing的编解码实战】zxing项目源码解读(2.3.0版本,Android部分)
- Android实战之app版本更新升级全文章(一)
- 【基于zxing的编解码实战】zxing项目源码解读(2.3.0版本,Android部分)
- Android笔记之:App应用之发布各广告平台版本的详解