1、為什么要進行多渠道打包?
安卓應用商店(一個商店也叫做一個渠道,如豌豆莢,360手機助手,應用寶)眾多,大大小小幾百個,每當我們發新版本時,需要將Android客戶端分發到各個應用市場,為了統計這些市場的效果(下載量、活躍數等),需要有一種方法來唯一標識它們,所以才有了多渠道打包。
2、如何統計各個渠道的下載量、活躍數?
現在有比較成熟的第三方應用幫我們實現統計功能(比如友盟),統計的本質就是收集用戶信息傳輸到后臺,后臺生成報表,幫助我們跟蹤分析并完善app。通過android系統的方法已經可以獲取到引用版本號,版本名稱,系統版本,機型等各種信息,唯獨應用商店(渠道)的信息我們是沒有辦法從系統獲取到的,所以我們就人為的在apk里面添加渠道信息(其實就用一個字段進行標識,如wandoujia
,360
,yingyongbao
),我們只要把這些信息打包到apk文件并將信息傳輸到后臺,后臺根據這個標識,可以統計各個渠道的下載量了。
多渠道打包只需要關注兩件事情:
- 將渠道信息寫入apk文件
- 將apk中的渠道信息傳輸到統計后臺
3、使用Gradle進行多渠道批量打包**
-
通常都是在在
AndroidManifest.xml
中加入渠道區分標識寫入一個meta標簽:<meta-data android:name="channel" android:value="${channel}" />
-
在app目錄下在build.gradle中配置productFlavors添加如下代碼:
productFlavors { qihu360 {} // 360手機助手 yingyongbao {} // 騰訊應用寶 wandoujia {} // 豌豆莢 baidu {} // 百度手機助手 miui {} // 小米 flyme {} // 魅族 lenovo {} // 聯想-樂商店 oppo {} // Oppo-可可軟件商店 huawei {} // 華為 vivo {} // vivo hiapk {} // 安卓市場 sj91 {} // 91手機助手 sogou {} // 搜狗手機市場 sohu {} // 搜狐應用中心 taobao {} // 淘寶手機助手 gfan {} // 機鋒 appchina {} // 應用匯 mumayi {} // 木螞蟻 wangyi163 {} // 網易應用 nduoa {} // N多市場 mm10086 {} // MM商城-中國移動 wostore {} // WO商店 youyi {} // 優異 uc {} // UC+開發平臺 anzhi {} // 安智市場 google {} // google play amazon {} // 亞馬遜 } productFlavors.all { flavor -> flavor.manifestPlaceholders = [channel: name] }
AndroidStudio的
Build -> Generate signed apk
打簽名包時即可選擇設置渠道:
Paste_Image.png 在代碼中獲取渠道信息:
public static String getFlavorChannel(Context context) {
try {
PackageManager pm = context.getPackageManager();
ApplicationInfo appInfo = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
return appInfo.metaData.getString("channel");
} catch (PackageManager.NameNotFoundException ignored) {
}
return "";
}
- 在android studio中測試不同渠道的apk。
4.APP定制渠道包(馬甲包)
在App的開發過程中,經常會遇到產品或者運營的同事提出要制作馬甲包的需求;馬甲包簡單來說就是原APP的小號,與原APP包除了包名,應用名稱、圖標等給用戶加以區分的東西,其他功能基本不變亦或者只采用原App的部分功能的APP包。
-
ApplicationId,版本號
Android 應用都有自己的包名。包名是設備上每個應用程序的唯一標識,同樣也是在各個下載平臺的唯一標識。就是說,假如你已經使用某個包名來發布應用,就不能再去改變應用的包名,因為這樣做會導致你的應用被視為一個全新的應用,你現有的用戶也不會收到應用的更新通知。
隨著渠道越來越多,不同渠道對應用的要求也不盡相同。有時候我們需要發布不同的版本,例如 pro,hd 版本,支持用戶可以下載安裝不同的版本。那么我們需要設置不同的ApplicationId和對應的版本號, 同時要與 PackageName 解耦合。
- 代碼中引用的 R 類要保持不變;
- 在構建不同版本的應用時,對應的(引用了 R 的) .java 源文件也不能改動。
那么我們只需要在productFlavors
對應的渠道中指定applicationId
和versionCode
,versionName
,例我們指定GooglePlay的applicationId
:
productFlavors {
GooglePlay {
//指定這個渠道的版本號
versionCode 2
versionName "1.2"
//指定區別于其他渠道的 applicationId
applicationId "com.liujc.androidtools.hd"
}
yingyongbao {} // 騰訊應用寶
wandoujia {} // 豌豆莢
baidu {} // 百度手機助手
miui {} // 小米
//其他...
}
-
BuildConfig
Gradle會在generateSources階段為flavor生成一個BuildConfig.java
文件。BuildConfig類默認提供了一些常量字段,比如應用的版本名(VERSION_NAME
),應用的包名(PACKAGE_NAME
)等。更強大的是,開發者還可以添加自定義的一些字段。下面的示例假設debug
版開啟LOG功能,使用debug的api,而release
版則使用不開啟LOG和使用release時的api:
那么代碼中就可以使用 BuildConfig.LOG_DEBUG 和 BuildConfig.API_HOST 了。buildTypes { debug { // debug模式下,顯示log buildConfigField("boolean", "LOG_DEBUG", "true") buildConfigField ("String", "API_HOST", "\"http://api.test.com\"")//debug API Host } release { // release模式下,不顯示log buildConfigField("boolean", "LOG_DEBUG", "false") buildConfigField("String", "API_HOST", "\"http://api.release.com\"")//release API Host } }
備注: 這里簡單介紹下buildConfigField
方法,可以發現其有三個參數:/** * Adds a new field to the generated BuildConfig class. * * <p>The field is generated as: {@code <type> <name> = <value>;} * * <p>This means each of these must have valid Java content. If the type is a String, then the * value should include quotes. * * @param type the type of the field * @param name the name of the field * @param value the value of the field */ public void buildConfigField( @NonNull String type, @NonNull String name, @NonNull String value)
String type 要創建的字段類型 如上面的String與boolean String name 要創建的字段名 如上面的API_HOST與LOG_DEBUG String value 創建此字段的值 如上面的"\"http://api.release.com\""與"true" 轉義字符
。
-
控制是否自動更新
一般應用在啟動時都會默認檢查客戶端是否有更新,如果有更新就會提示用戶下載。但是有些渠道和應用市場不允許這種默認行為,所以在適配這些渠道時需要禁止自動更新功能。
解決的思路是提供一個配置字段,應用啟動的時候檢查該字段的值以決定是否開啟自動更新功能。使用flavor可以完美的解決這類問題。甚至可以在productFlavors對應的渠道號進行區別,例如豌豆莢版默認禁止版本自動更新:android { defaultConfig { buildConfigField "boolean", "AUTO_UPDATES", "true" } productFlavors { wandoujia { buildConfigField "boolean", "AUTO_UPDATES", "false" } } }
多渠道資源文件定制
客戶端經常會和一些應用分發市場合作,需要在應用的啟動界面中加上第三方市場的Logo,類似這類適配形式還有很多。通常對于不同渠道,我們會區別不同的資源。例如我們一款應用需要在360發布,而應用圖標和歡迎界面要一個360標志的圖或者應用名稱和其他渠道不同,那么這個時候就需要按渠道打包對應的應用圖標和歡迎圖片以及應用名稱了了。
Gradle在構建應用時,會優先使用flavor所屬dataSet中的同名資源。所以,在flavor的dataSet中添加同名的字符串資源,以覆蓋默認的資源。上面我們已經有針對360的渠道了,就是qihu360,我們只需要在app/src/目錄下添加渠道對應的文件夾qihu360,然后覆蓋對應要覆蓋的內容。下面是定制應用圖標的步驟:
- 添加qihu360文件夾,那么在
app/src/
目錄下面就有 main , androidTest , qihu360,test這四個文件夾了。main 目錄是通用正常渠道包目錄,qihu360是我們需要定制資源的渠道包目錄。如下圖:
Paste_Image.png - 并添加如下應用名字符串資源
src/qihu360/res/values/strings.xml
:
默認的應用名字符串資源如下(<resources> <string name="app_name">360MultiChannelBuild</string> </resources>
src/main/res/values/strings.xml
):
最后運行360版本的app即應用名稱顯示成360MultiChannelBuild。<resources> <string name="app_name">MultiChannelBuild</string> </resources>
通過以上例也可以定制其他資源,包括drawable
,styles.xml
甚至AndroidManifest.xml
也都是可以的。即可實現不同渠道不同的應用顯示。
-
使用第三方SDK
某些渠道會要求客戶端嵌入第三方SDK來滿足特定的適配需求。問題的難點在于如何只為特定的渠道添加SDK,其他渠道不引入該SDK。使用flavor可以很好的解決這個問題,下面以為qihu360
flavor引入com.qihoo360.union.sdk:union:1.0
SDK為例進行說明:
android {
productFlavors {
qihu360 {
}
}
}
...
dependencies {
//參與編譯但不參與打包
provided 'com.qihoo360.union.sdk:union:1.0'
//指定qihu360這個渠道可以打包這個庫
qihu360Compile 'com.qihoo360.union.sdk:union:1.0'
}
上例添加了名為qihu360的flavor,并且指定編譯和運行時都依賴com.qihoo360.union.sdk:union:1.0
。而其他渠道只是在構建的時候依賴該SDK,打包的時候并不會添加它。
接下來,需要在代碼中使用反射技術判斷應用程序是否添加了該SDK,從而決定是否要顯示360 SDK提供的精品應用。部分代碼如下:
class MyActivity extends Activity {
private boolean useQihuSdk;
@override
public void onCreate(Bundle savedInstanceState) {
try {
Class.forName("com.qihoo360.union.sdk.UnionManager");
useQihuSdk = true;
} catch (ClassNotFoundException ignored) {
}
}
}
最后打包運行即可生成包含360精品應用模塊的渠道包了。