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 --outputbin/classes.dex bin/classes/如果有第三方jar包需要将jar一并打入到dex文件中。在命令后面再附带jar的路径。如dx.bat --dex --outputbin/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_第五步生成未签名APKapkbuilder 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 lt; 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批量打包的简单实现就此完成。代码有待优化欢迎指正。核心步骤总结准备一个包含渠道号占位符如%channel%的配置文件如strings.xml。编写Java程序循环遍历渠道号范围。在每次循环中读取模板配置文件替换占位符为当前渠道号写回项目目录。调用aapt、apkbuilder、jarsigner等命令行工具完成资源打包、APK构建和签名。输出以渠道号命名的最终APK文件。此方案虽然基于较旧的Android SDK工具链如apkbuilder但思路清晰适用于理解自动化打包的基本原理。在现代Android开发中可考虑使用Gradle productFlavors或第三方插件如VasDolly实现更高效、稳定的多渠道打包。