目錄
一、前言
二、我們需要解決的問題
1、所要達到的效果
2、需要解決的問題
三、編碼時刻
四、效果演示
五、寫在最後
一、前言
經過前幾篇 安卓gradle 文章的介紹,童鞋們應該對 安卓gradle 更加熟悉了。
1、defaultConfig——安卓gradle
2、buildTypes——安卓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 兩個應用
五、寫在最後
如果喜歡的話請給我一個贊,並關注我吧。文章中如有寫的不妥的地方,請評論區或加我微信與我討論吧,共同進步。