Java解析APK获取版本信息

前言

Android项目迭代中,常常将APK,APK的信息提交到后台,然后后台开发人员提供API给Android调用,返回迭代更新的内容以及apk,可以我们在提交APK的时候可以会本次更新的版本号比上次低,导致Android前端下载最新的APK无法正常安装,因为Android端覆盖安装的APK必须比之前的APK的版本号要大。

本文运用Android原生工具AAPT在提交APK的时候解析APK的版本号,在提交APK的时候进行判断APK的版本号是否大于上次的版本号,以达到每次迭代APK都是正确的

AAPT 介绍

开发过Android的同学对AAPT并不陌生,AAPT 全称Android Asset Packaging Tool,位于 Android SDK 的build-tools目录下,是Android打包的一个工具,也就是Android最后的APK就是由AAPT生成的,既然APK是由它生成的,AAPT当然也可以解析APK,下图是AAPT所在的位置截图,需要各位同学去下载Android SDK
Java解析APK获取版本信息

有了AAPT我们就能使用AAPT命令对APK进行命令操作,AAPT命令跟Android的adb命令类似,不懂的同学请自行查阅,AAPT命令如下:

查看apk一些信息
aapt d[ump] [–values] WHAT file.{apk} [asset [asset …]]

badging Print the label and icon for the app declared in APK
permissions Print the permissions from the APK.
resources Print the resource table from the APK.
configurations Print the configurations in the APK.
xmltree Print the compiled xmls in the given assets.
xmlstrings Print the strings of the given compiled xml assets
Java解析APK获取版本信息

技术要点

前面我们学习了AAPT的使用以及AAPT的命令,现在让我们用java代码去实现AAPT命令操作APK吧,具体代码如下

String apkPath ="C:\Users\Administrator\Desktop\test\PluginApp-debug-20180622_18-[V 2.1.1].apk";
String aaptTool = aaptToolPath +"/"+ getAaptToolName();
Process process = null;
InputStream inputStream = null;
BufferedReader bufferedReader = null;
try {
ProcessBuilder builder = new ProcessBuilder();
builder.redirectErrorStream(true);
process = builder.command(aaptTool, "d", "badging", apkPath).start();
inputStream = process.getInputStream();
bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
ApkInfo apkInfo = new ApkInfo();
apkInfo.setSize(new File(apkPath).length());
String temp = null;
while ((temp = bufferedReader.readLine()) != null) {
setApkInfoProperty(apkInfo, temp);
}
return apkInfo;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (process != null) {
process.destroy();
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

我们使用了ProcessBuilder执行了AAPT命令获取了执行过后的InputStream,其中InputStream就包含我们想要的信息,让我们开始解析InputStream信息,setApkInfoProperty函数的代码如下:

private void setApkInfoProperty(ApkInfo apkInfo, String source) {
if (source.startsWith(APPLICATION)) {
String[] rs = source.split("( icon=')|'");
apkInfo.setIcon(rs[rs.length - 1]);
} else if (source.startsWith(APPLICATION_ICON)) {
apkInfo.addToIcons(getKeyBeforeColon(source), getPropertyInQuote(source));
} else if (source.startsWith(APPLICATION_LABEL)) {
apkInfo.setLabel(getPropertyInQuote(source));
} else if (source.startsWith(LAUNCHABLE_ACTIVITY)) {
apkInfo.setLaunchableActivity(getPropertyInQuote(source));
} else if (source.startsWith(PACKAGE)) {
String[] packageInfo = source.split(SPLIT_REGEX);
apkInfo.setPackageName(packageInfo[2]);
apkInfo.setVersionCode(packageInfo[4]);
apkInfo.setVersionName(packageInfo[6]);
} else if (source.startsWith(SDK_VERSION)) {
apkInfo.setSdkVersion(getPropertyInQuote(source));
} else if (source.startsWith(TARGET_SDK_VERSION)) {
apkInfo.setTargetSdkVersion(getPropertyInQuote(source));
} else if (source.startsWith(USES_PERMISSION)) {
apkInfo.addToUsesPermissions(getPropertyInQuote(source));
} else if (source.startsWith(USES_FEATURE)) {
apkInfo.addToFeatures(getPropertyInQuote(source));
} else {
}
}

我们的APK信息就全部缓存到了ApkInfo 这个对象身上了

总结

此前看到网上有很多博客解析APK使用的是解析ZIP包,然后解析Manifest文件去实现的,但是那种方法对应APK加密,加壳的无法解析,使用AAPT可以无痕解析任何APK信息

源码下载