目录
一、前言
二、我们需要解决的问题
1、所要达到的效果
2、需要解决的问题
三、编码时刻
四、效果演示
五、写在最后
一、前言
经过前几篇 安卓gradle 文章的介绍,童鞋们应该对 安卓gradle 更加熟悉了。
3、flavorDimensions和productFlavors——安卓gradle
这几篇文章中,我们或多或少的提到 “渠道包” 和 “风味包” ,今天我们就来分享下,如何机遇一套代码,编译出多个 “可以并存” 且 “存在有些许差异” 的apk包。
完整代码:github传送,如果对你有帮助,给个star吧。
二、我们需要解决的问题
我们先理清楚做这件事情所要达到的效果 和 中间存在的问题。
1、所要达到的效果
- 一套核心代码编译出多个 “可以并存” 的apk包
- 可以有差异化,eg:logo、app名字、签名、统计渠道 等可以根据不同的apk有些许不同
- 易维护,耦合度低
2、需要解决的问题
根据我们需要想要达到的效果,我们需要解决以下几个问题
- 能够配置多个 applicationId
- 能够动态的设置 AndroidManifest.xml 中的数据
- 能够使用不同的资源,但又不污染核心代码
- 能够实现差异化逻辑
接下来我们就来解决这些问题,达到我们预期的效果
三、编码时刻
1、建立 维度 和 风味
对 维度 和 风味 陌生的童鞋,可以移步查看小盆友的另一片博文:flavorDimensions和productFlavors——安卓gradle
我们进入应用级的 build.gradle 中,增加以下代码
android {
// 省略其他代码...
// 创建风味维度
flavorDimensions('abi')
productFlavors {
x86 {
// 创建维度
dimension 'abi'
}
armV7 {
dimension 'abi'
}
}
}
至此,我们可以编译出两个apk包:“x86” 和 “armV7”。从编译器的提示,我们也可以看出已经有 四种变体。
因为 “x86” 和 “armV7” 两个风味各自都默认有 “release” 和 “debug” 两种编译类型,所以 2x2 则有 四种变体。
2、让两种风味并存
我们需要给他们各自定一个 applicationId ,这样才可以并存不冲突。
android {
// 省略其他代码...
// 创建风味维度
flavorDimensions('abi')
productFlavors {
x86 {
// 创建维度
dimension 'abi'
// 配置 风味的applicationId
applicationId 'com.zinc.bear'
}
armV7 {
dimension 'abi'
applicationId 'com.zinc.shark'
}
}
}
这样第一个问题解决了!!😊很简单吧,继续前行。 但此时运行起来,是两个完全相同的apk,还没进行差异化的配置。
3、动态的设置 AndroidManifest.xml 中的数据
通过使用 manifestPlaceholders 达到这一效果
首先还是在 build.gradle 中添加以下代码
productFlavors {
x86 {
dimension 'abi'
applicationId 'com.zinc.bear'
manifestPlaceholders = [
hostName: "www.x86.com",
logo : "@drawable/logo",
appName : "bear",
]
}
armV7 {
dimension 'abi'
applicationId 'com.zinc.shark'
manifestPlaceholders = [
hostName: "www.armv7.com",
logo : "@drawable/logo",
appName : "shark",
]
}
}
接着我们进入 AndroidManifest.xml 中,进行替换 logo 和 app名字,还有设置了一个 meta-data 的参数。
我们在 AndroidManifest.xml 中使用 manifestPlaceholders 的参数规则为 ${参数名字},具体完整代码如下
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zinc.flavordemo">
<application
android:allowBackup="true"
android:icon="${logo}"
android:label="${appName}"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data android:name="host" android:value="${hostName}"/>
</application>
</manifest>
我们选择 “Merged Manifest”(此时为 armV7Debug 变体),可以看到 Android Studio 已经将我们设置的参数替换为真实的参数。
至此,我们解决了第二个问题 “能够动态的设置 AndroidManifest.xml 中的数据”。
3、使用不同的资源
不同的apk,当然会出现 同个地方,使用同样的资源名,但又要表现不同(emmm,很绕😂),我们举个例子,细心的童鞋会发现我们 logo 的资源名是一样的,我们接下来看看如何配置。
这里的 配置有两种方法 ,小盆友就自作主张的起两个名字: “文件夹配置”、“Library 配置”。我们接下来一一展示。
3.1 文件夹配置
(1)我们在跟 main 同级 的地方建立和 风味一样名字 的文件夹,这里使用 "armV7" 作为例子,则命名为 armV7。然后按照和 main 文件夹同样的格式建立结构,具体如下图。
(2)最后将我们的 logo.png 图片放入 drawable 中即可。在编译包时,会将armV7 文件夹中的资源覆盖在 main 中同名的资源。
(3)这种方式也同样适用于其他资源。例如:此处我们也同样建立了 strings.xml 文件,其中也写入 app_name 的 string资源,最终编译 armv7 时,形成的 app_name 则会为 flavor_armv7。
armV7 下的 strings.xml 资源
main 下的 strings.xml 资源
3.2 Library 配置
(1)创建一个library,名称没有规定,这里取名为 “flavor_x86”。
(2)在 drawable 同样放入 logo.png 的图片,结构如下。
(3)最后在我们项目级的 build.gradle 中加入如下代码,将其引入。注意此处不是使用 implementation, 而是使用 [风味名称]+Implementation,这样引入的 library 只会作用于该风味,例如此处只会作用于 x86 风味。
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
// ...其他引入
// 为风味 x86 引入 library flavor_x86
x86Implementation project(':flavor_x86')
}
(4)其他资源也同样适用,和 “文件夹配置” 的第三小点是一样的,就不赘述了。
3.3 两种方法的比较
小盆友个人比较喜欢 “Library 配置” 的方法,在真实的项目中也是使用这方案。
主要考虑到如下两点:
- 真正的解耦,因为 “文件夹配置” 这一方案在小盆友看来,其实和 “main”文件夹(核心代码)还是有共用一些资源,例如 build.gradle。
- 易去除,“Library 配置” 方案在移除或更换依赖的 Library 时,非常简单,只需要更改
[风味名]Implementation所引入的 Library。
4、能够实现差异化逻辑
聪明的童鞋其实已经发现,我们在第三小点中,有存在一个 “java” 的文件夹,我们只需要保持每个风味都存在相同的类即可(类路径要完全一样,这样切换不同的风味包都不需要任何更换逻辑)。
4.1 代码时刻
我们需要在不同的风味包中调用如下代码,但又要避免污染核心代码。
// armv7 风味包
public class LogicUtils {
public static String calculate(int a, int b) {
return "armv7:" + (a + b);
}
}
// x86 风味包
public class LogicUtils {
public static String calculate(int a, int b) {
return "x86:" + (a + b);
}
}
使用方法
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tvContent = findViewById(R.id.tv_content);
tvContent.setText("LogicUtils:" + LogicUtils.calculate(1, 2));
}
}
项目结构
至此,我们第四个问题就解决了。
四、效果演示
完整代码:github传送,如果对你有帮助,给个star吧。
4.1 运行效果
4.2 并存效果
4.3 两个应用
五、写在最后
如果喜欢的话请给我一个赞,并关注我吧。文章中如有写的不妥的地方,请评论区与我讨论吧,共同进步。
如果觉得文章有很大的帮助,快来赞赏一次吧😄