這篇文章主要針對apk、ipa解析圖標,當然也會順帶解析其他一些基本信息,
比如:包名、版本號、版本名、應用名稱。之前google了好多文章,沒有完整的可以解決圖標的博文。今天我就爲大家帶來一篇。
首先給大家吃一顆定心丸:這篇文章絕對可以幫助大家解析出來圖標的。只要你的apk和ipa文件不是
損壞,是正常的,絕對是百發百中,彈無虛發,屢試不爽。
如果,大家不是爲了解析圖標而來,那麼就不需要看這篇文章了,
請移步到:
完整版java讀取apk、ipa包名、版本名、版本號等信息 (附源碼)
大家需要做的只是耐心看完這篇文章。
首先大家看一下效果圖:
接下來步入正題。。。。。。
1、首先看一下用到什麼工具
環境:linux
解析apk
(1). aapt工具
解析ipa
(1). python2.6
2、解析apk
對於apk的解析,我是一萬個不想用到aapt這個工具,感覺太煩人,如果能通過只需要引用幾個jar包,
用代碼讀取出來多好。
事實上,讀取一般信息,是可以的,對於,apk的名稱,是不好獲取的。我們知道安卓名稱都是
android:label=”@string/app_name” 這種方式,定義一個變量,然後就增加了我們獲取的難度,
還有另一個難點就是對於圖標的獲取。我知道可以通過以下方式獲取,前提是你要知道圖標的名稱。
//上面代碼省略。。。。。。。
zipFile = new ZipFile(apkUrl);
Enumeration<?> enumeration = zipFile.entries();
ZipEntry zipEntry = null;
while (enumeration.hasMoreElements()) {
zipEntry = (ZipEntry) enumeration.nextElement();
//我知道圖片的圖標名稱就叫appicon_logo,所以可以這樣獲取
if (zipEntry.getName().contains("appicon_logo")) {
int length = 0;
byte b[] = new byte [1024];
OutputStream outputStream = new FileOutputStream(
new File("E:\\python\\img\\apkicon.png"));
InputStream inputStream = zipFile.getInputStream(zipEntry);
while (-1 != (length = inputStream.read(b))){
outputStream.write(b, 0, length);
}
outputStream.close();
break;
}
}
//下面代碼省略。。。。。。
但是,一般我們要解析的,是我們不知道的apk,所以,我們就要獲取圖標的名稱,然而,無論我怎麼通過代碼解析,就是獲取不到,哎,不得已,只能用到aapt工具了。
##2.1. linux下安裝aapt
請移步到這篇博客
linux 64位 安裝aapt
window環境下,只需要下載aapt.exe就ok了
首先新建一個實體類
/**
* @author ZSL
* @since 2016年12月7日
* @desc [apk實體信息]
*/
public class ApkInfo{
public static final String APPLICATION_ICON_120 = "application-icon-120";
public static final String APPLICATION_ICON_160 = "application-icon-160";
public static final String APPLICATION_ICON_240 = "application-icon-240";
public static final String APPLICATION_ICON_320 = "application-icon-320";
/**
* apk內部版本號
*/
private String versionCode = null;
/**
* apk外部版本號
*/
private String versionName = null;
/**
* apk的包名
*/
private String packageName = null;
/**
* 支持的android平臺最低版本號
*/
private String minSdkVersion = null;
/**
* apk所需要的權限
*/
private List<String> usesPermissions = null;
/**
* 支持的SDK版本。
*/
private String sdkVersion;
/**
* 建議的SDK版本
*/
private String targetSdkVersion;
/**
* 應用程序名
*/
private String applicationLable;
/**
* 各個分辨率下的圖標的路徑。
*/
private Map<String, String> applicationIcons;
/**
* 程序的圖標。
*/
private String applicationIcon;
/**
* 暗指的特性。
*/
private List<ImpliedFeature> impliedFeatures;
/**
* 所需設備特性。
*/
private List<String> features;
/**
* 啓動界面
*/
private String launchableActivity;
public ApkInfo() {
this.usesPermissions = new ArrayList<String>();
this.applicationIcons = new HashMap<String, String>();
this.impliedFeatures = new ArrayList<ImpliedFeature>();
this.features = new ArrayList<String>();
}
/**
* 返回版本代碼。
*
* @return 版本代碼。
*/
public String getVersionCode() {
return versionCode;
}
/**
* @param versionCode
* the versionCode to set
*/
public void setVersionCode(String versionCode) {
this.versionCode = versionCode;
}
/**
* 返回版本名稱。
*
* @return 版本名稱。
*/
public String getVersionName() {
return versionName;
}
/**
* @param versionName
* the versionName to set
*/
public void setVersionName(String versionName) {
this.versionName = versionName;
}
/**
* 返回支持的最小sdk平臺版本。
*
* @return the minSdkVersion
*/
public String getMinSdkVersion() {
return minSdkVersion;
}
/**
* @param minSdkVersion
* the minSdkVersion to set
*/
public void setMinSdkVersion(String minSdkVersion) {
this.minSdkVersion = minSdkVersion;
}
/**
* 返回包名。
*
* @return 返回的包名。
*/
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
/**
* 返回sdk平臺版本。
*
* @return
*/
public String getSdkVersion() {
return sdkVersion;
}
public void setSdkVersion(String sdkVersion) {
this.sdkVersion = sdkVersion;
}
/**
* 返回所建議的SDK版本。
*
* @return
*/
public String getTargetSdkVersion() {
return targetSdkVersion;
}
public void setTargetSdkVersion(String targetSdkVersion) {
this.targetSdkVersion = targetSdkVersion;
}
/**
* 返回所需的用戶權限。
*
* @return
*/
public List<String> getUsesPermissions() {
return usesPermissions;
}
public void setUsesPermissions(List<String> usesPermission) {
this.usesPermissions = usesPermission;
}
public void addToUsesPermissions(String usesPermission) {
this.usesPermissions.add(usesPermission);
}
/**
* 返回程序的名稱標籤。
*
* @return
*/
public String getApplicationLable() {
return applicationLable;
}
public void setApplicationLable(String applicationLable) {
this.applicationLable = applicationLable;
}
/**
* 返回應用程序的圖標。
*
* @return
*/
public String getApplicationIcon() {
return applicationIcon;
}
public void setApplicationIcon(String applicationIcon) {
this.applicationIcon = applicationIcon;
}
/**
* 返回應用程序各個分辨率下的圖標。
*
* @return
*/
public Map<String, String> getApplicationIcons() {
return applicationIcons;
}
public void setApplicationIcons(Map<String, String> applicationIcons) {
this.applicationIcons = applicationIcons;
}
public void addToApplicationIcons(String key, String value) {
this.applicationIcons.put(key, value);
}
public void addToImpliedFeatures(ImpliedFeature impliedFeature) {
this.impliedFeatures.add(impliedFeature);
}
/**
* 返回應用程序所需的暗指的特性。
*
* @return
*/
public List<ImpliedFeature> getImpliedFeatures() {
return impliedFeatures;
}
public void setImpliedFeatures(List<ImpliedFeature> impliedFeatures) {
this.impliedFeatures = impliedFeatures;
}
/**
* 返回應用程序所需的特性。
*
* @return
*/
public List<String> getFeatures() {
return features;
}
public void setFeatures(List<String> features) {
this.features = features;
}
public void addToFeatures(String feature) {
this.features.add(feature);
}
@Override
public String toString() {
return "ApkInfo [versionCode=" + versionCode + ",\n versionName="
+ versionName + ",\n packageName=" + packageName
+ ",\n minSdkVersion=" + minSdkVersion + ",\n usesPermissions="
+ usesPermissions + ",\n sdkVersion=" + sdkVersion
+ ",\n targetSdkVersion=" + targetSdkVersion
+ ",\n applicationLable=" + applicationLable
+ ",\n applicationIcons=" + applicationIcons
+ ",\n applicationIcon=" + applicationIcon
+ ",\n impliedFeatures=" + impliedFeatures + ",\n features="
+ features + ",\n launchableActivity=" + launchableActivity + "\n]";
}
public String getLaunchableActivity() {
return launchableActivity;
}
public void setLaunchableActivity(String launchableActivity) {
this.launchableActivity = launchableActivity;
}
}
還需要一個實體
/**
* @author ZSL
* @since 2016年12月7日
* @desc [特性實體]
*/
public class ImpliedFeature {
/**
* 要的設備特性名稱。
*/
private String feature;
/**
* 表明所需特性的內容。
*/
private String implied;
public ImpliedFeature() {
super();
}
public ImpliedFeature(String feature, String implied) {
super();
this.feature = feature;
this.implied = implied;
}
public String getFeature() {
return feature;
}
public void setFeature(String feature) {
this.feature = feature;
}
public String getImplied() {
return implied;
}
public void setImplied(String implied) {
this.implied = implied;
}
@Override
public String toString() {
return "Feature [feature=" + feature + ", implied=" + implied + "]";
}
}
以上兩個實體,大家不需要特別關心。
接下來一個解析apk的工具類
public class ApkUtil {
public static final String VERSION_CODE = "versionCode";
public static final String VERSION_NAME = "versionName";
public static final String SDK_VERSION = "sdkVersion";
public static final String TARGET_SDK_VERSION = "targetSdkVersion";
public static final String USES_PERMISSION = "uses-permission";
public static final String APPLICATION_LABEL = "application-label";
public static final String APPLICATION_ICON = "application-icon";
public static final String USES_FEATURE = "uses-feature";
public static final String USES_IMPLIED_FEATURE = "uses-implied-feature";
public static final String SUPPORTS_SCREENS = "supports-screens";
public static final String SUPPORTS_ANY_DENSITY = "supports-any-density";
public static final String DENSITIES = "densities";
public static final String PACKAGE = "package";
public static final String APPLICATION = "application:";
public static final String LAUNCHABLE_ACTIVITY = "launchable-activity";
private ProcessBuilder mBuilder;
private static final String SPLIT_REGEX = "(: )|(=')|(' )|'";
private static final String FEATURE_SPLIT_REGEX = "(:')|(',')|'";
/**
* aapt所在的目錄。
*/
//windows環境下直接指向appt.exe
//比如你可以放在lib下
//private String mAaptPath = "lib/aapt";
//下面是linux下
private String mAaptPath = "/usr/local/python/img/aapt";
public ApkUtil() {
mBuilder = new ProcessBuilder();
mBuilder.redirectErrorStream(true);
}
/**
* 返回一個apk程序的信息。
*
* @param apkPath
* apk的路徑。
* @return apkInfo 一個Apk的信息。
*/
public ApkInfo getApkInfo(String apkPath) throws Exception {
//通過命令調用aapt工具解析apk文件
Process process = mBuilder.command(mAaptPath, "d", "badging", apkPath)
.start();
InputStream is = null;
is = process.getInputStream();
BufferedReader br = new BufferedReader(
new InputStreamReader(is, "utf8"));
String tmp = br.readLine();
try {
if (tmp == null || !tmp.startsWith("package")) {
throw new Exception("參數不正確,無法正常解析APK包。輸出結果爲:\n" + tmp + "...");
}
ApkInfo apkInfo = new ApkInfo();
do {
System.out.println("==============apkInfo");
System.out.println(tmp);
setApkInfoProperty(apkInfo, tmp);
} while ((tmp = br.readLine()) != null);
return apkInfo;
} catch (Exception e) {
throw e;
} finally {
process.destroy();
closeIO(is);
closeIO(br);
}
}
/**
* 設置APK的屬性信息。
*
* @param apkInfo
* @param source
*/
private void setApkInfoProperty(ApkInfo apkInfo, String source) {
if (source.startsWith(PACKAGE)) {
splitPackageInfo(apkInfo, source);
} else if(source.startsWith(LAUNCHABLE_ACTIVITY)){
apkInfo.setLaunchableActivity(getPropertyInQuote(source));
} 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(APPLICATION_LABEL)) {
//window下獲取應用名稱
apkInfo.setApplicationLable(getPropertyInQuote(source));
} else if (source.startsWith(APPLICATION_ICON)) {
apkInfo.addToApplicationIcons(getKeyBeforeColon(source),
getPropertyInQuote(source));
} else if (source.startsWith(APPLICATION)) {
String[] rs = source.split("( icon=')|'");
apkInfo.setApplicationIcon(rs[rs.length - 1]);
//linux下獲取應用名稱
apkInfo.setApplicationLable(rs[1]);
} else if (source.startsWith(USES_FEATURE)) {
apkInfo.addToFeatures(getPropertyInQuote(source));
} else if (source.startsWith(USES_IMPLIED_FEATURE)) {
apkInfo.addToImpliedFeatures(getFeature(source));
} else {
// System.out.println(source);
}
}
private ImpliedFeature getFeature(String source) {
String[] result = source.split(FEATURE_SPLIT_REGEX);
ImpliedFeature impliedFeature = new ImpliedFeature(result[1], result[2]);
return impliedFeature;
}
/**
* 返回出格式爲name: 'value'中的value內容。
*
* @param source
* @return
*/
private String getPropertyInQuote(String source) {
int index = source.indexOf("'") + 1;
return source.substring(index, source.indexOf('\'', index));
}
/**
* 返回冒號前的屬性名稱
*
* @param source
* @return
*/
private String getKeyBeforeColon(String source) {
return source.substring(0, source.indexOf(':'));
}
/**
* 分離出包名、版本等信息。
*
* @param apkInfo
* @param packageSource
*/
private void splitPackageInfo(ApkInfo apkInfo, String packageSource) {
String[] packageInfo = packageSource.split(SPLIT_REGEX);
apkInfo.setPackageName(packageInfo[2]);
apkInfo.setVersionCode(packageInfo[4]);
apkInfo.setVersionName(packageInfo[6]);
}
/**
* 釋放資源。
*
* @param c
* 將關閉的資源
*/
private final void closeIO(Closeable c) {
if (c != null) {
try {
c.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
String demo = "src/test.apk.apk";
ApkInfo apkInfo = new ApkUtil().getApkInfo(demo);
System.out.println(apkInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
public String getmAaptPath() {
return mAaptPath;
}
public void setmAaptPath(String mAaptPath) {
this.mAaptPath = mAaptPath;
}
}
下面這個是獲取apk圖標的工具類
/**
* 通過ApkInfo 裏的applicationIcon從APK裏解壓出icon圖片並存放到磁盤上
* @author zsl
*
*/
public class IconUtil {
/**
* 從指定的apk文件裏獲取指定file的流
* @param apkpath
* @param fileName
* @return
*/
public static InputStream extractFileFromApk(String apkpath, String fileName) {
try {
ZipFile zFile = new ZipFile(apkpath);
ZipEntry entry = zFile.getEntry(fileName);
entry.getComment();
entry.getCompressedSize();
entry.getCrc();
entry.isDirectory();
entry.getSize();
entry.getMethod();
InputStream stream = zFile.getInputStream(entry);
return stream;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static void extractFileFromApk(String apkpath, String fileName, String outputPath) throws Exception {
InputStream is = extractFileFromApk(apkpath, fileName);
File file = new File(outputPath);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file), 1024);
byte[] b = new byte[1024];
BufferedInputStream bis = new BufferedInputStream(is, 1024);
while(bis.read(b) != -1){
bos.write(b);
}
bos.flush();
is.close();
bis.close();
bos.close();
}
/**
* demo 獲取apk文件的icon並寫入磁盤指定位置
* @param args
*/
public static void main(String[] args) {
try {
String apkpath = "D:\\DefaultApkTempSaveFolder\\3G安卓市場\\com.jiubang.market.apk";
if (args.length > 0) {
apkpath = args[0];
}
ApkInfo apkInfo = new ApkUtil().getApkInfo(apkpath);
System.out.println(apkInfo);
extractFileFromApk(apkpath, apkInfo.getApplicationIcon(), "D:\\DefaultApkTempSaveFolder\\3G安卓市場\\crawler\\icon.png");
} catch (Exception e) {
e.printStackTrace();
}
}
apk就解析結束!
3、解析ipa
##3.1. 解析圖片
解析ipa基本屬性需要用到dd-plist-1.16.jar
下載地址:dd-plist-1.16.jar
對於獲取圖標,大家可以先看我另一篇博客:
完整版java讀取apk、ipa包名、版本名、版本號等信息
plist結構如下:
我們可以通過如下方式獲取
然後根據名稱去下載
最終可以獲取到圖片並下載到本地
很多人都會遇到這個問題:有的圖標是正常的,而有的圖標卻是黑色的,打不開。
根據網上搜索的資料,大家或者找到了辦法,就是通過ipin.py (python腳本)來反序列化恢復正常的圖片。
我這邊也下載了那個文件試了下,大家看效果:
大家一定注意到了吧,那張圖片本來是黑色的,執行了一次之後,就恢復正常了,
然而再次執行之後,就變得更糟了,變成一張損壞的圖片,完全打不開。。。。。
我們可以先生成一張黑色的圖片,然後執行ipin.py恢復成好的圖片,然後把這張圖片移動另一個目錄。
哎呦 ,這貌似是一個不錯的想法,但是,如果,你解析的ipa圖片,本身就是一張好的圖片,這種情況不是不可能的,不是所有的圖標本身就是黑色的。比如:愛奇藝的ipa,圖片本身就是正常的,這時候你去ipin.py一下,那麼這就尷尬了。
解決辦法:
根據之前的ipin.py文件,修改了一下,現在的功能是遇到好的圖片就跳過,不去反序列它,遇到黑色圖片纔去做操作。
請看效果:
沒錯,程序會報錯,直接跳過這張圖片。繼續執行。
##3.1. python 反序列圖片
安裝大家可以到這裏:
Python環境搭建
對於linux環境,一般python都是安裝好的,大家可以直接輸入python命令
如果版本太高下面執行會有問題,我的python版本是2.7的
下面最重要的來了,就是那個改進過的ipin.py文件。跳過正常圖片。
#---
# iPIN - iPhone PNG Images Normalizer v1.0
# Copyright (C) 2007
#
# Author:
# Axel E. Brzostowski
# http://www.axelbrz.com.ar/
# [email protected]
#
# References:
# http://iphone.fiveforty.net/wiki/index.php/PNG_Images
# http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#---
from struct import *
from zlib import *
import stat
import sys
import os
import shutil
import glob
def getNormalizedPNG(filename):
pngheader = "\x89PNG\r\n\x1a\n"
file = open(filename, "rb")
oldPNG = file.read()
file.close()
if oldPNG[:8] != pngheader:
return None
newPNG = oldPNG[:8]
chunkPos = len(newPNG)
idatAcc = ""
breakLoop = False
# For each chunk in the PNG file
while chunkPos < len(oldPNG):
skip = False
# Reading chunk
chunkLength = oldPNG[chunkPos:chunkPos+4]
chunkLength = unpack(">L", chunkLength)[0]
chunkType = oldPNG[chunkPos+4 : chunkPos+8]
chunkData = oldPNG[chunkPos+8:chunkPos+8+chunkLength]
chunkCRC = oldPNG[chunkPos+chunkLength+8:chunkPos+chunkLength+12]
chunkCRC = unpack(">L", chunkCRC)[0]
chunkPos += chunkLength + 12
# Parsing the header chunk
if chunkType == "IHDR":
width = unpack(">L", chunkData[0:4])[0]
height = unpack(">L", chunkData[4:8])[0]
# Parsing the image chunk
if chunkType == "IDAT":
# Store the chunk data for later decompression
idatAcc += chunkData
skip = True
# Removing CgBI chunk
if chunkType == "CgBI":
skip = True
# Add all accumulated IDATA chunks
if chunkType == "IEND":
try:
# Uncompressing the image chunk
bufSize = width * height * 4 + height
chunkData = decompress( idatAcc, -15, bufSize)
except Exception, e:
# The PNG image is normalized
print e
return None
chunkType = "IDAT"
# Swapping red & blue bytes for each pixel
newdata = ""
for y in xrange(height):
i = len(newdata)
newdata += chunkData[i]
for x in xrange(width):
i = len(newdata)
newdata += chunkData[i+2]
newdata += chunkData[i+1]
newdata += chunkData[i+0]
newdata += chunkData[i+3]
# Compressing the image chunk
chunkData = newdata
chunkData = compress( chunkData )
chunkLength = len( chunkData )
chunkCRC = crc32(chunkType)
chunkCRC = crc32(chunkData, chunkCRC)
chunkCRC = (chunkCRC + 0x100000000) % 0x100000000
breakLoop = True
if not skip:
newPNG += pack(">L", chunkLength)
newPNG += chunkType
if chunkLength > 0:
newPNG += chunkData
newPNG += pack(">L", chunkCRC)
if breakLoop:
break
return newPNG
def updatePNG(filename):
data = getNormalizedPNG(filename)
if data != None:
file = open(filename, "wb")
file.write(data)
file.close()
return True
return data
def getFiles(base):
global _dirs
global _pngs
if base == ".":
_dirs = []
_pngs = []
if base in _dirs:
return
files = os.listdir(base)
for file in files:
filepath = os.path.join(base, file)
try:
st = os.lstat(filepath)
except os.error:
continue
if stat.S_ISDIR(st.st_mode):
if not filepath in _dirs:
getFiles(filepath)
_dirs.append( filepath )
elif file[-4:].lower() == ".png":
if not filepath in _pngs:
_pngs.append( filepath )
if base == ".":
return _dirs, _pngs
print "-----------------------------------"
print " iPhone PNG Images Normalizer v1.0"
print "-----------------------------------"
print " "
print "[+] Searching PNG files...",
dirs, pngs = getFiles(".")
print "ok"
if len(pngs) == 0:
print " "
print "[!] Alert: There are no PNG files found. Move this python file to the folder that contains the PNG files to normalize."
exit()
print " "
print " - %d PNG files were found at this folder (and subfolders)." % len(pngs)
print " "
while True:
normalize = "y"
if len(normalize) > 0 and (normalize[0] == "y" or normalize[0] == "n"):
break
normalized = 0
if normalize[0] == "y":
for ipng in xrange(len(pngs)):
perc = (float(ipng) / len(pngs)) * 100.0
print "%.2f%% %s" % (perc, pngs[ipng])
if updatePNG(pngs[ipng]):
normalized += 1
print " "
print "[+] %d PNG files were normalized." % normalized
for filename in glob.glob(r'/E:/python/img2/*.png'):
shutil.move(filename,"/E:/python/img")
print filename
大家需要注意的地方有一點,最後三行代碼是我加上的,意思是把發序列的正常圖片,
全部移到另一個文件夾裏,這樣做的好處是什麼呢?
1、如果我們上傳解析很多ipa文件,
那麼理所當然的也就生成很多圖標,如果,都放在反序列化的文件夾,而不移走的話,
那麼, 程序,每次都會去查找有哪些圖片需要反序列,這都是耗時的。
2、那些之前是黑色的,被反序列成正常圖片之後是沒問題的,如果再次反序列還是
會照樣變成損壞的圖片。上面改進過的ipin.py文件雖然改進過了,但是它只是針對
源文件是正常圖片,而不是本身是黑色圖片,這時你轉成正常圖片,再轉一次,還是
會有問題。
其實兩者是不矛盾的。
下面需要做的就是寫個shell腳本,然後用程序去調用了。
shell腳本
#!/bin/sh
cd /usr/local/python/img/
python ipin.py
下面是後臺代碼:
/**
* @author ZSL
* @param ipaURL
* @return
* @desc [解析ipa文件]
*/
public static Map<String,Object> getIpaInfo(String ipaURL , HttpServletRequest request){
Map<String,Object> map = new HashMap<String,Object>();
String newIconName = "";
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("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);
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:" + name);
if(name.contains(icon.trim())){
newIconName = createDateSuffix()+ ".png";
FileOutputStream fos = new FileOutputStream(new File(pythonTempImagePath + File.separator + newIconName ));
int chunk = 0;
byte[] data = new byte[1024];
while(-1!=(chunk=zipIns2.read(data))){
fos.write(data, 0, chunk);
}
fos.close();
System.out.println("=================下載圖片成功");
break;
}
}
}
////////////////////////////////////////////////////////////////
//如果想要查看有哪些key ,可以把下面註釋放開
// for (String string : dictionary.allKeys()) {
// System.out.println(string + ":" + dictionary.get(string).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());
//應用名稱
parameters = (NSString) rootDict.get("CFBundleDisplayName");
if(null == parameters){
parameters = (NSString) rootDict.get("CFBundleName");
}
map.put("appName", parameters.toString());
// parameters = (NSString) rootDict.get("CFBundlePrimaryIcon");
// map.put("icon", parameters.toString());
/////////////////////////////////////////////////
infoIs.close();
is.close();
zipIns.close();
} catch (Exception e) {
e.printStackTrace();
map.put("code", "fail");
map.put("error","讀取ipa文件失敗");
}
// finally {
// if (null != infoIs) {
// infoIs.close();
// }
// if (null != is) {
// is.close();
// }
// if (null != zipIns) {
// zipIns.close();
// }
// }
System.out.println("================================執行命令獲取icon開始=========================");
Process process;
try {
process = Runtime.getRuntime().exec("sh /usr/local/python/img/ipin.sh");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
process.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("================================執行命令獲取icon結束=========================");
System.out.println("================================圖標名稱:"+newIconName+"=========================");
map.put("icon", newIconName);
map.put("code", "success");
return map;
}
大功告成! ipa文件解析成功 !
再來看一下效果:上傳了兩個比較大的,100M多
我這裏是在linux下操作的,不過windows下也是可以的,只需要把shell腳本改成bat文件,然後
調用系統命令執行bat文件。
下面提供一份源碼
Java解析apk、ipa圖標,包名,應用名稱,版本號源碼
希望對大家有所幫助!