文章目錄
背景
學習多渠道打包,用的 demo 來做一個簡單的演示。
多渠道是什麼
渠道就是指不同的安裝包發佈平臺:有應用寶、百度、小米、360、豌豆莢等應用發佈平臺。你打包後的 app 就可以發佈在這些平臺上供用戶自行下載體驗。
爲什麼要做多渠道打包
方便後臺統計 app 在各個平臺上的下載次數。
多渠道打包原理
一般,我們在清單配置文件中聲明一個 meta-data
標籤,裏面設置對應的 name
和 value
屬性,我們可以在代碼中根據 name
獲取到對應的 value
,這個 value
就是我們需要的渠道信息。
怎麼實現渠道信息的統計
這裏簡單說一下實現:例如用戶從豌豆莢市場
下載了 app
,安裝後並打開使用。這個時候我們在代碼中就能獲取到 wandoujia
這個渠道信息,然後把這個信息通過調用接口統計到後臺。
不過目前有很多開發者平臺都提供了對應的 sdk 供我們集成使用,比如 友盟
這個大家族,集成友盟統計
的功能我會在下期文章中提供。通過集成友盟的統計功能,我們可以很方便的在友盟開發者平臺
上看到我們發佈後的 app
的一個下載使用情況,很實用的一個功能。
打包實現
清單配置文件添加 meta-data 標籤
<meta-data android:value="${MY_CHANNEL_VALUE}" android:name="MY_CHANNEL"/>
如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.fragmentdemo">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:name=".application.MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data android:value="${MY_CHANNEL_VALUE}" android:name="MY_CHANNEL"/>
<activity android:name=".activity.MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
這裏簡單說明一下:value
值用了佔位符來表示,方便一次性打多個渠道包。
密鑰生成
使用簽名密鑰來生成 release 版 apk,這纔是正式生產使用的安裝包。所以我們需要先來生產簽名密鑰。
密鑰生成我在上一篇文章 Warning:JKS 密鑰庫使用專用格式。建議使用 “keytool -importkeystore -srckeystore…pkcs12” 遷移到行業標準格式 PKCS12 中有詳細的介紹,供同學們參考。
簽名配置
我們有了簽名密鑰,可以用來配置一個 release 版的簽名,方便打包,如下:
配置好後,app 模塊下的 build.gradle 文件會多出簽名信息配置,如下圖:
我這裏還用到了一個 keystore.properties 文件,如下圖示:
內容如下圖示:
app 模塊下 build.gradle 文件完整內容如下:
apply plugin: 'com.android.application'
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdkVersion 28
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.fragmentdemo"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
signingConfigs {
release {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
buildTypes {
release {
minifyEnabled false // 不啓用混淆
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
創建風味維度與 Product 風味
什麼是風味維度,這裏暫不作說明,同學們可參考文章 Android Studio3.0 flavorDimensions多維度理解(版本差異化打包) 自行了解。
-
打開
風味維度
創建窗口,如下圖示:
-
首先添加一個
風味維度
,如下圖示:
-
輸入
風味維度
名稱,如下圖示:
-
風味維度
創建完成後如下圖示:
-
Add Product Flavor
-
輸入 product flavor 名稱,如下圖示:
-
product flavor 名稱創建完成後,選擇 release 簽名配置,如下圖示:
-
依次添加 baidu 和 yingyongbao 渠道,並點擊 apply ,如下圖示:
-
等待 gradle 同步完成後,我們再看 app 模塊下的 build.gradle 文件多出的風味配置項如下圖示:
風味配置項的一些優化
-
佔位符使用,減少重複配置
-
可自定義打包後的 apk 名稱
最後給出完整的 build.gradle 文件內容,如下:
apply plugin: 'com.android.application'
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdkVersion 28
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.fragmentdemo"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
signingConfigs {
release {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
buildTypes {
release {
minifyEnabled false // 不啓用混淆
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
flavorDimensions 'dimensionOne'
productFlavors {
wandoujia {
dimension = 'dimensionOne'
signingConfig signingConfigs.release
}
baidu {
dimension = 'dimensionOne'
signingConfig signingConfigs.release
}
yingyongbao {
dimension = 'dimensionOne'
signingConfig signingConfigs.release
}
}
// 使用 佔位符 優化,減少重複風味配置寫法
productFlavors.all{
flavor -> flavor.manifestPlaceholders = [MY_CHANNEL_VALUE : name]
}
// 輸出文件配置,格式如下:app-{版本號}-{渠道名稱}.apk
applicationVariants.all { variant ->
variant.outputs.all { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
// 文件名修改
def fileName = "app-v${defaultConfig.versionName}-${variant.productFlavors[0].name}.apk"
// 重命名賦值
outputFileName = fileName
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
使用 gradle 的build tasks 進行打包
- 如下圖示:
- 打包成功後如下圖示:
多渠道包查看
- 我們打完多渠道包後,可在如下圖示位置查看一次性生成的多渠道包:
渠道信息驗證
- 獲取渠道信息代碼如下:
// 獲取渠道信息
try {
ApplicationInfo applicationInfo = MainActivity.this.getPackageManager().getApplicationInfo(
MainActivity.this.getPackageName(), PackageManager.GET_META_DATA
);
String channel = applicationInfo.metaData.getString("MY_CHANNEL");
Log.e("TAG", "metaData value is :" + channel);
Toast.makeText(MainActivity.this, "當前應用的渠道爲:" + channel,
Toast.LENGTH_LONG).show();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
- 我們依次安裝,打開獲取渠道信息如下圖示:
到此,我們就實現了多渠道打包。
技術永不眠,我們下期見!