之前没有设置过打包的命名,每次打包都是默认的"app-realease.apk",之后手动修改名字来显示出它是一个新版本。

晚上学习了如何配置打包名称,很简单,修改build.gradle里的代码就行。

详细记录如下:

1、打开app这个directory下的build.gradle

2、定义打包时间:

//时间
def releaseTime() {
returnnewDate().format("yyyyMMdd", TimeZone.getTimeZone("UTC"))
}

3、自定义发布时的版本号(return的返回值可自行修改,例如1.0、2.0):

//版本号
def getVersionName(){
return"2.0"
}

4、自定义打包名称(代码中的XYZ可修改为app名字):

//名称
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
def fileName
if(outputFile != null&& outputFile.name.endsWith('.apk')) {
if(variant.buildType.name.equals('release')) {
variant.mergedFlavor.versionName = getVersionName()
fileName = "XYZ_${variant.mergedFlavor.versionName}_release.apk"
} elseif(variant.buildType.name.equals('debug')) {
variant.mergedFlavor.versionName = getVersionName()+"."+releaseTime()
fileName = "XYZ_${variant.mergedFlavor.versionName}_debug.apk"
}
output.outputFile = newFile(outputFile.parent, fileName)
}
}
}
5、build.gradle的完整代码:
apply plugin: 'com.android.application'
//定义时间
def releaseTime() {
returnnewDate().format("yyyyMMdd", TimeZone.getTimeZone("UTC"))
}
//设置发布时的版本号
def getVersionName(){
return"2.0"
}
android {
compileSdkVersion 26
buildToolsVersion "26.0.0"
defaultConfig {
applicationId "***"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
buildConfigField("boolean","API_DEBUG","false")
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
buildConfigField("boolean","API_DEBUG","true")
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
//配置打包名称
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
def fileName
if(outputFile != null&& outputFile.name.endsWith('.apk')) {
if(variant.buildType.name.equals('release')) {
variant.mergedFlavor.versionName = getVersionName()
fileName = "XYZ_${variant.mergedFlavor.versionName}_release.apk"
} elseif(variant.buildType.name.equals('debug')) {
variant.mergedFlavor.versionName = getVersionName()+"."+releaseTime()
fileName = "XYZ_${variant.mergedFlavor.versionName}_debug.apk"
}
output.outputFile = newFile(outputFile.parent, fileName)
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
}

今天升级了AS3.1以后,在项目编译的时候发现Gradle中报错了,错误如下:

Error:(60, 0) Cannot set the value of read-only property 'outputFile' for ApkVariantOutputImpl_Decorated{apkData=Main{type=MAIN, fullName=xiaomiRelease, filters=[]}} of type com.android.build.gradle.internal.api.ApkVariantOutputImpl.
Open File

经过一番折腾,网上找大牛的解读,弄明白了output.outputFile变成了只读属性,不能再往里面写东西了,以下是3.0之前的配置:

applicationVariants.all { variant ->    //批量修改Apk名字
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk') && 'release'.equals(variant.buildType.name)) {
def fileName = outputFile.name.replace("${variant.flavorName}", "V${defaultConfig.versionName}-${variant.flavorName}")
fileName = fileName.replace('.apk', "-${buildTime()}.apk")
output.outputFile = new File(outputFile.parent, fileName)
}
}
}

下面是经过修改之后3.0里面批量修改APK名字的配置:

applicationVariants.all { variant ->    //批量修改Apk名字
variant.outputs.all { output ->
if (!variant.buildType.isDebuggable()) {
//获取签名的名字 variant.signingConfig.name
//要被替换的源字符串
def sourceFile = "-${variant.flavorName}-${variant.buildType.name}"
//替换的字符串
def replaceFile = "_V${variant.versionName}_${variant.flavorName}_${variant.buildType.name}_${buildTime()}"
outputFileName = output.outputFile.name.replace(sourceFile, replaceFile);
//遗留问题:如何获取当前module的name,如CodeBooke这个名字怎么获取到
}
}
}

问题:对于如何在gradle中获取module的name,还是没有找到相关的方法,希望有知道的大神留言交流。

2、model下build.gradle

import java.text.SimpleDateFormat
applyplugin:'com.android.application'
android{
compileSdkVersion 28
defaultConfig {
applicationId"ma.mhy.sqliteeditorroot"
minSdkVersion15
targetSdkVersion28
versionCode getMyVersionCode()
versionName getMyVersionName()
testInstrumentationRunner"android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabledfalse
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibilityJavaVersion.VERSION_1_8
targetCompatibilityJavaVersion.VERSION_1_8
}
}
dependencies{
implementation fileTree(dir:'libs',include: ['*.jar'])
}
staticdefgetMyVersionCode() {
returnInteger.parseInt(newSimpleDateFormat("yyMMdd").format(newDate()))
}
staticdefgetMyVersionName() {
return"1.0.6."+"git describe --always".execute().getText().trim()//自动打包命名区分新旧版
}

build.gradle中自动生成版本号

Android Studio中,gradle.build里的设定会覆盖AndroidManifest.xml中的设置。

Gradle中使用的是Groovy语言,这是一种基于JVM的敏捷开发语言,还算是易学易用的。

在项目的build.gradle中,android.defaultConfig里,把versionCode和versionName改成自定义函数getSelfDefinedVersion:

android {    compileSdkVersion23buildToolsVersion"23.0.3"defaultConfig {        applicationId"com.example.xxx"minSdkVersion22targetSdkVersion23versionCode getSelfDefinedVersion("code")        versionName getSelfDefinedVersion("name")    }    buildTypes {        release {            minifyEnabledfalse}    }}
在build.gradle文件底部,统一实现版本号自动生成管理:
defgetSelfDefinedVersion(type) {intaa =1intbb =0Process process ="git rev-list --count HEAD".execute()    process.waitFor()intcccc = process.getText().toInteger()if("code".equals(type)) {        aa *1000000+ bb *10000+ cccc    }elseif("name".equals(type)) {        String today =newDate().format("yyMMdd")        process ="git describe --always".execute()        process.waitFor()        String sha1 = process.getText().trim()"$aa.$bb.$cccc.$today.$sha1"}}
这样虽然比较简陋,但功能是有了。
打包自动版本号与日期
(build.gradle 文件内容,com.android.tools.build:gradle:3.0.0 以下版本)
android{
defaultConfig {...}
自动追加版本号和版本名称
android.applicationVariants.all {
variant->variant.outputs.each {
output-> output.outputFile = new File(output.outputFile.parent,"app_device_v"+defaultConfig.versionName+"_"+new Date().format("yyyy-MM-dd")+"_"+buildType.name+".apk")
}
}
}
如com.android.tools.build:gradle:3.0.0及其以上版本
android{
defaultConfig {...}
//release版本输出包名自动追加版本号和版本名称
applicationVariants.all {
variant ->
variant.outputs.all {
if (buildType.name == 'release'){
outputFileName = "app_v" + defaultConfig.versionName + "_" + new Date().format("yyyy-MM-dd") + "_" + buildType.name + ".apk"
}
}
}
}

android应用程序的版本号Version name可以在android manifest下手动修改,在code中调用api获取该版本号。

项目中的目标是:每编译一次程序都会自动修改版本号,而不需要手动修改,由于中间debug的次数较多,我打算以每次Build时的当前时间作为版本号。

操作步骤:

1. Android工程目录的assets文件夹下新建一文件,命名为version

2. code中通过api获取assets下的文件内容,回显在activity,基础android编程知识,不解释

3. Android.mk 文件中LOCAL_PATH:= $(call my-dir)下紧接着调用shell语句: $(shell date +%Y%m%d%H:%M:%S>$(LOCAL_PATH)/assets/version)

如果项目对版本号或者版本名称有更复杂的需求,可以直接写成shell脚本,再由Android.mk 调用

编译时自动输出当前编译时间到version文件,并打包到apk中,done!

感谢

import java.text.SimpleDateFormat
applyplugin:'com.android.application'
//设置发布时的版本号
static def getVersionName() {
int aa =3
int i ="git rev-list --count HEAD".execute().getText().toInteger()//build次数
String today =new Date().format("yyMMdd")
Process process ="git describe --always".execute()
process.waitFor()
String str = process.getText().trim()
return "$aa.$i.$today.$str"//"3.276"+str     //3.1.20181015.内容
// return aa+"."+i+"."+"."+today+"."+str
}
//设置发布时的版本码
static def getVersionCode() {
Process process ="git rev-list --count HEAD".execute()
process.waitFor()
int i = process.getText().toInteger()
return Integer.parseInt(new SimpleDateFormat("yyMMdd").format(new Date()))*10000 +i//今天日期1810150001+次数
}
android {
compileSdkVersion28
defaultConfig {
applicationId"com.hcsoft.storekeeper"
minSdkVersion15
targetSdkVersion28
versionCodegetVersionCode()
versionName getVersionName()
testInstrumentationRunner"android.support.test.runner.AndroidJUnitRunner"
}
//release版本输出包名自动追加版本号和版本名称
applicationVariants.all {
variant ->
variant.outputs.all {
if (buildType.name =='release'){
outputFileName ="app_v" + defaultConfig.versionName +"_" +new Date().format("yyyy-MM-dd") +"_" + buildType.name +".apk"
}
}
}
buildTypes {
release {
buildConfigField("boolean","API_DEBUG","false")
minifyEnabledfalse
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
}
debug {
buildConfigField("boolean","API_DEBUG","true")
minifyEnabledfalse
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies{
implementation fileTree(dir:'libs',include: ['*.jar'])
}
原理是获得打包签名文件的生成时间。
/**
* 获得app的打包时间
*
* @return
*/
private String getAppBuildTime() {
String result = "";
try {
ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(),0);
ZipFile zf = new ZipFile(ai.sourceDir);
ZipEntry ze = zf.getEntry("META-INF/MANIFEST.MF");
long time = ze.getTime();
SimpleDateFormat formatter = (SimpleDateFormat) SimpleDateFormat.getInstance();
formatter.applyPattern("yyyy/MM/dd HH:mm:ss");
result = formatter.format(new java.util.Date(time));
zf.close();
} catch (Exception e) {
}
return result;
}
上述方法貌似在5.0之后不好用,所以现在建议使用Gradle生成打包时间,然后再引用。核心代码如下:
buildTypes {
release {
buildConfigField("String", "releaseTime", "\""+new Date().format("yyyy/MM/dd HH:mm:ss")+"\"")
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
....
}

主要是使用buildConfigField生成打包时间。

然后使用 TextView.setText(BuildConfig.releaseTime);