有时候,我们上传apk或者是ipa文件的时候,是需要读取到里面的一些信息的,比如软件的包名,以及其版本信息等。在网上搜索了一下资料 , 找了很多版本,对于apk文件的版本号,一直读取不到,在这里,笔者自己总结了,读取apk、ipa文件的一些代码,大家可以参考下,去其糟粕,取其精华。以便适用于自己的需求。
如果大家希望解析到图标的话,可以看我后面写的博文
下面会提供源码给大家,我用的开发工具是eclipse,直接导入就可以,jar包也是我已经下载好的,大家可以免积分拿去。本来里面是有2个apk、2个ipa文件提供测试的,但是由于文件太大,上传不了源码,所以就删除了一个最大的ipa包
apk jar包地址:java解析apk所需jar包下载 ipa jar包地址:java解析ipa所需jar包下载
首先看一下我的项目目录
下面请看代码
package com.zsl.cn;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import org.apkinfo.api.util.AXmlResourceParser;
import org.apkinfo.api.util.TypedValue;
import org.apkinfo.api.util.XmlPullParser;
import com.dd.plist.NSDictionary;
import com.dd.plist.NSString;
import com.dd.plist.PropertyListParser;
/**
*
* @author ZSL
*
*/
public final class ReadUtil {
/**
* 读取apk
* @param apkUrl
* @return
*/
public static Map<String,Object> readAPK(String apkUrl){
ZipFile zipFile;
Map<String,Object> map = new HashMap<String, Object>();
try {
zipFile = new ZipFile(apkUrl);
Enumeration<?> enumeration = zipFile.entries();
ZipEntry zipEntry = null;
while (enumeration.hasMoreElements()) {
zipEntry = (ZipEntry) enumeration.nextElement();
if (zipEntry.isDirectory()) {
} else {
if ("androidmanifest.xml".equals(zipEntry.getName().toLowerCase())) {
AXmlResourceParser parser = new AXmlResourceParser();
parser.open(zipFile.getInputStream(zipEntry));
while (true) {
int type = parser.next();
if (type == XmlPullParser.END_DOCUMENT) {
break;
}
String name = parser.getName();
if(null != name && name.toLowerCase().equals("manifest")){
for (int i = 0; i != parser.getAttributeCount(); i++) {
if ("versionName".equals(parser.getAttributeName(i))) {
String versionName = getAttributeValue(parser, i);
if(null == versionName){
versionName = "";
}
map.put("versionName", versionName);
} else if ("package".equals(parser.getAttributeName(i))) {
String packageName = getAttributeValue(parser, i);
if(null == packageName){
packageName = "";
}
map.put("package", packageName);
} else if("versionCode".equals(parser.getAttributeName(i))){
String versionCode = getAttributeValue(parser, i);
if(null == versionCode){
versionCode = "";
}
map.put("versionCode", versionCode);
}
}
break;
}
}
}
}
}
zipFile.close();
} catch (Exception e) {
map.put("code", "fail");
map.put("error","读取apk失败");
}
return map;
}
private static String getAttributeValue(AXmlResourceParser parser, int index) {
int type = parser.getAttributeValueType(index);
int data = parser.getAttributeValueData(index);
if (type == TypedValue.TYPE_STRING) {
return parser.getAttributeValue(index);
}
if (type == TypedValue.TYPE_ATTRIBUTE) {
return String.format("?%s%08X", getPackage(data), data);
}
if (type == TypedValue.TYPE_REFERENCE) {
return String.format("@%s%08X", getPackage(data), data);
}
if (type == TypedValue.TYPE_FLOAT) {
return String.valueOf(Float.intBitsToFloat(data));
}
if (type == TypedValue.TYPE_INT_HEX) {
return String.format("0x%08X", data);
}
if (type == TypedValue.TYPE_INT_BOOLEAN) {
return data != 0 ? "true" : "false";
}
if (type == TypedValue.TYPE_DIMENSION) {
return Float.toString(complexToFloat(data)) + DIMENSION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK];
}
if (type == TypedValue.TYPE_FRACTION) {
return Float.toString(complexToFloat(data)) + FRACTION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK];
}
if (type >= TypedValue.TYPE_FIRST_COLOR_INT && type <= TypedValue.TYPE_LAST_COLOR_INT) {
return String.format("#%08X", data);
}
if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) {
return String.valueOf(data);
}
return String.format("<0x%X, type 0x%02X>", data, type);
}
private static String getPackage(int id) {
if (id >>> 24 == 1) {
return "android:";
}
return "";
}
// / ILLEGAL STUFF, DONT LOOK :)
public static float complexToFloat(int complex) {
return (float) (complex & 0xFFFFFF00) * RADIX_MULTS[(complex >> 4) & 3];
}
private static final float RADIX_MULTS[] =
{
0.00390625F, 3.051758E-005F,
1.192093E-007F, 4.656613E-010F
};
private static final String DIMENSION_UNITS[] = { "px", "dip", "sp", "pt", "in", "mm", "", "" };
private static final String FRACTION_UNITS[] = { "%", "%p", "", "", "", "", "", "" };
/**
* 读取ipa
*/
public static Map<String,Object> readIPA(String ipaURL){
Map<String,Object> map = new HashMap<String,Object>();
try {
File file = new File(ipaURL);
InputStream is = new FileInputStream(file);
InputStream is2 = new FileInputStream(file);
ZipInputStream zipIns = new ZipInputStream(is);
ZipInputStream zipIns2 = new ZipInputStream(is2);
ZipEntry ze;
ZipEntry ze2;
InputStream infoIs = null;
NSDictionary rootDict = null;
String icon = null;
while ((ze = zipIns.getNextEntry()) != null) {
if (!ze.isDirectory()) {
String name = ze.getName();
if (null != name &&
name.toLowerCase().contains(".app/info.plist")) {
ByteArrayOutputStream _copy = new
ByteArrayOutputStream();
int chunk = 0;
byte[] data = new byte[1024];
while(-1!=(chunk=zipIns.read(data))){
_copy.write(data, 0, chunk);
}
infoIs = new ByteArrayInputStream(_copy.toByteArray());
rootDict = (NSDictionary) PropertyListParser.parse(infoIs);
//我们可以根据info.plist结构获取任意我们需要的东西
//比如下面我获取图标名称,图标的目录结构请下面图片
//获取图标名称
NSDictionary iconDict = (NSDictionary) rootDict.get("CFBundleIcons");
while (null != iconDict) {
if(iconDict.containsKey("CFBundlePrimaryIcon")){
NSDictionary CFBundlePrimaryIcon = (NSDictionary)iconDict.get("CFBundlePrimaryIcon");
if(CFBundlePrimaryIcon.containsKey("CFBundleIconFiles")){
NSArray CFBundleIconFiles =(NSArray)CFBundlePrimaryIcon.get("CFBundleIconFiles");
icon = CFBundleIconFiles.getArray()[0].toString();
if(icon.contains(".png")){
icon = icon.replace(".png", "");
}
System.out.println("获取icon名称:" + icon);
break;
}
}
}
break;
}
}
}
//根据图标名称下载图标文件到指定位置
while ((ze2 = zipIns2.getNextEntry()) != null) {
if (!ze2.isDirectory()) {
String name = ze2.getName();
System.out.println(name);
if(name.contains(icon.trim())){
System.out.println(11111);
FileOutputStream fos = new FileOutputStream(new File("E:\\python\\img\\icon.png"));
int chunk = 0;
byte[] data = new byte[1024];
while(-1!=(chunk=zipIns2.read(data))){
fos.write(data, 0, chunk);
}
fos.close();
break;
}
}
}
//如果想要查看有哪些key ,可以把下面注释放开
// for (String keyName : rootDict.allKeys()) {
// System.out.println(keyName + ":" + rootDict.get(keyName).toString());
// }
// 应用包名
NSString parameters = (NSString) rootDict.get("CFBundleIdentifier");
map.put("package", parameters.toString());
// 应用版本名
parameters = (NSString) rootDict.objectForKey("CFBundleShortVersionString");
map.put("versionName", parameters.toString());
//应用版本号
parameters = (NSString) rootDict.get("CFBundleVersion");
map.put("versionCode", parameters.toString());
/
infoIs.close();
is.close();
zipIns.close();
} catch (Exception e) {
map.put("code", "fail");
map.put("error","读取ipa文件失败");
}
return map;
}
public static void main(String[] args) {
System.out.println("======apk=========");
String apkUrl = "src/shenmiaotaowang_966.apk";
Map<String,Object> mapApk = ReadUtil.readAPK(apkUrl);
for (String key : mapApk.keySet()) {
System.out.println(key + ":" + mapApk.get(key));
}
System.out.println("======ipa==========");
String ipaUrl = "src/IM.ipa";
Map<String,Object> mapIpa = ReadUtil.readIPA(ipaUrl);
for (String key : mapIpa.keySet()) {
System.out.println(key + ":" + mapIpa.get(key));
}
}
}
下面附上源码一份:java读取apk、ipa源码完整版
总结:对于ipa文件的解析,我们可以根据info.plist文件结构进行解析,获取我们需要的值,如果我们key对应的是dict ,那么我们就用NSDictionary来获取,如果是key下面是array,那么我们就用NSArray来获取,这时获取到的是一个数组。
我们可以看到上面解析到的图片是黑色的,这个确实是这样子的,在window或者linux下都是黑色的,只有在mac下才是正常的,应该是ios开发者压缩的结果。不过不要担心,可以看后面的文章,怎么把图片反编译成正常图片。
补充:
1. 2016-9-14补充获取ipa图标,并下载到指定位置
如果大家希望解析到图标的话,可以看我后面写的博文