场景:
在项目开发过程中,经常会有需要打包不同版本的 APK 的需求。 比如 debug版,release版,dev版等等。 有时候不同的版本中使用到的不同的服务端api域名也不相同。 比如 debug_api.com,release_api.com,dev_api.com等等。
多个分支版本之间,通常有以下不同:
- 不同版本之间大部分代码相同,如果分成多个工程,修改一处功能就要多2个
- 工程同时修改,不便于管理及多人协作。
- 不同版本的UI样式不同,包括颜色、图标等资源、部分页面的代码等。
- 不同版本的manifest元素有不同,比如部分key值等。
- 不同版本包名不同。
解决方案:
Android Studio中使用Gradle编译多种apk包依靠productFlavors功能实现。
具体方式如下:
在 Android Studio 中打开 build.gradle(Module中)的 android 节点下添加对应代码。
操作步骤
1. 准备好原始工程,在对应Module的build.gradle中定义productFlavors。如:
buildTypes {
//这里的名字自定义,不要求大小写
release{
// 这里是在 applicationId 中添加了一个后缀。所以『.』要加上
applicationIdSuffix ".release"
// 这里的作用是选择是否混淆代码
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
dev{
applicationIdSuffix ".dev"
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// 这里是为了不同过的版本设置一些特殊的参数,并不直接和 buildType 关联。
//例如:使用 buildType 中的 dev 版本,也可以使用 flavors_release 里面设置的自定义参数。这需要自己按照需求制定。
productFlavors{
//自定义名字不过不能和上面buildType中相同,不然Gradle编译会不通过。在这里使用了『flavors_』前缀以便区分。
flavors_release{
// manifestPlaceholders中写到的『str』,『package_name』不支持用大写,否则Gradle编译会不通过。
// 这里所设置的变量可以直接使用在『AndroidManifest.xml』中,使用方式为:${package_name}
// android:label="${package_name}"
applicationId "com.mvptest.release"
manifestPlaceholders = [str:"releaseStr",package_name:"com.mvptest.release",jpush_key:"123456789"]
// 这里的参数是为了在 java 代码中使用,具体的使用方式为:context.getResources().getString(R.string.strKey);
resValue("string" , "strKey","releaseStrValue")
versionCode 1
versionName "1.0.0"
}
flavors_dev{
applicationId "com.mvptest.dev"
manifestPlaceholders = [str:"devStr",package_name:"com.mvptest.dev",jpush_key:"987654321"]
resValue("string" , "strKey","devStrValue")
versionCode 2
versionName "2.0.0"
}
}
在这个主工程下面定义了两个分支版本,flavors_release和flavors_dev,包名分别为com.mvptest.release和com.mvptest.dev。还可以为其指定不同的版本号及proguardFiles等。其他没有特别指定的值,会默认为主工程defaultConfig中的值,相当于在productFlavors中重写了主工程的默认值,类似于Java中的Override。
2. 在src目录下新建相对应的文件夹存放每个分支与主版本之间不同的代码及资源。
3. 分支版本的目录需要和main的目录保持一致。
flavors_release和flavors_dev中的MainActivity代码不同,所以从main中抽离出来。TestActivity1和TestActivity2是不同版本之间一样的,所以仍然放在main中。两个版本的ic_launcher启动图标不同,所以都覆盖了main中的ic_launcher。需要注意的是,java文件夹中的文件,在分支版本和main中不能同时存在;而res中可以同时存在,分支会覆盖mian中的文件。
4. manifest中的元素并不是覆盖的关系,而是合并的关系。
示例中,flavors_dev的manifest并没有对main的manifest中的application元素节点进行复写,假如在flavors_dev的application元素添加 android:supportsRtl=”false”就会和main中的android:supportsRtl=”true”的冲突。另外flavors_dev针对TestActivity1添加了android:screenOrientation=”portrait”属性,则会和main中的publicActivity的属性进行合并。