Android 多渠道打包方式详解

幼敏悟过人,读书辄成诵。这篇文章主要讲述Android 多渠道打包方式详解相关的知识,希望能为你提供帮助。
Android 多渠道打包方式详解
面试的时候,如果面试官突然问到:你们渠道包是怎么打的?如果你说是用gradle一个一个编译的,然后他很鄙视的说这个效率太低啦,你们写过什么脚本自己打渠道包没?你肯定心里想,卧槽,这么狂炫吊炸天,自己写脚本打包?!其实这个根本也不是太难啦!!今天就来聊聊多渠道打包的原理以及如何自己DIY多渠道打包的工具!
 
渠道包出现
 
当一个产品到发版的时候,我们搞android的就会面临一个超级尴尬的问题:国内这么多的渠道,渠道统计是必须做滴,那么十多个主要渠道再加无限量的地推渠道包就成了一个巨坑了!这一块耗费的时间是一个无底洞啊!!!
【Android 多渠道打包方式详解】 
方式一览
 
这里一共会介绍三种渠道包的实现方式,分别是:
 
1、使用gradle配置直接编译出不同的渠道包。
2、通过反编译修改对应的渠道号。
3、META-INF里面新加一个文件。
 
Gradle方式
 
不管是用友盟统计还是其他什么的,首先肯定都是要有一些准备工作的,由于本人就比较了解友盟的,所以就用友盟统计来举例啦!
友盟统计提供了两种渠道统计策略,其实就是一个自动挡的一个手动挡的。
 
< meta-data
android:name="UMENG_APPKEY"
android:value="https://www.songbingjia.com/android/xxxxxxxx"/>
< meta-data
android:name="UMENG_CHANNEL"
android:value="https://www.songbingjia.com/android/${GRADLE_CHANNEL_VALUE}"/>

在对应的build.gradle里面配置对应的信息:
 
productFlavors.all { flavor ->
flavor.manifestPlaceholders = [GRADLE_CHANNEL_VALUE: name]
}
productFlavors {
dev {
}
baidu {
minSdkVersion 18
applicationId "com.test.michat"
}
}

如果手动去设置对应的渠道号的话,就在程序入口处调用以下方法:
 
MobclickAgent. startWithConfigure(UMAnalyticsConfig config)
 
UMAnalyticsConfig(Context context, String appkey, String channelId)
 
UMAnalyticsConfig(Context context, String appkey, String channelId, EScenarioType eType)
 
UMAnalyticsConfig(Context context, String appkey, String channelId, EScenarioType eType,Boolean isCrashEnable)

那么怎么获取到对应的渠道号呢?!这个方法在之后的所有方式中都要使用滴,其实不管是哪种方式,最后都会调用这个方法去读相关数据的!!
 
private String getChannel(Context context) {
try {
PackageManager pm = context.getPackageManager();
ApplicationInfo appInfo = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
return appInfo.metaData.getString("CHANNEL_VALUE");
} catch (PackageManager.NameNotFoundException ignored) {
}
return "";
 
}

Android 多渠道打包方式详解(上)
buildVariants.png
 
当然你也可以使用命令行:gradlew assemble 组装出所有的渠道包!!
 
反编译方式
 
gradle方式用着也挺不错的,为什么还要去搞什么反编译这么麻烦的东西呢?因为它有一个很大的问题,那就是每一个包都是要去编译打包的!这是相当的耗时!time is 加班啊!谁也不想加班打渠道包咯!!反编译的方式就是节省了每个渠道包都去编译的时间,而是编译好一个渠道包之后就使用该渠道包,通过反编译动态修改AndroidManifest.xml里面的信息,然后再重新打包签名!
 
说到反编译,那么这里就不得不提大名鼎鼎的apktool.jar了!纳尼,你说你从未听说过?!没事儿,以前没有听过,现在会用了就行了!!
 
然后总结一下接下来的一系列套路:
 
解包-> 修改相关参数-> 打包-> 签名-> Zipalign优化
 
1、解包
 
apktool d your_original_apk build

你没有看错,就是这样的!因为我们是站在巨人的肩膀上工作的嘛,所以好多工作就不同自己搞了!
 
执行以上命令之后,如果不出什么意外,你就会得到一个文件夹:
 
Android 多渠道打包方式详解(上)
 
相关代码:
 
try {
brut.apktool.Main.main(new String[]{"d", "-f", apkFilePath, "-o", outPath});
return true;
} catch (Exception e) {
e.printStackTrace();
callback("解包失败 !!!!!\r\n" + e.getMessage());
}

2、修改对应的参数
 
打开对应的AndroidManifest.xml,你没有看错,什么都在里面,直接修改就好了!等等,xml解析你不会?!没有关系,这里有dom4j.jar给你使用啦!!
修改反编译之后的AndroidManifest文件相关代码
 
try {
File androidManifestFile = new File(appFolderName + File.separator + "AndroidManifest.xml");
Document document = new SAXReader().read(androidManifestFile); //使用dom4j的sax解析
Element element = document.getRootElement().element("application");
List< Element> list = element.elements("meta-data"); //获取到所有的“ meta-data”
List< MetaData> metaData = https://www.songbingjia.com/android/manifest.getMetaData();
boolean isUpdate = false;
for (MetaData data : metaData) {
String name = data.getName();
String value = https://www.songbingjia.com/android/data.getValue();
callback(" meta-data name=‘" + name + "‘ value=https://www.songbingjia.com/android/‘" + value + "‘");
for (Element s : list) {
Attribute attribute = s.attribute("name");
//更新相关渠道号
if ( "UMENG_CHANNEL".equals(name)& & "UMENG_CHANNEL".equals(attribute.getValue())) {//更换相关的渠道号
s.attribute("value").setValue(value);
isUpdate = true;
callback("更新1 AndroidManifest.xml meta-data name=‘" + attribute.getValue() + "‘ value=https://www.songbingjia.com/android/‘" + value + "‘");
break;
}
 
}
}
if(isUpdate){//更新后重新写入
XMLWriter writer = new XMLWriter(new FileOutputStream(androidManifestFile));
writer.write(document);
writer.close();
callback("更新 AndroidManifest.xml 完成 ~ ");
}
} catch (Exception e) {
e.printStackTrace();
return false;
}

3、打包
 
apktool b build your_unsigned_apk

还是这么简单:
 
try {
brut.apktool.Main.main(new String[]{"b", buildApkFolderPath, "-o", buildApkOutPath});
return true;
} catch (Exception e) {
e.printStackTrace();
callback("打包失败 !!!!!\r\n" + e.getMessage());
}

4、签名
 
jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore your_keystore_path -storepass your_storepass -signedjar your_signed_apk, your_unsigned_apk, your_alias

这个是jdk里面直接提供了的,只要你的环境变量配置好了的,就没有什么问题啦!重新签名相关代码
 
executeCommand("jarsigner", "-verbose", "-sigalg", "SHA1withRSA", "-digestalg", "SHA1", "-keystore", keystoreFilePath, apkFilePath, alias, "-storepass", password);
 
 
/**
* 执行命令
*
* @param command 命令
*/
private synchronized boolean executeCommand(String... command) {
Process process = null;
BufferedReader reader = null;
try {
ProcessBuilder builder = new ProcessBuilder();
builder.command(command);
builder.redirectErrorStream(true);
process = builder.start();
reader = new BufferedReader(new InputStreamReader(process.getInputStream(),"UTF-8"));
String line;
while ((line = reader.readLine()) != null) {
callback(line);
if (line.contains("Exception") || line.contains("Unable to open")) {
return false;
}
}
return true;
} catch (IOException e) {
e.printStackTrace();
callback(e.getMessage());
} finally {
close(reader);
if (process != null) {
process.destroy();
}
}
return false;
}

5、Zipalign优化
 
Android 多渠道打包方式详解(上)
 
如图所示,sdk/build-tools里面每个版本都是有这个东西的,加到环境变量中就好了!!!
稿源:勤快学QKXue.NET
扩展阅读:
Android 多渠道打包方式详解(上):http://qkxue.net/info/25417/Android
Android 多渠道打包方式详解(下):http://qkxue.net/info/25418/Android

    推荐阅读