Android开发-多环境打包

前言

在 Android 开发中,多环境打包是常见需求(如开发、测试、生产环境),可通过 Gradle 配置实现环境隔离,避免手动修改参数。

以下是常用实现方案:

示例

app/build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
android {
signingConfigs {
release {
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_STORE_PASSWORD
}
debug {
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_STORE_PASSWORD
}
}

buildTypes {
release {
minifyEnabled false
shrinkResources false //移除无用的resourse文件
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
multiDexEnabled true //dex 分包
zipAlignEnabled true //zipAlign优化
aaptOptions.cruncherEnabled = false
aaptOptions.useNewCruncher = false
signingConfig signingConfigs.release
resValue "bool", "IS_DEBUG", "false"
}
debug {
signingConfig signingConfigs.debug
debuggable true
resValue "bool", "IS_DEBUG", "true"
}
}

// 多厂商配置
flavorDimensions "app"
productFlavors {
xhly {
resValue "string", "APP_CODE", "xhly"
resValue "bool", "IS_NEW_URL", "true"
manifestPlaceholders = [
APP_ICON : "ic_launcher",
APP_NAME : "应用名",
HOME_DEFAULT: "HOME"
]
}
}
}

其中变量如RELEASE_KEY_ALIAS在项目根目录gradle.properties中定义

1
2
3
4
RELEASE_KEY_PASSWORD=psvmc
RELEASE_KEY_ALIAS=psvmc
RELEASE_STORE_PASSWORD=psvmc
RELEASE_STORE_FILE=psvmc.keystore

变量获取

productFlavors中通过resValue定义资源,覆盖main中的值:

resValue定义的变量

代码中获取

1
2
3
public static boolean isDebug = MyApp.instance.getResources().getBoolean(R.bool.IS_DEBUG);

public static String appCode = MyApp.instance.getResources().getString(R.bool.APP_CODE);

manifestPlaceholders中定义的变量

在AndroidManifest.xml中可以这样获取

1
<category android:name="android.intent.category.${HOME_DEFAULT}" />

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<application
android:name=".MyApp"
android:allowBackup="true"
android:icon="@mipmap/${APP_ICON}"
android:label="${APP_NAME}"
android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
tools:remove="android:appComponentFactory"
tools:targetApi="n">
</application>

定义多维度flavorDimensions

配置说明

  1. 声明维度

app/build.gradle 中,通过 flavorDimensions 定义维度名称(顺序会影响变种名称的生成):

1
2
3
4
android {
// 定义两个维度:先「环境」,后「渠道」
flavorDimensions "environment", "channel"
}

如果是单维度则不用通过 dimension 指定所属维度。

  1. 为每个维度定义具体风味

productFlavors 中,为每个维度声明具体的风味值,并通过 dimension 指定所属维度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
productFlavors {
// 「环境」维度的具体风味
dev {
dimension "environment" // 属于「环境」维度
buildConfigField "String", "BASE_URL", "\"https://dev.example.com\""
}
test {
dimension "environment"
buildConfigField "String", "BASE_URL", "\"https://test.example.com\""
}
prod {
dimension "environment"
buildConfigField "String", "BASE_URL", "\"https://prod.example.com\""
}

// 「渠道」维度的具体风味
huawei {
dimension "channel" // 属于「渠道」维度
manifestPlaceholders = [CHANNEL_NAME: "huawei"]
}
xiaomi {
dimension "channel"
manifestPlaceholders = [CHANNEL_NAME: "xiaomi"]
}
google {
dimension "channel"
manifestPlaceholders = [CHANNEL_NAME: "google"]
}
}
  1. 自动生成的变种组合

Gradle 会根据维度和风味的所有可能组合,生成打包变种(结合 buildTypesdebug/release)。

例如:

  • devHuaweiDebug(开发环境 + 华为渠道 + debug 构建)

  • testXiaomiRelease(测试环境 + 小米渠道 + release 构建)

  • prodGoogleRelease(生产环境 + 谷歌渠道 + release 构建)

    3(环境)× 3(渠道)× 2(构建类型)= 18种变种。

默认环境

在 Android 开发调试时,默认生效的环境(产品风味)取决于你的 Gradle 配置和 Android Studio 中选中的「构建变体(Build Variant)」。

核心规则:默认由「当前选中的构建变体」决定

开发调试时(点击 Run 按钮运行 app),生效的环境是 Android Studio 中当前选中的 Build Variant 对应的产品风味(Product Flavor)。

  • Build Variant 是「产品风味(Flavor)」+「构建类型(Build Type,如 debug/release)」的组合,例如 devDebugprodDebug 等。
  • 默认情况下,Android Studio 会选中第一个声明的产品风味 + debug 构建类型的组合,但这个「默认选中项」可手动修改。

单维度风味(如仅区分环境)

若配置如下:

1
2
3
4
5
6
7
8
9
10
flavorDimensions "environment"
productFlavors {
dev { dimension "environment" } // 第一个声明
test { dimension "environment" }
prod { dimension "environment" }
}
buildTypes {
debug {}
release {}
}

初始默认选中的 Build Variant 是 devDebug,因此调试时默认生效 dev 环境

多维度风味(如环境 + 渠道)

若配置如下:

1
2
3
4
5
6
7
8
9
flavorDimensions "environment", "channel"
productFlavors {
// 环境维度(第一个维度)
dev { dimension "environment" } // 第一个声明
prod { dimension "environment" }
// 渠道维度(第二个维度)
huawei { dimension "channel" } // 第一个声明
xiaomi { dimension "channel" }
}

初始默认选中的 Build Variant 是「第一个环境风味 + 第一个渠道风味 + debug」,即 devHuaweiDebug,因此调试时默认生效 dev 环境 + huawei 渠道

环境资源文件隔离

如果需要不同环境使用不同的资源(如图片、字符串、Manifest 配置、layout),可通过资源目录隔离实现。

创建环境专属资源目录

app/src下创建与productFlavors同名的目录,放置对应资源:

有替换资源的时候才需要创建对应文件夹,如果不替换不用创建。

1
2
3
4
5
6
7
8
9
10
app/
├─ src/
│ ├─ dev/ // 开发环境资源
│ │ ├─ res/
│ │ │ ├─ drawable/ic_logo.png // 开发环境logo
│ │ │ └─ values/strings.xml // 开发环境字符串
│ │ └─ AndroidManifest.xml // 开发环境Manifest(如权限、组件)
│ ├─ test/ // 测试环境资源(结构同上)
│ ├─ prod/ // 生产环境资源(结构同上)
│ └─ main/ // 公共资源(所有环境共享)

资源优先级

  • 环境专属目录的资源会覆盖main目录中同名资源。
  • 例如:dev/res/values/strings.xml中的app_name会覆盖main中的同名值。

打包apk名称设置

1
2
3
4
5
6
7
android {
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "App-${variant.name}-${variant.versionName}-${variant.versionCode}-${releaseTime()}.apk"
}
}
}