news 2026/7/27 1:20:21

Android项目自动批量打包之程序实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android项目自动批量打包之程序实现

Android项目自动批量打包之程序实现

在Android应用分发过程中,经常需要为不同渠道(如应用宝、华为应用市场、小米应用商店等)生成带有唯一渠道标识的APK包,以便统计各渠道的用户来源和活跃度。当渠道数量众多时(例如92个),手动修改配置、逐个打包签名的工作量巨大且容易出错。本文将详细介绍如何通过Java程序实现Android项目的自动批量打包,核心思路是:自动修改配置文件中的渠道号,并调用命令行工具完成编译、打包、签名全流程。

一、背景与需求

因为需要记录不同渠道下APK的使用情况,每一个渠道对应的APK都需要附带自己的渠道号。而当你手动完成92个渠道的打包时,切腹的心都有了!!

现在的任务就是:实现项目的自动更改配置文件的渠道号,自动完成打包、签名工作。

二、尝试过的方案

开始的解决方案是,使用Ant完成自动打包。据说这个方案是可行的,尝试了N多次build.xml的配置,始终运行出错,以后再慢慢研究吧。

下面详细讲解我的实现方案。

三、手动打包流程回顾

首先普及下,如何手动实现一个Android项目的编译、打包、签名的过程。

为了看到效果,先把Eclipse的自动编译开关去掉。

新建一个Android工程。里面除了源码什么都没有了,Eclipse也没有帮你编译生成R文件和class文件。

开启命令行:

第一步:使用aapt工具生成R文件

命令行进入工程目录:

aapt package -f -m -J gen\ -S res\ -M AndroidManifest.xml -I ../android-8/android.jar

参数的意思应该不用解释了。执行命令,会看到在gen/目录下生成了对应资源的R文件。

第二步:使用javac编译Java文件
javac -encoding utf-8 -target 1.5 -d bin/ src/../../../*.java gen/*.java -bootclasspath ../{sdk-path}/android-8/android.jar
第三步:使用dx工具将所有class打包生成dex文件
dx.bat --dex --output=bin/classes.dex bin/classes/

如果有第三方jar包,需要将jar一并打入到dex文件中。在命令后面再附带jar的路径。如:

dx.bat --dex --output=bin/classes.dex bin/classes/ libs/***.jar
第四步:打包资源文件生成resources.ap_
aapt package -f -M AndroidManifest.xml -S res/ -A assets/ -I ../{sdk-path}/android-8/android.jar -F bin/resources.ap_
第五步:生成未签名APK
apkbuilder bin/unsigned.apk -u -z bin/resources.ap_ -f bin/classes.dex -rf src/

如果引入了第三方jar包,还需要加入jar包的引用。在命令的最后加上:-rj libs/

第六步:对未签名的APK进行签名
jarsigner -keystore {keystore path} -storepass *** -keypass {alias pass} -signedjar e:\signed.apk bin/unsigned.apk {alias name}

到此,手动对一个Android工程的编译、打包、签名就完成了。而我的方案也就出来了。每个不同的渠道包有一个不同的渠道属性。可以通过文件流操作循环改变这个属性的值。每次改变完成之后,在程序中调用命令,完成项目的打包签名工作。下面看具体实现。

四、自动批量打包程序实现

我的项目中渠道号是写在strings.xml文件中。其实任何地方都可以,只要你的工程能引用到。

因为Eclipse能够自动编译,所以我们省去了很多麻烦。不需要手动生成R文件,不需要手动编译Java文件。

因为需要替换strings文件中的字段,在strings中设置一个标记变量%channel%,当读取到这个标记时,将标记替换成指定的值。先将strings文件做一个临时备份。读取临时文件,读取的同时修改渠道号并将新的文件写入到res/values/strings.xml目录下。(读取时,因为strings文件有很多中文,注意编码问题)

下面看具体代码实现:

public static final String PROJECT_PATH = "D:\\workspace\\Idea123_v3\\"; //工程的具体路径。后面很多地方会用到 public static void main(String[] args) { int minChannel = 10, maxChannel = 20; // 设置最小渠道,最大渠道号 try { // 先完成class --> dex的打包。因为java文件未曾改变过,所以打包一次就够。 Process process = Runtime.getRuntime().exec( "D:\android-sdk-windows\platform-tools\dx.bat --dex --output=" + PROJECT_PATH + "bin\classes.dex " + PROJECT_PATH + "bin\classes\ " + PROJECT_PATH + "libs\pinyin4j-2.5.0.jar" ); process.waitFor(); process.destroy(); replaceChannel(minChannel, maxChannel); // 替换渠道号 System.out.println("batch pack finished!"); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void replaceChannel(int minChannel, int maxChannel) { try { String outPath = PROJECT_PATH + "res\values\strings.xml"; //输出文件位置 String tempPath = "e:\temp.xml"; //临时文件位置 BufferedReader reader; BufferedWriter writer; for (int channelid = minChannel; channelid <= maxChannel; ++channelid) { String line = null; reader = new BufferedReader(new FileReader(tempPath)); writer = new BufferedWriter(new FileWriter(outPath)); while ((line = reader.readLine()) != null) { if (line.contains("%channel%")) { line = line.replace("%channel%", channelid + ""); //设置渠道号 } writer.write(line + "\r\n"); writer.flush(); } writer.close(); reader.close(); generatePackage(channelid); // 一次渠道号的更改完成。可以进行打包了。 } } catch (Exception e) { e.printStackTrace(); } } public static void generatePackage(int channelid) { try { Process process = null; // 将资源文件打包resources.ap_ process = Runtime.getRuntime().exec( "D:\android-sdk-windows\platform-tools\aapt package -f -M " + PROJECT_PATH + "AndroidManifest.xml -S " + PROJECT_PATH + "res -A " + PROJECT_PATH + "assets -I D:\android-sdk-windows\platforms\android-8\android.jar -F " + PROJECT_PATH + "bin\resources.ap_" ); process.waitFor(); process.destroy(); // 生成未签名的apk process = Runtime.getRuntime().exec( "D:\android-sdk-windows\tools\apkbuilder.bat " + PROJECT_PATH + "bin\Idea123_unsigned.apk -u -z " + PROJECT_PATH + "bin\resources.ap_ -f " + PROJECT_PATH + "bin\classes.dex -rf " + PROJECT_PATH + "src -rj " + PROJECT_PATH + "\libs" ); process.waitFor(); process.destroy(); // 对apk进行签名 process = Runtime.getRuntime().exec( "jarsigner -keystore E:\\idea123 -storepass *** -keypass *** -signedjar " + "E:\\test_apk\\Idea123_" + channelid + ".apk " + PROJECT_PATH + "bin\\Idea123_unsigned.apk idea123" ); process.waitFor(); process.destroy(); System.out.println(channelid + " finished..."); // 一条渠道的打包完成。文件会输出到指定目录 } catch (Exception e) { e.printStackTrace(); } }

五、总结

OK!批量打包的简单实现就此完成。代码有待优化,欢迎指正。

核心步骤总结:

  1. 准备一个包含渠道号占位符(如%channel%)的配置文件(如strings.xml)。
  2. 编写Java程序,循环遍历渠道号范围。
  3. 在每次循环中,读取模板配置文件,替换占位符为当前渠道号,写回项目目录。
  4. 调用aaptapkbuilderjarsigner等命令行工具完成资源打包、APK构建和签名。
  5. 输出以渠道号命名的最终APK文件。

此方案虽然基于较旧的Android SDK工具链(如apkbuilder),但思路清晰,适用于理解自动化打包的基本原理。在现代Android开发中,可考虑使用Gradle productFlavors或第三方插件(如VasDolly)实现更高效、稳定的多渠道打包。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/27 1:16:47

终极指南:5分钟搞定ChromeKeePass自动填充密码

终极指南:5分钟搞定ChromeKeePass自动填充密码 【免费下载链接】ChromeKeePass Chrome extensions for automatically filling credentials from KeePass 项目地址: https://gitcode.com/gh_mirrors/ch/ChromeKeePass 还在为记住各种网站密码而烦恼吗&#x…

作者头像 李华
网站建设 2026/7/27 1:13:15

UE4SS终极指南:5分钟掌握UE4/UE5游戏修改与脚本开发

UE4SS终极指南:5分钟掌握UE4/UE5游戏修改与脚本开发 【免费下载链接】RE-UE4SS Injectable LUA scripting system, SDK generator, live property editor and other dumping utilities for UE4/5 games 项目地址: https://gitcode.com/gh_mirrors/re/RE-UE4SS …

作者头像 李华
网站建设 2026/7/27 1:13:13

深度解析:5个高效技巧打造真实Unity海洋渲染的终极指南

深度解析:5个高效技巧打造真实Unity海洋渲染的终极指南 【免费下载链接】Ceto Ceto: Ocean system for Unity 项目地址: https://gitcode.com/gh_mirrors/ce/Ceto Unity海洋渲染与实时水面模拟是提升游戏场景真实感的核心技术。Ceto作为Unity平台的开源海洋模…

作者头像 李华