當項目需要有更多的客戶的時候,你就會考慮将apk上架到應用商店了,無奈天朝Android應用商店真的是百家争鳴,據某地不完全統計已經有900+。若将Apk上架到所有的應用商店是個好主意,但是據統計也就那麼十來個應用商店的占有率已經超過95%了,是以我覺得并沒有必要上架所有應用商店。這裡就好比Android裡面适配機型一個道理,機型無數,但是也就那麼幾個品牌占有的絕大多數市場。
話說回來為什麼要打管道包(比如說應用寶要發一個apk,小米應用商店要發一個apk等等),而不是同一個apk放到每一個應用商店呢?主要有下列原因:
1. 不同的應用商店對于apk的要求不同,有的甚至需要一些定制,比如某些應用商店要求在啟動頁加入商店的Logo
2. 為了跟蹤每家應用商店的使用者數等等統計資料
幸運的是gradle天生就提供了打多管道包的功能,這裡我們主要解決下列問題:
- 如何打多個管道的包
- 如何支援友盟統計
- 如何在代碼中根據不同管道做執行不同的代碼塊,即管道定制
- 如何支援多包名打包
- 一種對于BuildConfig的更有意思的用法(彩蛋)
修改build.gradle和預編譯BuildConfig類解析
在moudle的build.gradle的android元素下,有這麼productFlavors這麼一個函數,它支援不同“口味”的版本編譯。比如我們需要做一個應用寶的管道版本,可以添加下列代碼:
android {
productFlavors {
YingYongBao {
manifestPlaceholders = [UMENG_CHANNEL:"YingYongBao"]
buildConfigField "String", "CHANNEL", "\"YingYongBao\""
}
}
随後在AndroidManifest.xml的配置友盟管道号的地方改成:
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL}" />
productFlavors中的manifestPlaceholder類似于給AndroidManifest檔案定義了一個變量,上述例子中改變量名字就加UMENT_CHANNEL,值為字元串YingYongBao,這樣子改管道打包出來的apk即可以釋出到應用寶市場上了。同時你在友盟的背景統計子產品控制台中,也可以看到一個叫做“YingYongBao”的管道來源,可以跟蹤該管道的使用者使用資料。這樣我們要解決的第2個問題OK了。
productFlavors中的buildConfigField是在等于在預編譯期間建立了一個變量叫做CHANNEL,類型是字元串類型,值是字元串“YingYongBao”。
當我們寫C/C++代碼的時候通常會寫預編譯宏變量,可以控制代碼中在不同編譯條件下的執行分支。比如:
#ifdef _MACRO_YING_YONG_BAO
{
// code for yingyongbao
}
#else
{
// code for others
}
#endif
當需要執行應用寶那部分代碼的時候則編譯時打開宏_MACRO_YING_YONG_BAO。而Android Studio也提供了類似的機制,它在預編譯階段生成了一個叫做BuildConfig.java的類,它位于:
當我們預編譯時會自動生成這麼一個類,按照上述編譯應用寶管道的操作中,我們看下這個BuildConfig.java是什麼内容:
這裡發現有一個叫做CHANNEL的String常量,而且值是“YingYongBao”,是以想到那麼這裡為了讓不同管道執行不同的代碼,我們于是可以在代碼中這樣子寫:
if (BuildConfig.CHANNEL.equals("YingYongBao")) {
// code block for yingyongbao
} else if (BuildConfig.CHANNEL.equals("360") {
// code block for 360
} else {
// default code
}
這裡需要注意的一點,通過buildConfigField定義的變量中,如果是String類型的話,必須要在值裡面寫上轉義符号\",或者寫成:
buildConfigField "String", "CHANNEL", ‘"YingYongBao"’
否則的話預編譯器會認為YingYongBao不是一個值而是另一個變量,進而報錯:
到此,第3個問題也解決了!
在Android Studio中一鍵打包
到現在還沒有說如何打包的事情,這點很簡單,隻要打開Android Studio的gradle面闆,重新整理task清單,會發現很多task,隻要輕按兩下下assembleRelease這個task即可。稍等一段時間,Android Studio自動幫你生成所有的管道的Apk。
生成的所有apk在這裡:
這裡有兩個注意點:
- 執行打包前,最好先執行下clean那個task
- build那個task也可以打所有管道的包,但是比較慢,因為它同時也打出來debug包,而assembleRelease隻打release包,理論上減少一半時間
問題1解決
多包名支援
在build.gradle中是用applicationId來表述包名的,在productFlavors中是可以嵌入applicationId的定義,比如我們在Beta這樣的管道中想要打出一個com.qianmi.xxx.beta的包名的apk,可以這麼寫:
productFlavors {
Beta {
manifestPlaceholders = [UMENG_CHANNEL:"Beta"]
buildConfigField "String", "CHANNEL", "\"Beta\""
applicationId "com.qianmi.xxx.beta"
}
}
問題4解決
BuildConfig的一種有意思的用法(彩蛋)
我們知道釋出出去的apk都是帶有簽名的,而從Android Studio等IDE中直接run到手機上的是沒有簽名的,但是兩個手機,一台裝了簽名的apk一台是直接run上去的,你能區分出來嗎(實際是看不出來的)?
如果我們能動态的區分出來,同時對于直接從IDE運作出來的Apk中在app的某個界面(比如關于界面)有下列标志多好?
而在正式釋出出來的版本中沒有“開發版本”這個字串。
首先我們在build.gradle中的android元素下的buildTypes函數中加入:
buildTypes {
release {
minifyEnabled true
zipAlignEnabled true
shrinkResources true
signingConfig signingConfigs.release
proguardFiles 'proguard-rules.pro'
}
debug {
minifyEnabled false
zipAlignEnabled true
shrinkResources true
buildConfigField "String", "CHANNEL", "\"dev\""
}
}
這裡有一個release和一個debug。當你通過上述講解中的gradle task中打包時是release,打包時讀取具體的productFlavors。而如果你是直接從IDE運作出來調試的話,會檢查buildTypes中debug這個地方,這裡我們同樣把CHANNEL這個常量給定義出來了,值是dev。
于是我們再在關于界面,寫上下列語句即可:
if (BuildConfig.CHANNEL.equals("dev")) {
tvVersion.setText("v" + AppUtils.getVersionName(getApplicationContext()) + " " + getString(R.string.dev_version));
} else {
tvVersion.setText("v" + AppUtils.getVersionName(getApplicationContext()));
}
題外話
到此為止,多管道打包問題已經解決。但是打包的時候發現如果管道越多實際上打包的時間是越慢的,因為Android Studio在為每個管道一個個的生成apk,是以我們這裡提供的方式僅僅适用于管道比較少的情況下。如果你真的想要在900個以上的應用市場釋出你們的apk,那麼顯然這種方式就不行了。這裡提供兩個解決方法。
一個是對同一個base的apk進行修改的方式,可以參考美團技術團隊的部落格:
http://tech.meituan.com/mt-apk-packaging.html
或者
http://blog.csdn.net/luck_apple/article/details/8634572?utm_source=tuicool&utm_medium=referral
第二個是試用一些廠商的支援多管道打包的軟體,比如360的加強寶:
http://jiagu.360.cn/
文末附上一家統計公司提供的國内應用市場占有率的報告:
轉載請注明出處: http://www.cnblogs.com/soaringEveryday/p/5368540.html