一个使用命令行编译Android项目的工具类
简单介绍
编译apk项目须要使用的几个工具,基本都在sdk中,它们各自是(Windows系统):
- 1.aapt.exe
资源打包工具 - 2.android.jar
Android编译工具 - 3.dx.bat
dex文件生成工具 - 4.sdklib.jar
生成apk - 5.jarsigner
签名工具
准备
在打包前。须要的环境例如以下:
1.JDK1.6+
2.Android SDK
3.上述5个工具的路径
打包过程
1.生成R.java文件
比方:
aapt package -f -m -J ./gen -S res -M AndroidManifest.xml -I D:android_sdk_for_studioplatformsandroid-22android.jar
2.清空bin文件夹
清空上次生成的文件
3.编译java文件和jar包
javac -encoding GBK -target 1.5 -bootclasspath D:android_sdk_for_studioplatformsandroid-22android.jar -d bin src
etmobctrl
ormalapk*.java gen
etmobctrl
ormalapkR.java -classpath libs*.jar
4.使用dx工具打包成classes.dex
dx --dex --output=C:Usersmochuan.zhb
ewworkspaceBundleApk5inclasses.dex C:Usersmochuan.zhb
ewworkspaceBundleApk5in
5.编译成资源文件
aapt package -f -M AndroidManifest.xml -S res -I D:android_sdk_for_studioplatformsandroid-22android.jar -F bin
esources.ap_ --non-constant-id
6.使用sdklib.jar工具生成未签名的apk
java -cp D:android_sdk_for_studio oolslibsdklib.jar com.android.sdklib.build.ApkBuilderMain binMyCommond.apk -v -u -z bin
esources.ap_ -f binclasses.dex -rf C:Usersmochuan.zhb
ewworkspaceBundleApk5src
7.使用jarsigner对apk进行签名
jarsigner -verbose -keystore C: est.keystore -storepass 123456 -keypass 123456 -signedjar C:projectdemo-signed.apk C: est.apk test
BuildApkUtils源代码。自己主动生成命令并执行
package com.taobao.trip;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* @Author Zheng Haibo (mochuan)
* @Company Alibaba Group
* @PersonalWebsite http://www.mobctrl.net
* @version $Id: BuildUtils.java, v 0.1 2016年1月8日 下午8:47:42 mochuan.zhb Exp $
* @Description 在Windows平台,怎样使用命令行编译android项目
*/
public class BuildApkUtils {
private static final String ANDROID_JAR_PATH = "D:\android_sdk_for_studio\platforms\android-22\android.jar";
private static final String AAPT_PATH = "D:\mochuan.zhb\android-sdks\build-tools\22.0.1\aapt_alitrip.exe";
private static final String DX_PATH = "D:\android_sdk_for_studio\build-tools\22.0.1\dx.bat";
private static final String SDK_LIB_JAR_PATH = "D:\android_sdk_for_studio\tools\lib\sdklib.jar";
private static final String batchDir = System.getProperty("user.dir")
+ "\batch\";
private String projectDir;
private int packageId = 127;
public BuildApkUtils() {
}
public BuildApkUtils(String projectDir) {
this.projectDir = projectDir;
}
public BuildApkUtils(String projectDir, int packageId) {
this.projectDir = projectDir;
this.packageId = packageId;
}
public void buildUnsingedApk() {
clearDir(batchDir);
clearDir(projectDir + "\bin");
generateR(projectDir, packageId);
compileJavaFiles(projectDir);
buildDexFile(projectDir);
complieResources(projectDir, packageId);
buildUnsignedApk(projectDir, "unsigned.apk");
mergeExeBatchFiles();
}
/**
* 第一步:产生R文件
*
* @param projectDir
* @param packageId
*/
private static void generateR(String projectDir, int packageId) {
StringBuffer command = new StringBuffer();
command.append(AAPT_PATH).append(" package -f -m -J ")
.append(projectDir).append("\gen ").append("-S ")
.append(projectDir).append("\res ").append("-M ")
.append(projectDir).append("\AndroidManifest.xml ")
.append(" -A ").append(projectDir).append("\assets ")
.append("-I ").append(ANDROID_JAR_PATH)
.append(" --non-constant-id -x --package-id ")
.append(packageId);
buildExeBatchFiles(command.toString(), "1.bat");
}
/**
* 编译java文件
*
* @param projectDir
*/
private static void compileJavaFiles(String projectDir) {
StringBuffer command = new StringBuffer();
command.append("javac -target 1.5 -bootclasspath ")
.append(ANDROID_JAR_PATH).append(" -d ").append(projectDir)
.append("\bin ");
List<String> javaFilePaths = new ArrayList<String>();
findJavaFiles(projectDir + "\src", javaFilePaths);
findJavaFiles(projectDir + "\gen", javaFilePaths);
for (String javaPath : javaFilePaths) {
command.append(javaPath).append(" ");
}
command.append("-classpath ").append(projectDir)
.append("\libs\.*jar");
buildExeBatchFiles(command.toString(), "2.bat");
}
/**
* 创建dex文件
*
* @param projectDir
*/
private static void buildDexFile(String projectDir) {
StringBuffer command = new StringBuffer();
command.append(DX_PATH).append(" --dex --output=").append(projectDir)
.append("\bin\classes.dex").append(" ").append(projectDir)
.append("\bin");
buildExeBatchFiles(command.toString(), "3.bat");
}
/**
* 编译资源文件
*
* @param projectDir
*/
private static void complieResources(String projectDir, int packageId) {
StringBuffer command = new StringBuffer();
command.append(AAPT_PATH).append(" package -f -M ").append(projectDir)
.append("\AndroidManifest.xml ").append("-S ")
.append(projectDir).append("\res ").append("-I ")
.append(ANDROID_JAR_PATH).append(" -A ").append(projectDir)
.append("\assets ").append(" -F ").append(projectDir)
.append("\bin\resources.ap_")
.append(" --non-constant-id -x --package-id ")
.append(packageId);
buildExeBatchFiles(command.toString(), "4.bat");
}
/**
* 生成未签名的apk
*
* @param projectDir
* @param apkName
*/
private static void buildUnsignedApk(String projectDir, String apkName) {
StringBuffer command = new StringBuffer();
command.append("java -cp ").append(SDK_LIB_JAR_PATH)
.append(" com.android.sdklib.build.ApkBuilderMain ")
.append(projectDir).append("\bin\").append(apkName)
.append(" -v -u -z ").append(projectDir)
.append("\bin\resources.ap_").append(" -f ")
.append(projectDir).append("\bin\classes.dex")
.append(" -rf ").append(projectDir).append("\src");
buildExeBatchFiles(command.toString(), "5.bat");
}
/**
* 递归查找
*
* @param projectDir
* @param javaFilePaths
*/
private static void findJavaFiles(String projectDir,
List<String> javaFilePaths) {
File file = new File(projectDir);
File[] files = file.listFiles();
if (files == null || files.length == 0) {
return;
}
for (File f : files) {
if (f.isDirectory()) {
findJavaFiles(f.getAbsolutePath(), javaFilePaths);
} else {
if (f.getAbsolutePath().endsWith(".java")) {
javaFilePaths.add(f.getAbsolutePath());
}
}
}
}
/**
* 清理文件夹
*
* @param projectDir
*/
private static void clearDir(String projectDir) {
File file = new File(projectDir);
File[] files = file.listFiles();
if (files == null || files.length == 0) {
return;
}
for (File f : files) {
if (f.isDirectory()) {
clearDir(f.getAbsolutePath());
} else {
f.delete();
}
}
}
/**
* 创建批处理文件
*
* @param command
* @param file
*/
private static void buildExeBatchFiles(String command, String fileName) {
System.out.println(command);
if (!new File(batchDir).exists()) {
new File(batchDir).mkdirs();
}
String filePath = batchDir + fileName;
try {
writeFile(filePath, command);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void mergeExeBatchFiles() {
File file = new File(batchDir);
System.out.println("debug:current path = " + file.getAbsolutePath());
File[] files = file.listFiles();
if (files == null || files.length == 0) {
return;
}
Arrays.sort(files, new Comparator<File>() {
public int compare(File file1, File file2) {
if (file1.lastModified() > file2.lastModified()) {
return 1;
} else if (file1.lastModified() < file2.lastModified()) {
return -1;
} else {
return 0;
}
}
});
StringBuffer command = new StringBuffer();
for (File f : files) {
command.append("call ").append(f.getAbsolutePath()).append("
");
}
try {
String filePath = batchDir + "build.bat";
writeFile(filePath, command.toString());
Runtime.getRuntime().exec("cmd /c start " + filePath);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 写文件
*
* @param filePath
* @param sets
* @throws IOException
*/
private static void writeFile(String filePath, String content)
throws IOException {
FileWriter fw = new FileWriter(filePath);
PrintWriter out = new PrintWriter(fw);
out.write(content);
out.println();
fw.close();
out.close();
}
}
使用
执行的时候,我们仅仅须要传入项目路径就可以:
public class BuildMain {
private static final String PROJECT_PATH = "C:\Users\mochuan.zhb\newworkspace\BundleApk5";
private static final int PACKAGE_ID = 5;
public static void main(String[] args) {
new BuildApkUtils(PROJECT_PATH, PACKAGE_ID).buildUnsingedApk();
}
}
执行过程中。会将生成的命令打印出来:
D:mochuan.zhbandroid-sdksuild-tools22.0.1aapt_alitrip.exe package -f -m -J C:Usersmochuan.zhb
ewworkspaceBundleApk5gen -S C:Usersmochuan.zhb
ewworkspaceBundleApk5
es -M C:Usersmochuan.zhb
ewworkspaceBundleApk5AndroidManifest.xml -A C:Usersmochuan.zhb
ewworkspaceBundleApk5assets -I D:android_sdk_for_studioplatformsandroid-22android.jar --non-constant-id -x --package-id 5
javac -target 1.5 -bootclasspath D:android_sdk_for_studioplatformsandroid-22android.jar -d C:Usersmochuan.zhb
ewworkspaceBundleApk5in C:Usersmochuan.zhb
ewworkspaceBundleApk5src
etmobctrl
ormalapkBaseActivity.java C:Usersmochuan.zhb
ewworkspaceBundleApk5src
etmobctrl
ormalapkBundleActivity.java C:Usersmochuan.zhb
ewworkspaceBundleApk5src
etmobctrl
ormalapkFileUtils.java C:Usersmochuan.zhb
ewworkspaceBundleApk5src
etmobctrl
ormalapkUtils.java C:Usersmochuan.zhb
ewworkspaceBundleApk5gen
etmobctrl
ormalapkBuildConfig.java C:Usersmochuan.zhb
ewworkspaceBundleApk5gen
etmobctrl
ormalapkR.java -classpath C:Usersmochuan.zhb
ewworkspaceBundleApk5libs.*jar
D:android_sdk_for_studiouild-tools22.0.1dx.bat --dex --output=C:Usersmochuan.zhb
ewworkspaceBundleApk5inclasses.dex C:Usersmochuan.zhb
ewworkspaceBundleApk5in
D:mochuan.zhbandroid-sdksuild-tools22.0.1aapt_alitrip.exe package -f -M C:Usersmochuan.zhb
ewworkspaceBundleApk5AndroidManifest.xml -S C:Usersmochuan.zhb
ewworkspaceBundleApk5
es -I D:android_sdk_for_studioplatformsandroid-22android.jar -A C:Usersmochuan.zhb
ewworkspaceBundleApk5assets -F C:Usersmochuan.zhb
ewworkspaceBundleApk5in
esources.ap_ --non-constant-id -x --package-id 5
java -cp D:android_sdk_for_studio oolslibsdklib.jar com.android.sdklib.build.ApkBuilderMain C:Usersmochuan.zhb
ewworkspaceBundleApk5inunsigned.apk -v -u -z C:Usersmochuan.zhb
ewworkspaceBundleApk5in
esources.ap_ -f C:Usersmochuan.zhb
ewworkspaceBundleApk5inclasses.dex -rf C:Usersmochuan.zhb
ewworkspaceBundleApk5src
參考
1.Android应用程序资源的编译和打包过程分析
2.Android 自己主动编译、打包生成apk文件 1 - 命令行方式
3.使用ANT打包Android应用
4.怎样改动android aapt源代码实现自己定义package ID
Author
郑海波-莫川