Gradle多渠道打包(动态设定App名称,应用图标,替换常量,更改包名,变更渠道)
最近有个需求一次要打包多个类型的App,而且常量和String.xml都有变量。虽然之前也是一直存在变量,但是每次也仅仅只打包一个。这让我每次改变量,打包9个。要是以后每次都打包9次,我得疯了。
根据之前的了解,gradle 应该是可以解决这个问题的。所以就仔细研究了一番。
gradle.properties:
// build_tools版本号
ANDROID_BUILD_TOOLS_VERSION=22.0.1
// build_dk版本号
ANDROID_BUILD_SDK_VERSION=22// version_name
VERSION_NAME=2.1
// version_code
VERSION_CODE=2100
// 包名
// PACKAGE= xxxx
// english version
PACKAGE= xxxx.en//minSdkVersion
ANDROID_BUILD_MIN_SDK_VERSION=15
//targetSdkVersion
ANDROID_BUILD_TARGET_SDK_VERSION=22
// grade_version
GRADLE = com.android.tools.build:gradle:1.5.0
build.gradle(app 工程目录下):
//声明是Android程序,和library区别
apply plugin: 'com.android.application'
// 因为项目中使用了retrolambda
apply plugin: 'me.tatarka.retrolambda'// 打包时间
def releaseTime() {return new Date().format("yyyyMMdd", TimeZone.getTimeZone("UTC"))
}android {
//编译SDK的版本compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)// build tools的版本buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSIONdefaultConfig {//应用的包名,定义在gradle.propertiesapplicationId project.PACKAGEminSdkVersion Integer.parseInt(ANDROID_BUILD_MIN_SDK_VERSION)targetSdkVersion Integer.parseInt(ANDROID_BUILD_TARGET_SDK_VERSION)versionCode Integer.parseInt(project.VERSION_CODE)versionName project.VERSION_NAME// dex突破65535的限制,为大程序而设multiDexEnabled true// AndroidManifest.xml 里面CHANNEL的value为 ${CHANNEL_VALUE}manifestPlaceholders = [CHANNEL_VALUE: "name"]}//执行lint检查,有任何的错误或者警告提示,都会终止构建,我们可以将其关掉。lintOptions {checkReleaseBuilds falseabortOnError false// 防止在发布的时候出现因MissingTranslation导致Build Failed!disable 'MissingTranslation'}dexOptions {incremental truejavaMaxHeapSize "8g"jumboMode = truepreDexLibraries = falsethreadCount ="8"}//签名signingConfigs {debug {}relealse {storeFile file('xxx.keystore')storePassword '***'keyAlias 'xxx'keyPassword '***'}}// 打包buildTypes {//debug 版本
// debug {// buildConfigField "int", "ENV", "0"
// minifyEnabled true
// zipAlignEnabled true
// shrinkResources true
// proguardFile 'proguard-rules.pro'
// signingConfig signingConfigs.relealse
// }
// pre {// buildConfigField "int", "ENV", "1"
// minifyEnabled false
// zipAlignEnabled false
// shrinkResources false
// signingConfig signingConfigs.relealse
// }
// online {// buildConfigField "int", "ENV", "2"
// minifyEnabled false
// zipAlignEnabled false
// shrinkResources false
// signingConfig signingConfigs.relealse
// }release {// 定义ENV变量,可以通过BuildConfig.ENV 来获取,进行一些接口环境变化的操作buildConfigField "int", "ENV", "2"//混淆minifyEnabled true//Zipalign优化zipAlignEnabled true// 移除无用的resource文件shrinkResources true//混淆配置文件proguardFile 'proguard-rules.pro'//签名signingConfig signingConfigs.relealse}}//渠道FlavorsproductFlavors {Market_Google_Play {}
// Market_xiaomi {}
// Market_wandoujia {}
// Market_Default {}
// Market_Amazon {}
// Market_yingyongbao {}
// Market_360 {}
// Market_baidu {}}// Java版本compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}// 替换manifest中定义的占位符productFlavors.all { flavor ->flavor.manifestPlaceholders = [CHANNEL_VALUE: name]}// apk输出重命名applicationVariants.all { variant ->variant.outputs.each { output ->// oldNamedef outputFile = output.outputFileif (outputFile != null && outputFile.name.endsWith('.apk')){// 获取中英文版本def isEn = variant.applicationId.endsWith('.en')def newName = ''def version= "-v${defaultConfig.versionName}"def time = "-${releaseTime()}.apk"def enName = 'appEN'def cnName = 'appCN'// release版本
// if(variant.buildType.name.equals('release')) {//
// // 获取渠道号
// def productFlavor = variant.productFlavors[0].name
//
// if (isEn) {// newName = enName + version + '-' + productFlavor + time
//
// } else {// newName = cnName + version + '-' + productFlavor + time
// }
// }else if (variant.buildType.name.equals('pre')){// if (isEn) {// newName = enName + version + '-pre-online' + time
//
// } else {// newName = cnName + version + '-pre-online' + time
// }
// }else if (variant.buildType.name.equals('online')){// if (isEn) {// newName = enName + version + '-online' + time
//
// } else {// newName = cnName + version + '-online' + time
// }
// }else{if (isEn) {newName = enName + version + '-debug' + time} else {newName = cnName + version + '-debug' + time}
// }output.outputFile = new File(outputFile.parent, newName)}}}
}retrolambda {javaVersion JavaVersion.VERSION_1_7
}dependencies {compile fileTree(include: ['*.jar'], dir: 'libs')testCompile 'junit:junit:4.12'compile 'com.android.support:appcompat-v7:22.2.1'compile 'com.android.support:design:22.2.1'compile 'com.android.support:recyclerview-v7:22.2.1'compile 'com.annimon:stream:1.0.1'compile project(':mylibrary')
}
- 最后会在app/build/outputs/apk下生成相应的文件
debug:name-version-debug-time.apk
release: name-version-productFlavors-time.apk
AndroidManifest.xml
<application>
<meta-data
android:name="UMENG_APPKEY"android:value="54d32a3afd98c511cf000629" /><meta-data
android:name="UMENG_CHANNEL"android:value="${CHANNEL_VALUE}" /></application>
先放一个完整的 多渠道/多环境 打包的配置,然后再来讲解。
实现了:
- 不同环境,不同包名;
- 不同环境,修改不同的 string.xml 资源文件;
- 不同环境,修改指定的常量;
- 不同环境,修改 AndroidManifest.xml 里渠道变量;
- 不同环境,引用不同的 module。
先放一个完整的配置,可以参考:
apply plugin: 'com.android.application'android {compileSdkVersion 22buildToolsVersion '22.0.1'// 签名文件signingConfigs {config {keyAlias 'lyl'keyPassword '123456'storeFile file('../lyl.jks')storePassword '123456'}}// 默认配置defaultConfig {//applicationId "com.lyl.app"minSdkVersion 16targetSdkVersion 22versionCode 1versionName "1.0.0"signingConfig signingConfigs.configmultiDexEnabled true}// 多渠道/多环境 的不同配置productFlavors {dev {// 每个环境包名不同applicationId "com.lyl.dev"// 动态添加 string.xml 字段;// 注意,这里是添加,在 string.xml 不能有这个字段,会重名!!!resValue "string", "app_name", "dev_myapp"resValue "bool", "isrRank", 'false'// 动态修改 常量 字段buildConfigField "String", "ENVIRONMENT", '"dev"'// 修改 AndroidManifest.xml 里渠道变量manifestPlaceholders = [UMENG_CHANNEL_VALUE: "dev"]}stage {applicationId "com.lyl.stage"resValue "string", "app_name", "stage_myapp"resValue "bool", "isrRank", 'true'buildConfigField "String", "ENVIRONMENT", '"stage"'manifestPlaceholders = [UMENG_CHANNEL_VALUE: "stage"]}prod {applicationId "com.lyl.prod"resValue "string", "app_name", "myapp"resValue "bool", "isrRank", 'true'buildConfigField "String", "ENVIRONMENT", '"prod"'manifestPlaceholders = [UMENG_CHANNEL_VALUE: "prod"]}}dexOptions {incremental true// javaMaxHeapSize "4g"}//移除lint检测的errorlintOptions {abortOnError false}def releaseTime() {return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))}buildTypes {debug {signingConfig signingConfigs.config}release {buildConfigField("boolean", "LOG_DEBUG", "false")minifyEnabled falsezipAlignEnabled true//移除无用的resource文件shrinkResources trueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'signingConfig signingConfigs.config// 批量打包applicationVariants.all { variant ->variant.outputs.each { output ->def outputFile = output.outputFileif (outputFile != null && outputFile.name.endsWith('.apk')) {//输出apk名称为:渠道名_版本名_时间.apkdef fileName = "${variant.productFlavors[0].name}_v${defaultConfig.versionName}_${releaseTime()}.apk"output.outputFile = new File(outputFile.parent, fileName)}}}}}
}repositories {mavenCentral()
}dependencies {compile 'com.facebook.android:facebook-android-sdk:4.0.0'compile project(':qrscan')compile 'com.android.support:appcompat-v7:22.0.0'compile 'com.google.code.gson:gson:2.3'compile files('libs/android-async-http-1.4.6.jar')compile 'com.google.android.gms:play-services:7.5.0'compile 'com.android.support:support-annotations:22.1.1'compile 'com.github.bumptech.glide:glide:3.7.0'compile 'de.hdodenhof:circleimageview:2.1.0'
}
接下来我们来详细看看修改特定的字段。
不同环境的设置基本都是在 productFlavors 里设置的,
而且在里面你想添加多少个环境都可以。
1. 不同环境,不同包名;
productFlavors {dev {applicationId "com.lyl.dev"}stage {applicationId "com.lyl.stage"}prod {applicationId "com.lyl.prod"}
}
这里注意,在 defaultConfig 中,大家应该都是写了个默认的 applicationId 的。
经测试,productFlavors 设置的不同环境包名会覆盖 defaultConfig 里面的设置,
所以我们可以推测,它执行的顺序应该是先执行默认的,然后在执行分渠道的,如果冲突,会覆盖处理,这也很符合逻辑。
<br />
2. 不同环境,添加 string.xml 资源文件;
利用 resValue 来定义资源的值,顾名思义 res 底下的内容应该都可以创建,最后用 R.xxx.xxx 来引用。
如下就根据不同的类型,添加了不同的 app_name 字段,以及定义了 布尔值,可以通过 R.string.app_name 来引用。
注意,这里是添加,是在 string.xml 里面添加了一个字段app_name,所以在现有的 string.xml 中不能有这个字段,否则会报错!!!
productFlavors {dev {resValue "string", "app_name", "dev_myapp"resValue "bool", "isrRank", 'false'}stage {resValue "string", "app_name", "stage_myapp"resValue "bool", "isrRank", 'true'}prod {resValue "string", "app_name", "myapp"resValue "bool", "isrRank", 'true'}
}
通过以上我们大概可以推测出 color、dimen 也可以通过类似的方法添加。
<br />
3. 不同环境,动态修改指定的常量;
使用 BuildConfig 的变量。
①定义字段
当我们定义如下字段之后,编译后自动生成文件,在 app/build/source/BuildConfig/dev/com.lyl.dev/BuildConfig 目录,
打开这个文件,我们就能看到我们所定义的字段了。
productFlavors {dev {buildConfigField "String", "ENVIRONMENT", '"dev"'}stage {buildConfigField "String", "ENVIRONMENT", '"stage"'}prod {buildConfigField "String", "ENVIRONMENT", '"prod"'}
}
②引用字段
在我们自己的任意的类中,来直接通过 BuildConfig 就可以调用我们定义的字段。
public class Constants {public static final String ENVIRONMENT = BuildConfig.ENVIRONMENT;}
注意:这里有个小细节,看其中第三个参数,是先用了“'”,然后在用了“"”,这种语法在 Java 里可能比较陌生,但是在很多其他语言中,这种用法是很常见的。
它的意思是 "dev" 这个整体是属于一个字符串,至于为什么要这么写,你把单引号去掉,然后去 app/build/source/BuildConfig/dev/com.lyl.dev/BuildConfig 这个文件看一看就知道了。
<br />
4. 不同环境,修改 AndroidManifest.xml 里渠道变量
①在 AndroidManifest.xml 里添加渠道变量
<applicationandroid:icon="${app_icon}"android:label="@string/app_name"android:theme="@style/AppTheme">...<meta-dataandroid:name="UMENG_CHANNEL"android:value="${ENVIRONMENT}" />...
</application>
②在 build.gradle 设置 productFlavors
productFlavors {dev {manifestPlaceholders = [ENVIRONMENT: "dev",app_icon : "@drawable/icon_dev"]}stage {manifestPlaceholders = [ENVIRONMENT: "stage",app_icon : "@drawable/icon_stage"]}prod {manifestPlaceholders = [ENVIRONMENT: "prod",app_icon : "@drawable/icon_prod"]}
}
这样我们可以在不同环境使用不同的 key 值。
<br />
5. 不同环境,引用不同的 module
这个就很强大了,根据不同的环境,引用对应的 module。
你可以替换大量的图片,string,color,vaule等等。
首先,要建立跟渠道对应的 module,然后再引用。
引用方式如下:
dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])// 引用本的项目devCompile project(':devModule')stageCompile project(':stageModule')prodCompile project(':prodModule')// 也可以分渠道引用网络的。因为这里都相同,所以地址也就都一样了devCompile 'com.roughike:bottom-bar:2.0.2'stageCompile 'com.roughike:bottom-bar:2.0.2'prodCompile 'com.roughike:bottom-bar:2.0.2'
}
xxxCompile 代表 各个渠道的名称。
然后把需要分渠道的文件,放到不同的 module 里面,把主项目的文件删掉。
千万注意:如果这样做了,每次需要引用的时候,在各个渠道的 module 里面都必须要放置文件哦,不然会找不到资源。
通过这种方式可以替换整套素材资源,具体如何使用还得看项目需求。
<br />
<br />
通过以上方式,我们基本可以 通过 gradle 动态设定应用标题,应用图标,替换常量,设置不同包名,更改渠道等等。
打包编译
最后,做完所有的配置之后,然后就是打包操作了。
打包某一个(日常编译)
因为 buildTypes 里面有两种,所以每个渠道都会有两种模式。
打包所有的,就是正常打包流程
如图所示:
<br />
打包完成之后,然后就可以在我们指定的目录下,看到我们所生成的apk包。
使用 local.properties 存放私密配置
以上就可以基本实现 gradle 的设置,但是如果我们要将我们的项目上传到 Github ,或者要将项目发送给别人。上面有些私密的东西就会被别人看到。比如:.jks 文件的密码。
在项目跟目录下,有个 local.properties 文件,我们可以使用它来存放一些私密的属性,然后在 gradle 中读取,而 local.properties 文件不需要上传。
local.properties 文件里设置如下:
sdk.dir=D\:\\Android\\android-sdkgaodeKey=e348025dd034d1c347dd0345e34802keyPassword=123456
在 build.gradle 读取 local.properties 字段信息
// 加载 local.properties 资源
Properties properties = new Properties()
InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream() ;
properties.load( inputStream )android {...// 签名文件signingConfigs {config {keyAlias 'lyl'// 获取 local.properties 字段信息keyPassword properties.getProperty( 'keyPassword' )storeFile file('../lyl.jks')storePassword properties.getProperty( 'keyPassword' )}}...
}
这样就可以将自己想要隐藏的一些数据隐藏起来。
可能加快 Android Studio 编译的办法
1. 在根目录的 build.gradle 里加上如下代码:
allprojects {// 加上这个tasks.withType(JavaCompile) {//使在一个单独的守护进程编译options.fork = true//增量编译options.incremental = true}repositories {jcenter()}
}
2. 在 app 级别下 build.gradle 里 加上
android {dexOptions {incremental true}
}
最后放上一个多渠道的项目地址,可以参考:
https://github.com/Wing-Li/boon
链接:https://www.jianshu.com/p/533240d222d3
Gradle多渠道打包(动态设定App名称,应用图标,替换常量,更改包名,变更渠道)相关推荐
- 厦门之旅第一篇Gradle多渠道打包(动态设定App名称,应用图标,背景图片,状态栏颜色)
我不是诗人,写不出厦门的美:我不是歌手,唱不出厦门的情:我不是画家,画不出厦门的景. 我只是一名程序员,我只能用我的眼,我的心去看去感受,那久违的海风吹拂着脸颊,好似内心那一份烦躁与沉重也随着海风飘向 ...
- android 动态更改包名,Gradle多渠道打包(动态设定App名称,应用图标,替换常量,更改包名,变更渠道)...
// 多渠道/多环境 的不同配置 productFlavors { dev { // 每个环境包名不同 applicationId "com.lyl.dev" // 动态添加 st ...
- Gradle编译生成不同的版本,动态设定应用标题 / 应用图标 / 替换常量
转载请标明出处:http://blog.csdn.net/xx326664162/article/details/51508132 文章出自:薛瑄的博客 你也可以查看我的其他同类文章,也会让你有一定的 ...
- Android Gradle 多渠道打包、动态配置AppName
目录 一.简介 二.Gradle多渠道打包 1.普通做法 2.Gradle多渠道打包 一.简介 因为国内Android应用分发市场的现状,我们在发布APP时,一般需要生成多个渠道包,上传到不同的应用市 ...
- android开发 Gradle多渠道打包以及集成360加固
先上图 先说实现的目的 主要是 用脚本做渠道打包 然后在APP里面添加数据 在启动时读取数据上传服务器做统计 然后为啥要这么做:第一个是因为APP里面在做渠道包的时候 需要加特定的东西,区别这些APP ...
- [转]Android Studio系列教程六--Gradle多渠道打包
转自:http://www.stormzhang.com/devtools/2015/01/15/android-studio-tutorial6/ Android Studio系列教程六--Grad ...
- Android 多渠道打包实操(更改包名、图标、主题资源 、替换常量、第三方SDK Appkey配置)
最近在做一个通用版的会员系统,给予不同的公司使用,先前是通过切换版本分支来管理的,后面发现实在是繁琐和痛苦管理,仅仅是需要更改不同的常量.主题资源.包名.图标等等,主体代码逻辑功能基本不变. 先前了解 ...
- Xcode更改包名和APP名称
在公司实习的第一个月,从前端页面的实现到iOS的打包上传,都自己在尝试,因为之前同事离职的比较早,这些任务就落到了我实习生的手上,由于是公司项目,所以一切都 只有靠自己,分享一波自己在打包更改包名的实 ...
- Android 判断颜色为深颜色还是浅颜色来动态调整app文字和图标颜色
Android 判断颜色为深颜色还是浅颜色来动态调整app文字和图标颜色 前言 解决 方案1 方案2 完事 前言 现在,项目中有个需求就是 app 的顶部 title bar 的颜色能根据背景图片动态 ...
最新文章
- Android WiFi热点完全研究(自定义创建、跳转系统界面设置、读取配置、切换,Android6.0适配)...
- jQuery实现父窗口的问题
- LeetCode 86. 分隔链表 中等难度
- 微信小程序(六) 文章详情静态页面detail
- Windows未能启动,原因可能是最近更改了硬件或软件,解决此问题的步骤
- volatile的学习总结
- Spring Boot 应用的测试
- `find -name`模式匹配多个模式
- 洛谷 题解 P2117 【小Z的矩阵】
- 计算机系统结构自考知识点总结,自考《计算机系统结构》问答题总结(3)
- 动态规划:完全背包问题
- Python算24点
- Shapely——基础操作汇总
- linux安装源文件出现错误,编译安装源码时出现错误的解决方法
- LeetCode力扣刷题——巧解数学问题
- STM32F103驱动HCSR04超声波测距显示
- java课题背景,办公自动化系统论文-课题研究的背景和意义及国内外发展状况.doc...
- HTML5七夕520情人节表白网页❤ 属于我们的浪漫星空 ❤ HTML+CSS+JavaScript
- 3d打印热床的PEI/玻璃/晶格玻璃/柔性平台/弹簧钢板如何选择
- 职场员工有没有潜力,看这一个能力就够了
热门文章
- [人脸活体检测] 人脸活体检测简介
- 【VB】机房收费系统(结账)
- 出现Cannot refer to the non-final local variable guoDeGang defined in an enclosing scope
- 第三章平面坐标系之地图投影
- CSS3动画帧数科学计算法http://tid.tenpay.com/?p=5983
- bzoj_2676_Contra
- 菜鸟教程:Python练习实例1~10
- 江西伟人系列第三篇:唐宋八大家(欧阳修)
- IDEA git配置
- 布朗的计算机排名,布朗计算机工程硕士排名,千万好好考查