總結了一下 目前以爲比較好用的gradle 和一些打包 經驗。放在這裏。java
首先說下 渠道號 這個概念,咱們常常會統計咱們的api 訪問來源 是來自於那個app store,這有利於 咱們針對性的推廣。也能夠知道用戶的分佈狀況,目前咱們的作法一般是這樣的:python
而後在咱們的自定義application裏面 定義一個函數 來取得這個渠道號android
1 package com.example.administrator.gradletest; 2 3 import android.app.Application; 4 import android.content.Context; 5 import android.content.pm.ApplicationInfo; 6 import android.content.pm.PackageManager; 7 8 /** 9 * Created by Administrator on 2015/12/7. 10 */ 11 public class BaseApplication extends Application { 12 @Override 13 public void onCreate() { 14 super.onCreate(); 15 } 16 17 private String getChannel(Context context) { 18 try { 19 PackageManager pm = context.getPackageManager(); 20 21 ApplicationInfo appInfo = pm.getApplicationInfo(context.getPackageName(),PackageManager.GET_META_DATA); 22 return appInfo.metaData.getString("channel"); 23 24 } catch (PackageManager.NameNotFoundException e) { 25 e.printStackTrace(); 26 } 27 return ""; 28 } 29 }
而後再到gradle 腳本里面 動態更改manifest編譯時的 meta data裏面的值,這個方法是目前流傳最廣也是使用最多的方法,可是有個缺陷是速度太慢了。api
隨着咱們app的擴展,自己咱們編譯一次就要好久的時間,而後你想一想 至少還要分爲debug和release2個版本,而後各類渠道 什麼360 pp 百度 豌豆莢 錘子 小米 華爲閉包
之類7788 起碼20個以上的渠道 再加上debug 和release 你想一想 動不動 就是40幾個版本,你要是再在gradle 修改腳原本作這件事,後果就是你編譯一次 產出app
就要好久好久,由於你每次修改完manifest文件裏的meta data 都會從新編譯一次,太浪費時間了!ide
因此咱們採用下面的方法:函數
首先咱們能夠看一下 apk 這個文件 解壓縮之後的結構:gradle
看一下這個就知道了,咱們能夠想一下 可否把咱們的渠道號 放到這個路徑下面呢?讓咱們的android app代碼來讀取這個文件路徑下的渠道號,而後想辦法在build出來的apk裏面直接在這個路徑下ui
放一個渠道號不就好了麼?這樣只用build 一次,修改n次 便可獲得咱們須要的n個渠道包了!
首先咱們來修改咱們的app代碼:
1 package com.example.administrator.gradletest; 2 3 import android.app.Application; 4 import android.content.Context; 5 import android.content.pm.ApplicationInfo; 6 import android.content.pm.PackageManager; 7 import android.util.Log; 8 9 import java.io.IOException; 10 import java.util.Enumeration; 11 import java.util.zip.ZipEntry; 12 import java.util.zip.ZipFile; 13 14 /** 15 * Created by Administrator on 2015/12/7. 16 */ 17 public class BaseApplication extends Application { 18 @Override 19 public void onCreate() { 20 super.onCreate(); 21 } 22 23 24 //這段代碼的做用就是遍歷本身的apk包 把包裏的文件名全找到 25 //而後找到某個META-INF/mtchannel_xiaomishangcheng 這樣格式的文件便可 26 //而後把xiaomishangcheng 這個字符串提取出來返回就是獲得的渠道號了 27 private String getChannelByFile(Context context) { 28 ApplicationInfo appinfo = context.getApplicationInfo(); 29 String sourceDir = appinfo.sourceDir; 30 String ret = ""; 31 ZipFile zipfile = null; 32 try { 33 zipfile = new ZipFile(sourceDir); 34 Enumeration<?> entries = zipfile.entries(); 35 while (entries.hasMoreElements()) { 36 ZipEntry entry = ((ZipEntry) entries.nextElement()); 37 String entryName = entry.getName(); 38 Log.v("burning","entryName=="+entryName); 39 if (entryName.startsWith("META-INF/mtchannel")) { 40 ret = entryName; 41 break; 42 } 43 } 44 } catch (IOException e) { 45 e.printStackTrace(); 46 } finally { 47 if (zipfile != null) { 48 try { 49 zipfile.close(); 50 } catch (IOException e) { 51 e.printStackTrace(); 52 } 53 } 54 } 55 56 String[] split = ret.split("_"); 57 if (split != null && split.length >= 2) { 58 return ret.substring(split[0].length() + 1); 59 60 } else { 61 return ""; 62 } 63 } 64 65 // 66 // 67 // private String getChannel(Context context) { 68 // try { 69 // PackageManager pm = context.getPackageManager(); 70 // 71 // ApplicationInfo appInfo = pm.getApplicationInfo(context.getPackageName(),PackageManager.GET_META_DATA); 72 // return appInfo.metaData.getString("channel"); 73 // 74 // } catch (PackageManager.NameNotFoundException e) { 75 // e.printStackTrace(); 76 // } 77 // return ""; 78 // } 79 }
好,而後咱們來編寫腳本,這個腳本也很簡單,拿到build出來的apk之後 就打開這個zip文件,而後往裏面寫一個咱們規定好的那個格式的文件便可:
注意這是python代碼,固然你若是不會python 用java也能夠作。
1 # coding=UTF-8 2 import zipfile 3 #這個就是你build完之後輸出的apk包的路徑 4 apkpath = 'C:/Users/Administrator/GradleTest/app/build/outputs/apk/app-debug.apk' 5 #這個就是隨便創建一個空文件就能夠了 一會要把這個文件寫入apk 6 emptypath = 'C:/Users/Administrator/GradleTest/app/build\outputs/apk/none' 7 zipped = zipfile.ZipFile(apkpath, 'a', zipfile.ZIP_DEFLATED) 8 #這個就是用format的方法 給上面那個空文件重命名一下。 9 empty_channel_file = "META-INF/mtchannel_{channel}".format(channel='xiaomishangcheng') 10 #命名結束之後 就直接把這個空文件寫入到apk中便可 11 zipped.write(emptypath,empty_channel_file)
你看,這樣就能夠完成咱們快速生成渠道包的功能了,哪怕你有1000個渠道包須要打,你也只須要編譯一次,而後稍微修改一下這個腳本,便可自動生成1000個渠道包。
而後接着看下一個:
如何配置簽名信息,你們都知道android的 簽名資源通常都是很寶貴的,一個組 可能只有1-2個 管理者能拿到那個原始的keystore。因此咱們固然不能在咱們的版本庫
裏面上傳這個keystore文件 對吧,尤爲作開源項目的時候更是如此,因此有下面的解決方法:
你看 咱們能夠把簽名文件定義在外面~~
而後繼續修改build.gradle文件,就是你要打版本的 那個project下的build.gradle文件,別改錯了!
1 apply plugin: 'com.android.application' 2 3 android { 4 compileSdkVersion 23 5 buildToolsVersion "23.0.1" 6 7 8 //配置簽名信息 9 signingConfigs{ 10 release{ 11 storeFile file(RELEASE_STORE_FILE) 12 storePassword RELEASE_STORE_PASSWORD 13 keyAlias RELEASE_KEY_ALIAS 14 keyPassword RELEASE_KEY_PASSWORD 15 } 16 } 17 defaultConfig { 18 applicationId "com.example.administrator.gradletest" 19 minSdkVersion 14 20 targetSdkVersion 23 21 versionCode 1 22 versionName "1.0" 23 } 24 buildTypes { 25 release { 26 //告訴gradle 打release版本的時候使用的簽名信息 27 signingConfig signingConfigs.release 28 //無效資源不要打包進release版本 29 shrinkResources true 30 minifyEnabled false 31 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 32 } 33 } 34 } 35 36 dependencies { 37 compile fileTree(dir: 'libs', include: ['*.jar']) 38 testCompile 'junit:junit:4.12' 39 compile 'com.android.support:appcompat-v7:23.1.1' 40 compile 'com.android.support:design:23.1.1' 41 }
混淆開關大家本身決定開不開~~
最後說一下 多渠道打包的gradle問題,前面那個 python腳本 已經告訴了你們 如何針對渠道號這一需求 進行多渠道打包。
有興趣的人能夠擴展一下那個腳本,把apk的命名也能夠規範一下。這是比較好的習慣。
固然了 關於多渠道打包 還有一種狀況就是 好比某些應用市場 或者 某些渠道 非要讓你加入他們的sdk什麼的 才讓你發佈
那針對於這種狀況,咱們python 腳本 直接修改apk 這個zip包 就搞不定了。這種狀況就須要 用gradle來解決。下面給出一個例子,
之後你們能夠針對性的 也進行配置!
咱們假設 如今要發2個版本,一個版本 要用EventBus的代碼 還有一個版本不須要。
apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.1" //這裏咱們定義了2個渠道版本 一個是modifyDev 這個dev包就是會打包進去eventbus的 //還有一個dev包 則是不會把eventbus打包進去 productFlavors { "modifyDev" { } "dev"{ } } applicationVariants.all { variant -> variant.outputs.each { output -> //命名規則,這個就是主要演示一下groovy閉包操做的 //前面咱們知道多渠道打包仍是用python來完成速度是最快的 output.outputFile = new File( output.outputFile.parent, "custom-${variant.buildType.name}-${variant.versionName}-${variant.productFlavors[0].name}.apk".toLowerCase()) //把unaligned apk刪除掉 看着礙眼 File unaligned=output.packageApplication.outputFile unaligned.delete() } } signingConfigs{ release{ storeFile file(RELEASE_STORE_FILE) storePassword RELEASE_STORE_PASSWORD keyAlias RELEASE_KEY_ALIAS keyPassword RELEASE_KEY_PASSWORD } } defaultConfig { applicationId "com.example.administrator.gradletest" minSdkVersion 14 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { //咱們固然也能夠針對debug版本進行配置 這裏就省略不進行特殊配置 debug { } release { //告訴gradle 打release版本的時候使用的簽名信息 signingConfig signingConfigs.release //無效資源不要打包進release版本 shrinkResources true //release版本咱們把擾碼打開 minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.1.1' compile 'com.android.support:design:23.1.1' //注意看這個地方咱們使用的是provided 這樣咱們在寫代碼的時候就能夠引用這個包下面的全部代碼了 provided 'de.greenrobot:eventbus:2.4.0' //但實際上你看只有 modifyDev 這個Flavor 會真正的Compile進去 其餘默認狀況下 是不會Compile進去的 modifyDevCompile 'de.greenrobot:eventbus:2.4.0' }
而後看下咱們activity的代碼 就增長了一行:
1 try { 2 Class.forName("de.greenrobot.event.EventBus"); 3 Log.v("MainActivity","加載到EventBus包"); 4 } catch (ClassNotFoundException e) { 5 Log.v("MainActivity","找不到EventBus包"); 6 e.printStackTrace(); 7 }
而後咱們選擇devRelease 做爲咱們的build varint看一下日誌輸出什麼:
發現 這裏是找不到eventBus這個包的。
而後咱們看下modifyDevRelease 看看是什麼效果:
發現 這個eventBus已經打進去了~~
到這,基本上android studio gradle 和 python配合打包的一些主要部分就講解結束了。