在开发中我们可能遇到各种需求,有时候公司为了渠道的推广或者制作马甲包,一套代码要打出多个App来。对于多个App的定义,首先applicationId不一样,可能App的名字也不一样,图标不一样,可能一些配置文件如服务地址,友盟的key等都不一样,但又确实是一套代码。为了不分出多的项目,避免后期更新麻烦,那么就需要我们类似多渠道配置进行配置。
假设:当前代码需要打出两个APP,APP的不同点个前言说的一样
首先看一下channel_config的目录情况,以及json的信息,下面可以看到jngi目录和yzxy目录下就只是放了icon,这是后面需要指定路径的,这里要注意,jngi这个目录要和channels文件中的名字对应,要和groupAll.josn 中的channel_name对应。还有,jngi和yzxy目录下的文件,res-style-blue和res-style-yellow下的目录要和app下面的目录文件位置和名字要保持一致
接下来我们看看groupAll.json的类容,这里定义了一个数组,channel_name是一个标识,用于在icons和channels目录下寻找资源。label代表这个是哪个App的配置,COLOR_STYLE表示这个App的主题颜色是什么
[
{
"channel_name": "yzxy",
"label": "反清",
"COLOR_STYLE": "blue"
},
{
"channel_name": "jngj",
"label": "复明",
"COLOR_STYLE": "yellow"
}
]
我们直接看AndroidManifest.xml文件,其中 tools:replace="android:icon,android:label,android:theme"
这个很关键,意思是:将低优先级清单中的指定属性替换为 此清单中的属性。 换言之,始终保持 高优先级清单的值,下面developer.android中讲的很清楚
合并多个清单文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.cs.demo">
<application
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon="${icon}"
android:label="${label}"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:icon,android:label,android:theme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
在 gradle.properties中添加 GROUP=All,当App打包很多的时候,打批包要很长时间,我们可以多创建几个groupAll.josn,中间放不容的App配置,那么就可以进行分批打包,比如groupType1,GROUP=Type1,进行指定
import groovy.json.JsonSlurper
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.cs.demo"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0.0"
resConfigs "zh"
// 设置MultiDex可用
multiDexEnabled true
flavorDimensions "versionCode"
javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
//配置默认的app图标和名字
manifestPlaceholders = [
label : "反清",
icon : "@drawable/yzxy"
]
}
buildTypes {
release {
//混淆
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
//混淆
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "String", "BASE_URL", "\"https://www.baidu.com\"" //配置debug的服务器地址
}
}
productFlavors {
//在 gradle.properties中添加 GROUP=All
//读取channel_config下json文件
def json = file("${rootProject.projectDir.path}/channel_config/group${GROUP}.json").getText("UTF-8")
//读取app目录下的channel.json文件内容
def flavors = new JsonSlurper().parseText(json) //转换成Jsons数组对象
flavors.each { flavor ->
//循环flavors数组
def fileName = flavor.channel_name
def channelJson = file("${rootProject.projectDir.path}/channel_config/channels/${fileName}.json").getText("UTF-8")
def qudao = new JsonSlurper().parseText(channelJson) //转换成Jsons数组对象
def INDEPENDENT_URL = flavor.INDEPENDENT_URL
"${flavor.channel_name}" {//渠道名称,对应文件中的channel_name
//单独服务地址
if ("Y".equals(INDEPENDENT_URL)) {
buildConfigField "String", "BASE_URL", "\"${qudao.BASE_URL}\"" //服务器地址
}
buildConfigField "String", "COLOR_STYLE", "\"${qudao.COLOR_STYLE}\"" //主题颜色
applicationId qudao.applicationId
buildConfigField "String", "APP_NAME", "\"${qudao.label}\"" //应用名称
manifestPlaceholders = [
icon : qudao.icon,//APP要显示的ICON图标,对应文件中的icon
label : qudao.label,
channel_name: flavor.channel_name
]
}
}
}
sourceSets {
def json = file("${rootProject.projectDir.path}/channel_config/group${GROUP}.json").getText("UTF-8")
//读取app目录下的channel.json文件内容
def flavors = new JsonSlurper().parseText(json) //转换成Jsons数组对象
flavors.each { flavor ->
def colorstyle = flavor.COLOR_STYLE
"${flavor.channel_name}" { //渠道资源配置
if (colorstyle.equals("blue")) {
//单独指定两个资源文件,一个替换res-style-blue下目录文件,一个替换icons下文件
res.srcDirs = ["src/res-style-blue", "${rootProject.projectDir.path}/channel_config/icons/${flavor.channel_name}"]
}
if (colorstyle.equals("yellow")) {
res.srcDirs = ["src/res-style-yellow", "${rootProject.projectDir.path}/channel_config/icons/${flavor.channel_name}"]
//指定资源目录
}
}
}
}
//修改apk名
applicationVariants.all { variant ->
variant.outputs.all { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('release.apk')) {
// 输出apk名称为xxx_v1.1.1_20171005-21:23:30.apk
def fileName = "${variant.flavorName}_v${variant.versionName}_${releaseTime()}_release.apk"
outputFileName = new File(fileName)
}
if (outputFile != null && outputFile.name.endsWith('debug.apk')) {
// 输出apk名称为xxx_v1.1.1_20171005-21:23:30.apk
def fileName = "${variant.flavorName}_v${variant.versionName}_${releaseTime()}_debug.apk"
outputFileName = new File(fileName)
}
}
}
}
def releaseTime() {
return new Date().format("yyyyMMdd_HH_mm_ss", TimeZone.getTimeZone("GMT+8:00"))
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
基本完成了上面的需要,Demo,不懂请留言