您的位置:首页 > 移动开发

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。

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版本,这对于我们来说是多余的。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐