最近由于要独立开发,所以需要掌握从开发到上线的所有阶段,以前从没接触过打包的问题,现在突然接触感觉手忙脚乱的,特此总结一下。
国内的Android开发由于众所周知的原因,Google play无法在国内打开,所以android的市场群雄争霸,为了方便统计各个安卓市场的下载量,需要为每个应用市场的Android包设定一个可以区分应用市场的标识,这个为Android包设定应用市场标识的过程就是多渠道打包。
其实感觉多渠道打包就是一个换应用名字的过程,但是换了应用名字谁也不知道该如何统计,为了能让友盟统计到下载量,就需要在AndroidManifest.xml中的mate-date改变渠道号的标识,这样才能让友盟根据渠道号统计到下载量。由于android7.0修改了签名机制,感觉最纯正的打包方式还是使用gradle。
仔细搜一下的话还是发现了很多写好的gradle代码,方法都大同小异,去将每一个包的渠道号修改掉,并动态获取此时的包名,这也是因为友盟的渠道号只能在代码中去注册。
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.wooyun.castiel"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
//签名
signingConfigs {
debugConfig {
storeFile file("../wooyun_keystore") //签名文件
storePassword "123456"
keyAlias "123456"
keyPassword "123456" //签名密码
}
release{
storeFile file("../wooyun_keystore") //签名文件
storePassword "123456"
keyAlias "123456"
keyPassword "123456" //签名密码
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// 自定义输出配置
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
// 输出apk名称为wooyun_v1.0_wandoujia.apk
def fileName = "wooyun_v${defaultConfig.versionName}_${variant.productFlavors[0].name}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}
}
productFlavors {
kuan {}
xiaomi {}
qh360 {}
baidu {}
wandoujia {}
}
productFlavors.all {
flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
}
/**
* 渠道号工具类:解析压缩包,从中获取渠道号
*/
public class ChannelUtil {
private static final String CHANNEL_KEY = "uuchannel";
private static final String DEFAULT_CHANNEL = "internal";
private static String mChannel;
public static String getChannel(Context context) {
return getChannel(context, DEFAULT_CHANNEL);
}
public static String getChannel(Context context, String defaultChannel) {
if (!TextUtils.isEmpty(mChannel)) {
return mChannel;
}
//从apk中获取
mChannel = getChannelFromApk(context, CHANNEL_KEY);
if (!TextUtils.isEmpty(mChannel)) {
return mChannel;
}
//全部获取失败
return defaultChannel;
}
/**
* 从apk中获取版本信息
*
* @param context
* @param channelKey
* @return
*/
private static String getChannelFromApk(Context context, String channelKey) {
long startTime = System.currentTimeMillis();
//从apk包中获取
ApplicationInfo appinfo = context.getApplicationInfo();
String sourceDir = appinfo.sourceDir;
//默认放在meta-inf/里, 所以需要再拼接一下
String key = "META-INF/" + channelKey;
String ret = "";
ZipFile zipfile = null;
try {
zipfile = new ZipFile(sourceDir);
Enumeration<?> entries = zipfile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
String entryName = entry.getName();
if (entryName.startsWith(key)) {
ret = entryName;
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (zipfile != null) {
try {
zipfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
String channel = "";
if (!TextUtils.isEmpty(ret)) {
String[] split = ret.split("_");
if (split != null && split.length >= 2) {
channel = ret.substring(split[0].length() + 1);
}
System.out.println("-----------------------------");
System.out.println("渠道号:" + channel + ",解压获取渠道号耗时:" + (System.currentTimeMillis() - startTime) + "ms");
System.out.println("-----------------------------");
} else {
System.out.println("未解析到相应的渠道号,使用默认内部渠道号");
channel = DEFAULT_CHANNEL;
}
return channel;
}
}
android打包的过程也是一个很值得研究的地方,主要分为七步
1.打包资源文件,生成R.java文件
2.处理aidl文件,生成相应Java文件。
3.编译工程源代码,生成相应class文件。
4.转换所有class文件,生成class.dex文件
5.打包生产apk
6.对apk文件进行签名
7.对签名的apk进行对其操作。