Android批量打包-如何一秒内打完几百个apk渠道包

news/2025/2/12 7:45:36/

在国内Android常用渠道可能多达几十个,如:
谷歌市场、腾讯应用宝、百度手机助手、91手机商城、360应用平台、豌豆荚、安卓市场、小米、魅族商店、oppo手机、联想乐商、中兴汇天地、华为、安智、应用汇、木蚂蚁、3G安卓市场(久邦开发者发布系统)
uc应用商店、苏宁应用、淘宝手机助手、蘑菇市场、搜狗市场、搜狗助手、机锋、易用汇(金立手机)、中国联通沃商、中国移动MM、中国电信天翼、亿优市场、历趣世界、冒泡堂、网讯安卓开发者平台、桌乐、网易、泡椒网、十字猫、酷传、安粉、安卓园、安卓之家
所以在工作中,当项目开发、测试完毕后就需要针对不同的渠道打出对应的apk安装包。为了统计每个渠道效果,我们可以使用Umeng sdk或者百度的sdk。这些sdk的使用我就不再这里赘述了,请看相应的开发文档即可。本文以友盟统计为例。

#批量打包方式一:Gradle方式
我相信现在应该很多开发环境都是AndroidStudio了,对Gradle相对还是熟悉的。如果您使用的是Eclipse也没有关系,用AndroidStudio导入Eclipse工程,或者把gradle配置放在Eclipse工程下(因为AndroidStudio和Eclipse的工程目录有些差别,把对应的目录配置对即可)
首先我们使用AndroidStudio新建一个工程,名叫AndroidBatchApk,工程结构如下:
打开AndroidManifest.xml文件 添加友盟的渠道配置如下:

这里写图片描述

在MainActivity 显示渠道名代码:


public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);String channel = ManifestUtil.getMetaDataFromAppication(this, "UMENG_CHANNEL");//String channel = ManifestUtil.getUmengChannel(this);((TextView) findViewById(R.id.tv_channel)).setText(channel);}
}
<meta-dataandroid:name="UMENG_APPKEY"android:value="Your UMENG_APPKEY" /><meta-dataandroid:name="UMENG_CHANNEL"android:value="${UMENG_CHANNEL_VALUE}" /> //${UMENG_CHANNEL_VALUE}是个占位符
 打开app目录下的build.gradle文件,修改成如下形式:
apply plugin: 'com.android.application'android {compileSdkVersion 22buildToolsVersion "22.0.1"packagingOptions {exclude 'META-INF/NOTICE.txt'exclude 'META-INF/LICENSE.txt'}//签名signingConfigs {release {//storeFile file("../yourapp.keystore")storeFile file("keystore_apk.jks")storePassword "123456"keyAlias "apk"keyPassword "123456"}}buildTypes {release {// 不显示Log//buildConfigField "boolean", "LOG_DEBUG", "false"//minifyEnabled true //混淆zipAlignEnabled true //内存对齐shrinkResources true //移除无用的resource文件proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'signingConfig signingConfigs.releaseandroid.applicationVariants.all { variant ->def stringsFile = new File(variant.outputs[0].processResources.assetsDir, "abc.txt")stringsFile.mkdir()}applicationVariants.all { variant ->variant.outputs.each { output ->def outputFile = output.outputFileif (outputFile != null && outputFile.name.endsWith('.apk')) {def fileName = "APK_${releaseTime()}_${variant.productFlavors[0].name}.apk"output.outputFile = new File(outputFile.parent, fileName)}}}}}lintOptions {checkReleaseBuilds falseabortOnError falseignoreWarnings true}// 渠道列表productFlavors {_360 {}_91 {}QQ {}appChina {}baidu {}google {}//.....}productFlavors.all { flavor ->flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]}}def releaseTime() {return new Date().format("yyyy-MM-dd HH-mm-ss", TimeZone.getTimeZone("GMT+8"))}dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])compile 'com.android.support:appcompat-v7:22.2.1'}

上面的配置,我们测试打6个包,分别是google 、_360、 _91、 appChina、 QQ、 baidu

打开cmd命令行 进入工程所在的目录,输入命令:gradle build 不出意外将看到如下成功界面:
这里写图片描述

并且在output目录下生成了我们要的apk包(AndroidBatchApk\app\build\outputs\apk)
这里写图片描述

现在用的安装我们的生成apk文件,安装google渠道的apk。
这里写图片描述

到这里我们就通过gradle方式成功的批量打包了,

时间我们只花费了25秒,但是这是最简单的工程,如果是实际的开发中,我们的项目会很大,打包的时间也会花费很长时间,我现在公司的项目,通过这种方式打包,需要30、40分钟左右,这也是挺长的。时间上并不占优势。但是比我们用工具一个个的打apk强太多了。下面为大家界面一种更高效的打包方式。

#批量打包方式二:Python批量打包

首先配置好Python,我用的是Python2.7版本。使用该方式,不把渠道名称放在AndroidManifest.xml 里,而是新建一个空文件,文件名就是渠道名称。该文件放在apk目录的META-INF里。META-INF目录下默认文件列表如下:
这里写图片描述

现在我们要解决两个问题:
##如果在META-INF目录下新建文件?
我们解决第一个问题。首先我们通过AndroidStudio或者Eclipse打一个正式环境的apk安装包,不需要有渠道。
然后按照渠道列表 复制出各个渠道的,然后往apk文件里写入文件为渠道名的空文件。我们使用Python代码来实现该功能,代码如下:

import sys,os,shutil,zipfile,time
apkVersion="1.0"
srcFileName="source.apk"
destDir=os.path.abspath('.')
file=open("channel.txt")def writeChannelToApk(filename,channel):z=zipfile.ZipFile(filename,'a',zipfile.ZIP_DEFLATED)empty_channel_file="META-INF/channel_{channe}".format(channe=channel)target_file="channel.apk"z.write(target_file,empty_channel_file)z.close()print "writeChannelToApkchannel"+channel+","+filename+"\n"def cpFile(srcPath,fileName):destPath = destDir + os.path.sep + fileNameif os.path.exists(srcPath) and not os.path.exists(destPath):shutil.copy(srcPath,destPath)if not os.path.exists(srcFileName):print "sourcefile"+srcFileName+"notexists"sys.exit(1)start = time.clock()for line in file:channel=line.strip('\n').strip()targetFileName="apk_"+channel+"-"+apkVersion+".apk"print "copyfile:"+targetFileNamecpFile(srcFileName,targetFileName)writeChannelToApk(targetFileName,channel)
end = time.clock()print("The function run time is : %.03f seconds" %(end-start))

上面是我编写的Python代码,根据代码我们需要三个文件,一个我们打出的apk文件(source.apk 当然名字可以改)、一个空apk文件(channel.apk)和渠道列表文件(channel.txt) 目录如下:
这里写图片描述

渠道文件内容如下:
360
appChina
wandoujia
91
baidu
QQ
3G
eoe
anzhi
163
hiapk
jifeng
xiaomi
meizu
oppo
lenovo

在命令行输入:python batch_apk.py 回车

这里写图片描述

瞬间完成:
这里写图片描述

解压文件oppo渠道的apk,看看是不是META-INF下是不是有渠道文件:
这里写图片描述

##如果读取META-INF下的渠道文件?

public class ManifestUtil {public static String channel;public static String getUmengChannel(Context context) {//return getMetaDataFromAppication(context, "UMENG_CHANNEL");return getChannel(context);}/*** 获取META-INFO下面的渠道* @param context* @return*/public static String getChannel(Context context) {if (!TextUtils.isEmpty(channel)) {return channel;}ApplicationInfo appinfo = context.getApplicationInfo();String sourceDir = appinfo.sourceDir;ZipFile zipfile = null;final String start_flag = "META-INF/channel_";try {zipfile = new ZipFile(sourceDir);Enumeration<?> entries = zipfile.entries();while (entries.hasMoreElements()) {ZipEntry entry = ((ZipEntry) entries.nextElement());String entryName = entry.getName();if (entryName.contains(start_flag)) {channel = entryName.replaceAll(start_flag, "");return channel;}}} catch (IOException e) {e.printStackTrace();} finally {if (zipfile != null) {try {zipfile.close();} catch (IOException e) {e.printStackTrace();}}}return "";}
}

我们安装oppo渠道的apk看看能否读取到渠道名:
这里写图片描述

通过上面的对比,使用Pyhon往META-INF写入渠道的方式,比gradle方式极大的提高了效率。

资源下载

脚本地址Github

Have Fun !


2017-07-06 更新

由于 Android7.0 的版本上不能安装app,提示 INSTALL_PARSE_FAILED_NO_CERTIFICATES 可以使用美团开源的工具:
打包工具


如果你觉得本文帮助到你,给我个关注和赞呗!

另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图

如果有需要可以移步我的 GitHub -> AndroidAll,里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 由于篇幅原因只展示了 Android 思维导图:
超详细的Android技术栈


http://www.ppmy.cn/news/744363.html

相关文章

IPv6主要知识

IPV6相较于IPV4有如下几点优点 1、“无限”地址空间&#xff0c;地址长度为128比特、海量的地址空间&#xff0c;满足物联网等新兴业务&#xff0c;有利于业务演进及扩展。 2、层次化的地址结构 &#xff0c;相较于IPV4地址&#xff0c;IPV6地址的分配更加规范&#xff0c;利…

二叉树的存储表示

二叉树的数组存储表示 在数据处理过程中二叉树的大小、形态不发生剧烈的动态变化的场合&#xff0c;适宜采用数组方式来表示二叉树的抽象数据类型 1、完全二叉树的数组存储表示 设有一棵完全二叉树&#xff0c;将其所有结点按照层次自顶向下、同一层自左向右进行按序编号1 - n…

Android 点击View实现翻转动画仿金立软件商店会员点击翻转

代码如下 直接使用工具类: import android.animation.Animator; import android.animation.ObjectAnimator; import android.view.View; import android.view.animation.OvershootInterpolator; public class AnimUtil { public static void FlipAnimatorXViewShow(final…

不仅仅是商务旗舰,金立M2017的拍照实力同样给力

近年来金立在商务手机市场不断发力&#xff0c;连续推出了多款专为高端商务人士打造的精品商务手机&#xff0c;其中在去年年底发布商务旗舰金立M2017更是获得了大量高端商务人士的认可。但是值得一提的是该机不仅在商务功能方面做得极为出色&#xff0c;就是在拍照方面金立M20…

22. 查询相同时刻多地登陆的用户

文章目录 题目需求思路一实现一题目来源 题目需求 从登录明细表&#xff08;user_login_detail&#xff09;中查询在相同时刻&#xff0c;多地登陆&#xff08;ip_address不同&#xff09;的用户。 期望结果如下&#xff1a; user_id(用户id)101102104… 需要用到的表&…

Docker学习笔记17

跨主机容器间网络&#xff1a; 实现跨主机容器间通信的工具&#xff1a; 1&#xff09;Pipework 2&#xff09;Flannel 3&#xff09;Weave 4&#xff09;Open V Switch &#xff08;OVS&#xff09; 5&#xff09;Calico 1. Weave&#xff1a; 在每个宿主机上布置一个特…

获取今天日期或今天之前多少天 之后多少天

GetDateStr(AddDayCount) {let dd new Date();dd.setDate(dd.getDate() AddDayCount); //获取AddDayCount天后的日期let y dd.getFullYear();let m dd.getMonth() 1 < 10 ? "0" (dd.getMonth() 1) : dd.getMonth() 1; //获取当前月份的日期&#xff0c;不…

2022中元节前后几天不出门?前三天后三天不能出门是真的吗?

随着中元节的临近&#xff0c;在民间所流传的一些习俗也受到大家的关注&#xff0c;部分地区在中元节前后几天有不出门的说法&#xff0c;那中元节前后几天不出门&#xff1f;前三天后三天不能出门是真的吗&#xff1f; 一、2022中元节前后几天不出门&#xff1f; 中元节前3天…