Android使用Gradle命令动态传参完成打包,不需要修改代码

Android使用Gradle命令动态传参完成打包,不需要修改代码

2023年7月13日发(作者:)

Android使⽤Gradle命令动态传参完成打包,不需要修改代码不得不说,Gradle很强⼤,有⼈会问Gradle是什么?这⾥也不细讲,在我认为他就是⼀个构建神器。Gradle 提供了:⼀个像 Ant ⼀样的⾮常灵活的通⽤构建⼯具⼀种可切换的, 像 Maven ⼀样的基于合约构建的框架⽀持强⼤的多⼯程构建⽀持强⼤的依赖管理(基于 ApacheIvy )⽀持已有的 Maven 和 ivy 仓库⽀持传递性依赖管理, ⽽不需要远程仓库或者 或者 ivy 配置⽂件基于 Groovy 的构建脚本有丰富的领域模型来描述你的构建⽂件  ⾸先先来说说这个⽂件,⼤家都知道Android 项⽬默认⽬录下就有两个⽂件,其实也类似Maven中的⽂件,⼀个是Project范围的,另⼀个是Module范围的,由于⼀个Project可以有多个Module,所以每个Module下都会对应⼀个。

  这两个⽂件是有区别的,Project下的是基于整个Project的配置,⽽Module下的是每个模块⾃⼰的配置。这⾥我主要讲⼀下module下的⽂件,也就是通常说的默认Module app下的。

  讲到这个配置,还需要引⼊Gradle中的Task概念。Gradle的Project从本质上说只是含有多个Task的容器,⼀个Task与Ant的Target相似,表⽰⼀个逻辑上的执⾏单元。我们可以通过很多种⽅式定义Task,所有的Task都存放在Project的TaskContainer中,Task是Gradle的第⼀公民。

  Task可以⾃定义,但如果没有什么太⼤需求,其实⼏乎都⽤不到,因为Gradle会根据你的⾃动创建Task,你只需要在配置⽂件⾥⾯配置⼀些需要的就可以了,在构建的时候会⾃动⽣成Task,⽤Android Studio的点击同步也会⾃动⽣成。

  可以打开Android Studio右侧的Gradle⾯板,双击就可以执⾏Task  命令⾏⾥⾯可以直接使⽤gradle taskName(例如: gradle assemble360会将360市场的所有包都打出来,包括debug,release等,当然,这些还得你先在⾥⾯配置好)。Android Studio 中也可以打开左下⾓的terminal中使⽤gradlew 执⾏命令,gradlew是gradle wrapper的简写,和gradle命令功能⼀样。  好,开始切⼊正题,假如现在有这样的需求:通常我们的应⽤都会有开发环境(也可以理解为debug环境)、测试环境、预发环境、正式环境区分,我想要不改代码就可以打出我想要环境的包。⽐如我现在分别想要⼀个测试环境的包和⼀个线上环境的包,但是我⼜不想改代码发布的时候发现版本号和版本名忘记改了我想要随时指定⼀个⽬录,将打包好的⽂件放在这⾥⾯我想要在打包时可以⾃定义安装包的⽂件名  很简单,如果你不想改代码⼜想要得到不同环境的包,那当然是使⽤Gradle的命令,前⾯说过Gradle命令后⾯可以加上Task的name直接执⾏Task,那我们可以⾃⼰定义我们需要的Task,让不同的Task去做我们想要做的事不就解决问题了吗。

  可是下⾯⼜说要动态指定版本号版本名⽂件名和⽂件输出路径,那怎么办?

  也不难,传参,需要什么就传⼊什么,这样就解决了动态指定的问题了。  思路讲到这⾥,我们来看看具体要怎么配置这个⽂件:第⼀个问题:怎么去配置不同环境的Task?  原先⽹络请求路径可能很多⼈都会写在代码⾥⾯,如下图所⽰/** * 存放⼀些全局常量 */public class Constants { //外⽹测试环境 public static final String BASEHTTP = ""; //线上地址// public static final String BASEHTTP = ""; //预发环境// public static final String BASEHTTP = ""; //本地测试// public static final String BASEHTTP = ""; //登录 public static final String LOGIN_URL = BASEHTTP + "/api/user/login";}在需要更换环境的时候就换⼀个BASEHTTP的值,这样可以解决问题,但是每⼀次编译打包都需要重新去改⼀下代码。⼀两个包还好,如果多了就会觉得很⿇烦,不⽅便。

  所以就想到了可不可以将这些信息都写在配置⽂件⾥⾯,这样好像就可以跟Gradle有点挂钩了//正式环境 def API_RELEASE_HOST = """" //预发环境 def API_PRE_RELEASE_HOST = """" //测试环境 def API_TEST_HOST = """" //开发环境 def API_DEV_HOST = """"Gradle脚本是⽤Groovy语⾔来写的,Groovy语⾔这⾥不细讲,⼤家可以⽹上搜Groovy语法,资料还是蛮多的,使⽤Groovy可以感受到到以下两个特点:Groovy继承了Java的所有东西,就是你突然忘了Groovy的语法可以写成Java代码,也就是Groovy和Java混在⼀起也能执⾏。Groovy和Java⼀样运⾏在JVM,源码都是先编译为class字节码。  这⾥我⽤def定义了⼏个常量,分别⽤来表⽰不同的环境的请求地址,然后在defaultConfig⾥⾯⾃定义了⼀个常量名,作为代码与配置⽂件的桥梁,建⽴了连接。注意:这⾥的字符串需要在⾥⾯加⼊引号,⽤转义符转义,因为Groovy会直接把最外层引号内的值赋值给⽣成的⾃定义变量,如果不加,赋值后的String字符串就会没有引号,导致编译出错。 defaultConfig { applicationId "" minSdkVersion 15 targetSdkVersion 23 versionCode 5 versionName 1.1.0 buildConfigField("String", "API_HOST", "${API_DEV_HOST}") }这⾥的buildConfigField就是⾃定义⼀个常量,第⼀个参数表⽰类型,第⼆参数表⽰常量名,第三个参数传⼊的是值。

  点击同步后在代码中就可以直接调⽤_HOST来使⽤了,因为当点击同步后,Gradle就会在BuildConfig这个类中加⼊常量API_HOST public final class BuildConfig { public static final boolean DEBUG = oolean("true"); public static final String APPLICATION_ID = ""; public static final String BUILD_TYPE = "debug"; public static final String FLAVOR = "360"; public static final int VERSION_CODE = 6; public static final String VERSION_NAME = "1.1.1"; // Fields from default config. public static final String API_HOST = ""; }可以看到BuildConfig这个类中的最后⼀⾏已经有了API_HOST这个常量了,还有⼀些其他的常量也是根据配置⾃动⽣成的,这⾥可以先不⽤管。

  现在可以通过代码请求到配置⽂件⾥⾯的配置了。 public static final String LOGIN_URL = _HOST + "/api/user/login";接下来要做的就是怎么执⾏不同的task就会引⽤不同的配置。

  ⽂件中有⼀个buildTypes,⾥⾯放的是你在build的时候需要选择的类型,默认有⼀个debug,也可以⾃⼰⾃定义,我在这⾥加了四种类型,debug(开发)、beta(测试)、preRelease(预发)、release(正式发布) buildTypes { /* 线上环境 */ release { // 不显⽰Log buildConfigField "boolean", "LOG_DEBUG", "false" buildConfigField "String", "API_HOST", "${API_RELEASE_HOST}"//API Host minifyEnabled true //是否混淆 //是否设置zip对齐优化 zipAlignEnabled true // 移除⽆⽤的resource⽂件 shrinkResources true //签名 signingConfig e proguardFiles getDefaultProguardFile(''), '' } /* 预发环境 */ preRelease { // 不显⽰Log buildConfigField "boolean", "LOG_DEBUG", "false" buildConfigField "String", "API_HOST", "${API_PRE_RELEASE_HOST}"//API Host minifyEnabled true //是否混淆 //是否设置zip对齐优化 zipAlignEnabled true // 移除⽆⽤的resource⽂件 shrinkResources true //签名 signingConfig e proguardFiles getDefaultProguardFile(''), '' } /* 本地开发环境 */ debug { minifyEnabled false } /* 测试环境 */ beta { // 显⽰Log buildConfigField "boolean", "LOG_DEBUG", "true" buildConfigField "String", "API_HOST", "${API_TEST_HOST}"//API Host minifyEnabled true //是否混淆 //是否设置zip对齐优化 zipAlignEnabled true // 移除⽆⽤的resource⽂件 shrinkResources true //签名 signingConfig e proguardFiles getDefaultProguardFile(''), '' } }可以看到,在每个buildType⾥⾯都有相应的配置,你打哪个类型的包,就会去读取哪个类型的配置,如果没有,默认会去读取defaultConfig⾥⾯的配置,defaultConfig⾥⾯相当于初始值,这样就做到了不同环境有了不同的配置,同步⼀下,再看⼀下AndroidStudio右侧的Gradle⾯板,可以发现多了⼀些Task,⾃动⽣成了⼀些Task,⽐如原先是assembleDebug,现在就多了assembleBeta、assemblePreRelease、assembleRelease,想要执⾏哪个环境就执⾏哪个任务就ok了。

  但是很多第三⽅的外部包配置不⽌在⽂件,还会在做⼀些正式环境和测试环境的区分,做⼀些不同的配置,这⾥的配置怎么处理?第⼆个问题:怎么将⾥⾯的配置在⾥⾯进⾏配置?  举个例⼦,我这⾥拿talkingData的配置来说,需要在⾥⾯指定APP_ID 包括百度地图、个推等其实很多第三⽅库都需要配置这些,在⾥⾯可以直接引⽤⽂件⾥⾯的配置,⾥⾯怎么配置我们⼀会再讲,先看看引⽤配置后代码: 这⾥使⽤了引⽤了⾥⾯的TALKING_DATA_APP_ID的值,我们再来看看⽂件⾥⾯怎么配置。def TEST_TALKING_DATA_APP_ID = "6E5389EAD0C2C2CFB7B379701F6D2BA8" defaultConfig { applicationId "" minSdkVersion 15 targetSdkVersion 23 versionCode 5 versionName 1.1.0 buildConfigField("String", "API_HOST", "${API_DEV_HOST}") manifestPlaceholders = [ /* talkingData 测试环境 */ TALKING_DATA_APP_ID: "${TEST_TALKING_DATA_APP_ID}" ] }我在defaultConfig⾥⾯指定了⼀个manifestPlaceholders属性,也是gradle默认就提供的⼀个属性,从形式可以看出是⼀个数组的形式,⾥⾯可以写多个键值对,⽤逗号隔开,会从manifestPlaceholders数组⾥⾯去寻找匹配的键,找到了就会引⽤这个键所对应的值。

  这样问题就迎刃⽽解了,所有的⾥⾯的配置都可以写在⾥⾯统⼀处理了。

  ⽽上⾯说过,defaultConfig是默认的配置,不同的buildType可以指定不同的配置,所以在不同的buildType,也可以理解为不同的环境⾥⾯配置不同的manifestPlaceholders就可以了。代码如下: buildTypes { /* 线上环境 */ release { // 不显⽰Log buildConfigField "boolean", "LOG_DEBUG", "false" buildConfigField "String", "API_HOST", "${API_RELEASE_HOST}"//API Host manifestPlaceholders = [ /* release 环境 */ GETUI_APP_ID : "${RELEASE_GETUI_APP_ID}", GETUI_APP_KEY : "${RELEASE_GETUI_APP_KEY}", GETUI_APP_SECRET : "${RELEASE_GETUI_APP_SECRET}", /* talkingData release 环境 */ TALKING_DATA_APP_ID: "${RELEASE_TALKING_DATA_APP_ID}", PACKAGE_NAME : ationId ] minifyEnabled true //是否混淆 //是否设置zip对齐优化 zipAlignEnabled true // 移除⽆⽤的resource⽂件 shrinkResources true //签名 signingConfig e proguardFiles getDefaultProguardFile(''), '' } /* 预发环境 */ preRelease { // 不显⽰Log buildConfigField "boolean", "LOG_DEBUG", "false" buildConfigField "String", "API_HOST", "${API_PRE_RELEASE_HOST}"//API Host manifestPlaceholders = [ /* release 环境 */ GETUI_APP_ID : "${RELEASE_GETUI_APP_ID}", GETUI_APP_KEY : "${RELEASE_GETUI_APP_KEY}", GETUI_APP_SECRET : "${RELEASE_GETUI_APP_SECRET}", /* talkingData release 环境 */ TALKING_DATA_APP_ID: "${RELEASE_TALKING_DATA_APP_ID}", PACKAGE_NAME : ationId ] minifyEnabled true //是否混淆 //是否设置zip对齐优化 zipAlignEnabled true // 移除⽆⽤的resource⽂件 shrinkResources true //签名 signingConfig e proguardFiles getDefaultProguardFile(''), '' } /* 本地开发环境 */ debug { minifyEnabled false } /* 测试环境 */ beta { // 显⽰Log buildConfigField "boolean", "LOG_DEBUG", "true" buildConfigField "String", "API_HOST", "${API_TEST_HOST}"//API Host manifestPlaceholders = [ /* 个推测试环境 */ GETUI_APP_ID : "${TEST_GETUI_APP_ID}", GETUI_APP_KEY : "${TEST_GETUI_APP_KEY}", GETUI_APP_SECRET : "${TEST_GETUI_APP_SECRET}", /* talkingData 测试环境 */ TALKING_DATA_APP_ID: "${TEST_TALKING_DATA_APP_ID}", PACKAGE_NAME : ationId ] minifyEnabled true //是否混淆 //是否设置zip对齐优化 zipAlignEnabled true // 移除⽆⽤的resource⽂件 shrinkResources true //签名 signingConfig e proguardFiles getDefaultProguardFile(''), '' } }我们已经可以执⾏不同的Task去打不同环境的包了。

  命令⾏⾥⾯可以使⽤gradle assembleBeta(assembleBeta表⽰测试环境,其他环境可以替换后⾯的名字,如assemblePreRelease、assembleRelease等,如果配置了渠道,还会在assemble后⾯拼上渠道名,例如我的渠道名是360,我要打release的包,那就是assemble360Release)。

  ide⾥⾯可以点击菜单上的build,再点击Generate Signed APK…,填完keystore密码后选择buildType进⾏打包操作。

  提醒:在打包前最好先做⼀下clean操作,否则会出现有些代码打包不进去,不知道其他⼈是不是这样的。第三个问题:怎么动态传参满⾜需求?很简单,直接上代码: defaultConfig { applicationId "" minSdkVersion 15 targetSdkVersion 23 //关键看这两⾏ versionCode perty('VERSION_CODE') ? nt(VERSION_CODE) : DEF_VERSION_CODE versionName perty('VERSION_NAME') ? VERSION_NAME : "${DEF_VERSION_NAME}" buildConfigField("String", "API_HOST", "${API_DEV_HOST}") }关键看versionCode 和versionName这两⾏,原先默认是直接在后边写上版本号和版本名,这⾥⽤了三⽬运算符,可以⽤perty(‘KEY’)来判断是否有KEY这个参数传⼊,如果有的话就就返回true,就会使⽤传⼊的值作为实际值,这⾥⽤了强转,将传⼊的String类型转为int类型的,如果没有就会返回false,使⽤默认的值。  同理,传⼊⽂件名和⽂件输出路径也⼀样。//修改⽣成的最终⽂件名 { variant -> { output -> def outputFile = File if (outputFile != null && th('.apk')) { //判断是否有这个OUT_PUT_DIR参数传⼊ File outputDirectory = new File(perty('OUT_PUT_DIR') ? OUT_PUT_DIR : ); def fileName if(!perty('FILE_NAME')){ if ( == "release" || == "preRelease") { // 输出apk名称为app_v1.0.0_2015-06-15_ fileName = "app_v${nName}_${releaseTime()}_${tFlavors[0].name}_${}.apk" } else if ( == "beta") { fileName = "app_v${nName}_${releaseTime()}_${}.apk" } else { fileName = } }else{ fileName = FILE_NAME }// println("输出apk ---> " + tePath + tor + ) File = new File(outputDirectory, fileName) } } }因为我这⾥配置了多个渠道,所以使⽤循环输出⽂件,在⾥⾯分别处理每⼀个包。

  在代码⾥⾯分别⽤了perty(‘OUT_PUT_DIR’)和perty(‘FILE_NAME’)来判断是否有这个参数,有⽆参数分别做了不同的处理。

  fileName = “app_v  万事俱备,只⽋东风了。到这⾥,基本所有都说完了,最后还有⼀个问题,哈哈,如何传参?第四个问题:如何传参? gradle clean assembleBeta -PVERSION_CODE=5 -PVERSION_NAME=1.1.1 -POUT_PUT_DIR=/home/user/Desktop -PFILE_NAME= 在命令⾏⾥⾯执⾏这个命令就可以打出所有的Beta包了(前提是已经安装好Gradle,并配置好Gradle的环境变量,或者使⽤IDE⾥⾯的terminal,在项项⽬⽬录下使⽤gradlew命令),其中assembleBeta 可以根据⾃⼰需求替换成其他的task名字。

  传参就是在后⾯加上 -P参数,-P后⾯再加上要传⼊的键值对,中间⽤=号连接,需要什么参数就传什么参数,如果有其他需要也可以⾃定义加⼊。最后附上源⽂件,可以点击此处下载以上是我的配置过程,如有问题欢迎留⾔,互相学习。

发布者:admin,转转请注明出处:http://www.yc00.com/news/1689202222a220305.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信