解密ADT第四篇-编译源码

      经过前面三篇的介绍,我相信大家应该知道ADT在编译 打包 AIR FOR ANDROID项目的时候的整个工作流程,所以关于流程原理上的东西 在这里就不多赘述了,ADT编译android源码相比编译资源来说就简单很多,大家直接看源代码注释就能看明白.这一篇分析的是 ADT操作androidSD之中的dx工具把 一堆jar文件编译成dex文件的操作过程,我们都知道 每一个apk文件都需要一个dex文件,而dex文件则是可以被android设备直接执行的文件,就类似与swf文件对于flash player而言.对于想了解更多android程序原理和结构的同学,我推荐大家去购买由国内知名安全论坛看雪学院出品的一本超级好书:android软件安全与逆向分析.

不够这本书需要一定的android基础.现在我们知道dex文件便是android设备最终可执行的文件 那么我们的ADT是如何生成dex文件的呢?下面我们直接看代码.

 

  • ADT编译android项目源码
//AppEntry_release.jar 里面包括了默认的启动activity样本代码
InputStream inClsJarStream;
if ((m_configType.equals("apk")) || (m_configType.equals("apk-emulator")) || (m_configType.equals("apk-captive-runtime")))
{
	inClsJarStream = getClass().getResourceAsStream("Dex/AppEntry_release.jar");
}
else
{
	InputStream inClsJarStream;
	if ((m_configType.equals("apk-debug")) || (m_configType.equals("apk-profile")))
	{
		inClsJarStream = getClass().getResourceAsStream("Dex/AppEntry_debug.jar");
	}
	else
	{
		throw new IOException("Unknown APK Config Type");
	}
}
InputStream inClsJarStream;
File tempDir = Utils.createTempDirectory(this.OsTempDir);
tempDir.deleteOnExit();

//新建一个jar 编译进去APK的jar 
String inputJarPath = tempDir.getCanonicalPath() + "/inputJAR.jar";
//生成的dex可执行文件名
String outputDexPath = tempDir.getCanonicalPath() + "/outputDEX.dex";
//新建一个jar  运行时框架库
String flashRuntimeExtensionsJarPath = tempDir.getCanonicalPath() + "/FlashRuntimeExtensions.jar";

File inputJar = new File(inputJarPath);
OutputStream outTmpClsJarStream = new FileOutputStream(inputJar);
byte[] buf = new byte[1024];
int len;
//AppEntry_release.jar写入新建的inputJAR.jar
while ((len = inClsJarStream.read(buf)) > 0)
	outTmpClsJarStream.write(buf, 0, len);
outTmpClsJarStream.close();
inClsJarStream.close();

//把Dex/FlashRuntimeExtensions.jar写入新建的FlashRuntimeExtensions.jar
InputStream inFlashRuntimeJarStream = getClass().getResourceAsStream("Dex/FlashRuntimeExtensions.jar");
File flashRuntimeExtensionsJar = new File(flashRuntimeExtensionsJarPath);
OutputStream outTmpFlashRuntimeJarStream = new FileOutputStream(flashRuntimeExtensionsJar);
while ((len = inFlashRuntimeJarStream.read(buf)) > 0)
	outTmpFlashRuntimeJarStream.write(buf, 0, len);
outTmpFlashRuntimeJarStream.close();
inFlashRuntimeJarStream.close();
String dxJarPath;
String dxJarPath;
//获取编译源码工具  它就是androidSDK的dx.jar工具.
if (getAndroidSDKDirectory() != null)
{
	dxJarPath = getAndroidSDKPlatformToolPath() + File.separator + "lib" + File.separator + "dx.jar";
}
else
{
	dxJarPath = Utils.getSDKLibDir() + File.separator + "android" + File.separator + "bin" + File.separator + "dx.jar";
}
if (!new File(dxJarPath).exists()) {
	throw new SDKDamagedException(dxJarPath);
}
List dexCommand = new LinkedList();
//调用java执行dx.jar程序
dexCommand.add("java");

//制定java虚拟机内存大小
//-----------------------------------------------------
//解密JAV内存不足:这里便是ADT获取java启动内存限制参数的位置
//只要在 sdk/bin 下的 adt.bat 或者 adt文件中
//加入 -Xmx512M或者-Xmx1024M
//便可拜托困扰我们已久的 java 内存爆掉的问题.
//------------------------------------------------------
for (String vmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
	if ((vmArg.startsWith("-Xms")) || (vmArg.startsWith("-Xmx"))) {
		dexCommand.add(vmArg);
	}
}
dexCommand.add("-jar");
dexCommand.add(dxJarPath);
//制定输出dex文件
dexCommand.add("--dex");
//指定输出路径
//tempDir.getCanonicalPath() + "/outputDEX.dex"
dexCommand.add("--output=" + outputDexPath);
//AppEntry_release.jar
dexCommand.add(inputJarPath);

//上上一篇 aapt编译的资源jar:resources.jar
if (this.m_resourceJar != null)
{
	dexCommand.add(this.m_resourceJar.getCanonicalPath());
}
//若带运行时 则加入 android/lib/runtimeClasses.jar
//这个在AIRSDK中  AIRSDK/lib/android/lib
if (hasCaptiveRuntime())
{
	File runtimeClassesJar = new File(Utils.getSDKLibDir(), "android/lib/runtimeClasses.jar");
	if (!runtimeClassesJar.exists())
		throw new SDKDamagedException(runtimeClassesJar.getCanonicalPath());
	dexCommand.add(runtimeClassesJar.getAbsolutePath());
}

//如果有ANE 则 加入FlashRuntimeExtensions.jar
//加入ANE编写合计后的jar文件
//PS:正因为这里加入了,所以编写ANE合计jar的时候
//不需要合计FlashRuntimeExtensions.jar
int numberOfExtensions = this.m_extensionsJars == null ? 0 : this.m_extensionsJars.length;
if (numberOfExtensions > 0)
{
	dexCommand.add(flashRuntimeExtensionsJarPath);
	for (int j = 0; j < numberOfExtensions; j++)
	{
		dexCommand.add(this.m_extensionsJars[j].getCanonicalPath());
	}
}

//制定输出错误信息的字符串
String dxOutputString = null;
try {
	ProcessBuilder pb = new ProcessBuilder(dexCommand);

	Map env = pb.environment();

	Process p = pb.start();
	ByteArrayOutputStream dxOutput = new ByteArrayOutputStream();
	new Utils.OutputEater(p.getErrorStream(), dxOutput).start();
	new Utils.OutputEater(p.getInputStream()).start();
	p.waitFor();
	if (p.exitValue() != 0)
		dxOutputString = new String(dxOutput.toByteArray(), "UTF-8");
}
catch (Exception e)
{
	throw new IOException("Unable to run dx");
}
//dx tool failed!
//dx tool failed!
//大家在打包APK发生错误的时候都应该看过这个错误信息弹出框.
if (dxOutputString != null)
{
	throw new ADTException("dx tool failed:" + dxOutputString.toString(), 17);
}
 
  • 分析代码后得出的完整命令如下
//调用java执行dx.jar
java 
//设定内存限制 举个例子 如下
-Xmx512M -Xmz256M
//调用 dx.jar
-jar dx.jar
//编译dex文件选项 
--dex 
//指定编译完成后的文件输出
--outpath=D:/out/outdex.dex
//启动类的模版类
AppEntry_release.jar
//资源ID所在的类 就是R.java(R.class文件) 
resource.jar
//运行时类
android/lib/runtimeClasses.jar
//下面的ANE类库
FlashRuntimeExtensions.jar
[add ane is jar ]

到现在,应该对整个编译源码过程有了大致的了解.
执行完这个函数后 会生成一个dex文件 用于下一步:打包APK

1 评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注