总结了一下 目前以为比较好用的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配合打包的一些主要部分就讲解结束了。