1. 程式人生 > >完整版java讀取apk、ipa包名、版本名、版本號等資訊

完整版java讀取apk、ipa包名、版本名、版本號等資訊

有時候,我們上傳apk或者是ipa檔案的時候,是需要讀取到裡面的一些資訊的,比如軟體的包名,以及其版本資訊等。在網上搜索了一下資料 , 找了很多版本,對於apk檔案的版本號,一直讀取不到,在這裡,筆者自己總結了,讀取apk、ipa檔案的一些程式碼,大家可以參考下,去其糟粕,取其精華。以便適用於自己的需求。

如果大家希望解析到圖示的話,可以看我後面寫的博文
博文地址請戳 :

下面會提供原始碼給大家,我用的開發工具是eclipse,直接匯入就可以,jar包也是我已經下載好的,大家可以免積分拿去。本來裡面是有2個apk、2個ipa檔案提供測試的,但是由於檔案太大,上傳不了原始碼,所以就刪除了一個最大的ipa包

首先看一下我的專案目錄
這裡寫圖片描述

下面請看程式碼

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)); } } }

這裡寫圖片描述

總結:對於ipa檔案的解析,我們可以根據info.plist檔案結構進行解析,獲取我們需要的值,如果我們key對應的是dict ,那麼我們就用NSDictionary來獲取,如果是key下面是array,那麼我們就用NSArray來獲取,這時獲取到的是一個數組。
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述


我們可以看到上面解析到的圖片是黑色的,這個確實是這樣子的,在window或者linux下都是黑色的,只有在mac下才是正常的,應該是ios開發者壓縮的結果。不過不要擔心,可以看後面的文章,怎麼把圖片反編譯成正常圖片。


補充:

1. 2016-9-14補充獲取ipa圖示,並下載到指定位置

如果大家希望解析到圖示的話,可以看我後面寫的博文
博文地址請戳 :