Android apk编译,打包过程详解及个人心得,附上java+doc写的自动化批量打包代码

本文详细解析了Android项目的自动化编译与打包流程,包括资源文件处理、Java编译、DEX转换、APK生成及签名等多个步骤,并提供了一个Java实现的自动化打包程序示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

声明:作者博客地址找不到了,找到了一定附上。

------------------------------------下面是摘抄的-------------------------------------

现在很多人想对Android工程的编译和打包进行自动化,比如建立每日构建系统、自动生成发布文件等等。这些都需要我们对Android工程的编译和打包有一个深入的理解,至少要知道它的每一步都做了什么,需要什么环境和工具,输入和输出是什么。那么我们就来挖掘一下Android的编译过程中的细节。

首先,我们假定你的系统(什么系统都行,不限于Linux还是Windows系统,当然,我在这里默认使用Linux系统来举例子,但在 Windows中几乎没有什么差别)已经安装了JDK和Android SDK。再假定你的Android SDK的路径是ANDROID_SDK_HOME,你想要编译的Android OS版本是ANDROID_OS_VERSION(比如android-1.6、android-8、android-10等)。

我们重点关心的是(1)这个过程的输入是什么?(2)这个过程的输出是什么?(3)这个过程使用了什么工具?至于使用什么参数,可以自己去看对应命令的帮助文件,或者在网上搜索,这不是本文的重点。

步骤中提到的工具如下表:

名称 功能介绍 在操作系统中的路径
aapt Android资源打包工具 ${ANDROID_SDK_HOME}/platform-tools/appt
aidl Android接口描述语言转化为.java文件的工具 ${ANDROID_SDK_HOME}/platform-tools/aidl
javac Java Compiler ${JDK_HOME}/javac或/usr/bin/javac
dex 转化.class文件为Davik VM能识别的.dex文件 ${ANDROID_SDK_HOME}/platform-tools/dx
apkbuilder 生成apk包 ${ANDROID_SDK_HOME}/tools/opkbuilder
jarsigner .jar文件的签名工具 ${JDK_HOME}/jarsigner或/usr/bin/jarsigner
zipalign 字节码对齐工具 ${ANDROID_SDK_HOME}/tools/zipalign

第一步:打包资源文件,生成R.java文件
【输入】Resource文件(就是工程中res中的文件)、Assets文件(相当于另外一种资源,这种资源Android系统并不像对res中的文件那样优化它)、AndroidManifest.xml文件(包名就是从这里读取的,因为生成R.java文件需要包名)、Android基础类库(Android.jar文件)
【输出】打包好的资源(一般在Android工程的bin目录可以看到一个叫resources.ap_的文件就是它了)、R.java文件(在gen目录中,大家应该很熟悉了)
【工具】aapt工具,它的路径在${ANDROID_SDK_HOME}/platform-tools/aapt(如果你使用的是Windows系统,按惯例路径应该这样写:%ANDROID_SDK_HOME%\platform-tools\aapt.exe,下同)。

第二步:处理AIDL文件,生成对应的.java文件(当然,有很多工程没有用到AIDL,那这个过程就可以省了)
【输入】源码文件、aidl文件、framework.aidl文件
【输出】对应的.java文件
【工具】aidl工具

第三步:编译Java文件,生成对应的.class文件
【输入】源码文件(包括R.java和AIDL生成的.java文件)、库文件(.jar文件)
【输出】.class文件
【工具】javac工具

第四步:把.class文件转化成Davik VM支持的.dex文件
【输入】 .class文件(包括Aidl生成.class文件,R生成的.class文件,源文件生成的.class文件),库文件(.jar文件)
【输出】.dex文件
【工具】javac工具

第五步:打包生成未签名的.apk文件
【输入】打包后的资源文件、打包后类文件(.dex文件)、libs文件(包括.so文件,当然很多工程都没有这样的文件,如果你不使用C/C++开发的话)
【输出】未签名的.apk文件
【工具】apkbuilder工具

第六步:对未签名.apk文件进行签名
【输入】未签名的.apk文件
【输出】签名的.apk文件
【工具】jarsigner

第七步:对签名后的.apk文件进行对齐处理(不进行对齐处理是不能发布到Google Market的
【输入】签名后的.apk文件
【输出】对齐后的.apk文件
【工具】zipalign工具

知道了这些细节之后,我们就可以实现很多我们想实现东西了,比如:自动化,我们可以使用某种脚本,像Windows下的批处理,linux下的Bash,Java下的Ant,Python、Perl这样的脚本语言,甚至直接用Java、.net这们的强类型语言也是可以的。如果你真正弄懂了上面的步骤,了解了编译打包过程的本质,你完全可以以你想要的任何方式实现它的自动化,这才是真正的“举一反三,以不变应万变”。再比如,对Android SDK的精简,大家知道,现在Android SDK动辙几百兆,我们完全可以应用上面的知识,只保留必要的工具,把SDK精简到10M以下。当然,还可以做很多事情,前提是你真正弄懂了它。

------------------------------------上面是摘抄的-------------------------------------

下面是我用java+doc来写的自动化批量打包程序

主要代码:

/**
 * 
 * @author liqiang
 * 
 */
public class ApkTool {

	private ConsoleText consoleText;//LOG窗口
	private String oldName = "";
	private String tempApkPath = "";
	private String tempDirPath = "";
	private String xmlPath = "";
	private String keystore = "";
	private String keystorePass = "xiaopeng";
	private String keystoreAliasName = "xiaopeng.keystore";

	public ApkTool(ConsoleText text) {
		this.consoleText = text;
		init();
	}

	public void init() {
		File in = new File("in");
		if (!in.exists()) {
			in.mkdirs();
		}
		File out = new File("out");
		if (!out.exists()) {
			out.mkdirs();
		}
		tempApkPath = getAbsolutePath("in\\temp.apk");//缓存路径
		tempDirPath = getAbsolutePath("in\\temp");// 解压到的路径
		xmlPath = getAbsolutePath("in\\temp\\AndroidManifest.xml");
		keystore = getAbsolutePath("apktool\\xiaopeng.keystore");
	}

	// 取得APK名字 改名复制到in目录下
	public void loopSaveApkName(String path) throws IOException {
		File form = new File(path);
		oldName = form.getName();// 原APK名
		if (form.exists() && path.endsWith(".apk")) {
			copyFile(form.getAbsolutePath(), tempApkPath);// 复制APK并改名
		}
	}

	// 解包temp.apk
	public void unpack() {
//		tempDirPath = getAbsolutePath("in\\temp");// 解压到的路径
		String command = "cmd.exe /c apktool d -f " + tempApkPath + " " + tempDirPath;
		consoleText.setContent("开始解包.............");
		new Console().runCmd(command, consoleText,"apktool", new ConsoleListener() {
			@Override
			public void onSuccess(String type) {
				if (type.equals("ERROR")) {
					consoleText.setContent("解压完:" + tempDirPath);
				}
			}
		});
	}

	// 修改内容
	public void modifyContent(Channel channel) {
//		XMLUtils.modifyChannel(xmlPath, channel.getName(), channel.getValue());
		XMLUtils.modifyChannel(xmlPath, channel.getValue(), channel.getUmeng());
		consoleText.setContent("apk修改渠道ID完毕.....");
		channel.setTempdir(tempDirPath);
	}


	// 打包APK
	public void packApk(final Channel channel) {
		final String nosign = getAbsolutePath("in\\nosign_" + channel.getId() + ".apk");
		String command = "cmd.exe /c apktool b " + tempDirPath + " " + nosign;
		System.out.println("打包command = " + command);
		new Console().runCmd(command, consoleText,"apktool", new ConsoleListener() {

			@Override
			public void onSuccess(String type) {
				if (type.equals("ERROR")) {
					channel.setNosignPath(nosign);
					consoleText.setContent("apk压缩完毕....." + nosign);
				}
			}
		});
	}

	// 签名APK
	public void signApk(final Channel channel) {
		String noSignPath = channel.getNosignPath();// 没签名的apk路径
		final String signPath = getAbsolutePath("in\\sign_" + channel.getId() + ".apk");// 签名后的apk路径
		
		String command = "cmd.exe /c jarsigner -verbose -keystore "+keystore+" -storepass "+keystorePass+" -signedjar " + signPath + " "+ noSignPath + " "+keystoreAliasName;
		System.out.println("签名command = " + command);
		new Console().runCmd(command, consoleText,"in", new ConsoleListener() {

			@Override
			public void onSuccess(String type) {
				if (type.equals("ERROR")) {
					consoleText.setContent("签名apk完毕....."+signPath);
					channel.setSignPath(signPath);
				}
			}
		});
	}

	public void outputAPK(Channel channel) throws IOException {
		String outPath = getAbsolutePath("out\\" + channel.getName() + "_" + oldName);
		copyFile(channel.getSignPath(), outPath);
	}

	// 清理APK目录
	public void cleanDir() {
		deleteDir(new File("in"));
	}

	public void openDir() {
		try {
			java.awt.Desktop.getDesktop().open(new File("out"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 复制文件
	public void copyFile(String fromPath, String toPath) throws IOException {
		File to = new File(toPath);
		File from = new File(fromPath);
		createFile(to);
		FileUtils.copyFile(from, to);
		consoleText.setContent("正在从" + from + "复制到:" + to);
	}

	// 创建文件
	public void createFile(File file) throws IOException {
		if (file.exists()) {
			file.delete();
		}
		if (!file.getParentFile().exists()) {
			if (!file.getParentFile().mkdirs()) {
				System.out.println("创建目录文件所在的目录失败!");
			}
		}
		file.createNewFile();
	}

	public void deleteDir(File dir) {
		if (dir == null || !dir.exists() || !dir.isDirectory())
			return; // 检查参数
		for (File file : dir.listFiles()) {
			if (file.isFile())
				file.delete(); // 删除所有文件
			else if (file.isDirectory())
				deleteDir(file); // 递规的方式删除文件夹
		}
		dir.delete();// 删除目录本身
	}
	//得到绝对路径
	private String getAbsolutePath(String filePath){
		return new File(filePath).getAbsolutePath();
	}
}

完整代码下载链接:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值