當項目需要有更多的客戶的時候,你就會考慮將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://www.cnblogs.com/soaringEveryday/p/5368540.html