最近有个需求一次要打包多个类型的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>

先放一个完整的 多渠道/多环境 打包的配置,然后再来讲解。

实现了:

  1. 不同环境,不同包名;
  2. 不同环境,修改不同的 string.xml 资源文件;
  3. 不同环境,修改指定的常量;
  4. 不同环境,修改 AndroidManifest.xml 里渠道变量;
  5. 不同环境,引用不同的 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名称,应用图标,替换常量,更改包名,变更渠道)相关推荐

  1. 厦门之旅第一篇Gradle多渠道打包(动态设定App名称,应用图标,背景图片,状态栏颜色)

    我不是诗人,写不出厦门的美:我不是歌手,唱不出厦门的情:我不是画家,画不出厦门的景. 我只是一名程序员,我只能用我的眼,我的心去看去感受,那久违的海风吹拂着脸颊,好似内心那一份烦躁与沉重也随着海风飘向 ...

  2. android 动态更改包名,Gradle多渠道打包(动态设定App名称,应用图标,替换常量,更改包名,变更渠道)...

    // 多渠道/多环境 的不同配置 productFlavors { dev { // 每个环境包名不同 applicationId "com.lyl.dev" // 动态添加 st ...

  3. Gradle编译生成不同的版本,动态设定应用标题 / 应用图标 / 替换常量

    转载请标明出处:http://blog.csdn.net/xx326664162/article/details/51508132 文章出自:薛瑄的博客 你也可以查看我的其他同类文章,也会让你有一定的 ...

  4. Android Gradle 多渠道打包、动态配置AppName

    目录 一.简介 二.Gradle多渠道打包 1.普通做法 2.Gradle多渠道打包 一.简介 因为国内Android应用分发市场的现状,我们在发布APP时,一般需要生成多个渠道包,上传到不同的应用市 ...

  5. android开发 Gradle多渠道打包以及集成360加固

    先上图 先说实现的目的 主要是 用脚本做渠道打包 然后在APP里面添加数据 在启动时读取数据上传服务器做统计 然后为啥要这么做:第一个是因为APP里面在做渠道包的时候 需要加特定的东西,区别这些APP ...

  6. [转]Android Studio系列教程六--Gradle多渠道打包

    转自:http://www.stormzhang.com/devtools/2015/01/15/android-studio-tutorial6/ Android Studio系列教程六--Grad ...

  7. Android 多渠道打包实操(更改包名、图标、主题资源 、替换常量、第三方SDK Appkey配置)

    最近在做一个通用版的会员系统,给予不同的公司使用,先前是通过切换版本分支来管理的,后面发现实在是繁琐和痛苦管理,仅仅是需要更改不同的常量.主题资源.包名.图标等等,主体代码逻辑功能基本不变. 先前了解 ...

  8. Xcode更改包名和APP名称

    在公司实习的第一个月,从前端页面的实现到iOS的打包上传,都自己在尝试,因为之前同事离职的比较早,这些任务就落到了我实习生的手上,由于是公司项目,所以一切都 只有靠自己,分享一波自己在打包更改包名的实 ...

  9. Android 判断颜色为深颜色还是浅颜色来动态调整app文字和图标颜色

    Android 判断颜色为深颜色还是浅颜色来动态调整app文字和图标颜色 前言 解决 方案1 方案2 完事 前言 现在,项目中有个需求就是 app 的顶部 title bar 的颜色能根据背景图片动态 ...

最新文章

  1. Android WiFi热点完全研究(自定义创建、跳转系统界面设置、读取配置、切换,Android6.0适配)...
  2. jQuery实现父窗口的问题
  3. LeetCode 86. 分隔链表 中等难度
  4. 微信小程序(六) 文章详情静态页面detail
  5. Windows未能启动,原因可能是最近更改了硬件或软件,解决此问题的步骤
  6. volatile的学习总结
  7. Spring Boot 应用的测试
  8. `find -name`模式匹配多个模式
  9. 洛谷 题解 P2117 【小Z的矩阵】
  10. 计算机系统结构自考知识点总结,自考《计算机系统结构》问答题总结(3)
  11. 动态规划:完全背包问题
  12. Python算24点
  13. Shapely——基础操作汇总
  14. linux安装源文件出现错误,编译安装源码时出现错误的解决方法
  15. LeetCode力扣刷题——巧解数学问题
  16. STM32F103驱动HCSR04超声波测距显示
  17. java课题背景,办公自动化系统论文-课题研究的背景和意义及国内外发展状况.doc...
  18. HTML5七夕520情人节表白网页❤ 属于我们的浪漫星空 ❤ HTML+CSS+JavaScript
  19. 3d打印热床的PEI/玻璃/晶格玻璃/柔性平台/弹簧钢板如何选择
  20. 职场员工有没有潜力,看这一个能力就够了

热门文章

  1. [人脸活体检测] 人脸活体检测简介
  2. 【VB】机房收费系统(结账)
  3. 出现Cannot refer to the non-final local variable guoDeGang defined in an enclosing scope
  4. 第三章平面坐标系之地图投影
  5. CSS3动画帧数科学计算法http://tid.tenpay.com/?p=5983
  6. bzoj_2676_Contra
  7. 菜鸟教程:Python练习实例1~10
  8. 江西伟人系列第三篇:唐宋八大家(欧阳修)
  9. IDEA git配置
  10. 布朗的计算机排名,布朗计算机工程硕士排名,千万好好考查