1. 思路:
- apk其实就是一个压缩文件, zipFile 可以在不解压的情况下, 读取压缩包的文件.
2.遍历找到AndroidManifest.xml文件写入流中,此时的文件是乱码的, 下一步是解码.
- 通过Android提供的AXMLPrinter.jar工具解码, 把, 查看AXMLPrinter.jar源码, 可以接受一个输入流.把主类复制过来, 修改代码.
4.把解码后的文件写入到InputStream中, 待用.
5.上一步解码后的流, 通过DocumentBuilderFactory进行解析, 获取manifest文件下的结点.
2. 代码实现
- 首先获取到apk文件, 遍历找到AndroidManifest.xml
File[] files = new File(apksDir).listFiles();
for (int i = 0; i < files.length; i++) {
String filePath = files[i].getAbsolutePath();
String fileName = files[i].getName();
if (!filePath.endsWith(".apk")) {
continue;
}
if (fileNameCheck("AndroidManifest.xml", filePath)) {
String packageValue = readZipFile(filePath, fileName);
}
}
/**
* 检查apk中是否有对应的文件
*
* @param fileName
* 要校验的文件
* @param fileRoot
* 应用存放的根目录
* @return 存在: true
*/
public static boolean fileNameCheck(String fileName, String fileRoot) throws IOException {
List<String> slf_fileList = new ArrayList<String>();
slf_fileList = getZipFile(fileRoot);
// for (int i = 0; i < slf_fileList.size(); i++) {
// System.out.println("---" + slf_fileList.get(i));
//
// }
return slf_fileList.contains(fileName);
}
/**
* 获取apk包的所有文件
*
* @param rootName
* apk包存放的根目录
* @return
*/
public static List<String> getZipFile(String rootName) throws IOException {
List<String> list = new ArrayList<String>();
ZipFile slf_zipFile = null;
try {
slf_zipFile = new ZipFile(rootName);
} catch (IOException e) {
// TODO 自动生成 catch 块
e.printStackTrace();
}
Enumeration<?> e = slf_zipFile.entries();
while (e.hasMoreElements()) {
ZipEntry slf_zipEntry = (ZipEntry) e.nextElement();
if (!slf_zipEntry.isDirectory()) {
String filename = slf_zipEntry.getName().substring(slf_zipEntry.getName().lastIndexOf("/") + 1, slf_zipEntry.getName().length());
list.add(filename);
}
}
slf_zipFile.close();
return list;
}
- 获取到AndroidManifest.xml文件,调用AMPrinter解码, 得到流.
public static String readZipFile(String file, String fileName) {
try {
ZipFile zf = new ZipFile(file);
String packageValue = null;
InputStream in = new BufferedInputStream(new FileInputStream(file));
ZipInputStream zin = new ZipInputStream(in);
ZipEntry ze;
while ((ze = zin.getNextEntry()) != null) {
if (ze.isDirectory()) {
} else {
if (ze.getName().equals("AndroidManifest.xml")) {
long size = ze.getSize();
// System.out.println(file + " -- " + ze.getName()
// + " size: " + size);
if (size > 0) {
InputStream is = zf.getInputStream(ze);
AMPrinter.decodeXML(is);
// Thread.sleep(1000);
InputStream decodeIs = AMPrinter.getDecodeIs();
packageValue = ManifestParser.getMetadataKeyFromApk(decodeIs);
}
}
}
}
zin.closeEntry();
return packageValue;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
- 调用getMetaDataKey()方法解析,(这里可以获取到任何结点, 根据你的需求来), 这里只获取metadata结点和package.
/**
* 获取结点属性
*
* @param is
* 传入流
* @return
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
*/
public static String getMetadataKeyFromApk(InputStream is) {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(is);
String packageValue = findPackage(doc);
// write2File(resultFile,package1);
System.out.println("package: " + packageValue);
NodeList permissionList = doc.getElementsByTagName("meta-data");
List<String> metadatas = new ArrayList<>();
for (int i = 0; i < permissionList.getLength(); i++) {
Node permission = permissionList.item(i);
String metadataKey = (permission.getAttributes()).item(0)
.getNodeValue();
String metadataValue = (permission.getAttributes()).item(1)
.getNodeValue();
if (metadataKey.toUpperCase().contains("CHANNEL")){
metadatas.add(metadataKey + " = " + metadataValue);
//System.out.println(metadataKey + " = " + metadataValue);
}
}
if(metadatas.size() != 0)
resultMap.put(packageValue, metadatas);
return packageValue;
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}