2018-10-15【Android打包版本号设置方法】

之前没有设置过打包的命名,每次打包都是默认的"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.

<a href="openFile:E:\Studio\MyApplication\CodeBook\build.gradle">Open File</a>

经过一番折腾,网上找大牛的解读,弄明白了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!

感谢https://my.oschina.net/u/659658/blog/84545


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);

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容