Java环境解析apk文件信息
概述:Java解析apk文件,获取apk文件里的包名,版本号,图标文件等;
功能:可以提供给windows和linux平台使用;
原理:利用aapt.exe或者aapt这些anroid平台解析apk文件的工具,借用终端shell调用命令解析输出信息;
代码:
这里贴出一些关键代码,并给出代码注释,如下
1 package com.apkutils;
2
3 import java.io.BufferedReader;
4 import java.io.Closeable;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.InputStreamReader;
10 import java.util.HashMap;
11 import java.util.Map;
12 import java.util.Properties; 16
17
18 /**
19 * apk工具类。封装了获取Apk信息的方法。
20 *
21 * @author @author tony
22 *
23 * <p>
24 * <b>version description</b><br />
25 * V0.2.1 修改程序名字为从路径中获得。
26 * </p>
27 */
28 public class ApkUtil {
29 public static final String VERSION_CODE = "versionCode";
30 public static final String VERSION_NAME = "versionName";
31 public static final String SDK_VERSION = "sdkVersion";
32 public static final String TARGET_SDK_VERSION = "targetSdkVersion";
33 public static final String USES_PERMISSION = "uses-permission";
34 public static final String APPLICATION_LABEL = "application-label";
35 public static final String APPLICATION_ICON = "application-icon";
36 public static final String USES_FEATURE = "uses-feature";
37 public static final String USES_IMPLIED_FEATURE = "uses-implied-feature";
38 public static final String SUPPORTS_SCREENS = "supports-screens";
39 public static final String SUPPORTS_ANY_DENSITY = "supports-any-density";
40 public static final String DENSITIES = "densities";
41 public static final String PACKAGE = "package";
42 public static final String APPLICATION = "application:";
43 public static final String LAUNCHABLE_ACTIVITY = "launchable-activity";
44
45 // api ---- os
46 static Map<String, String> OSVersion = new HashMap<String, String>();
47
48 static {
49 OSVersion.put("3", "1.5");
50 OSVersion.put("4", "1.6");
51 OSVersion.put("5", "2.0");
52 OSVersion.put("6", "2.0.1");
53 OSVersion.put("7", "2.1");
54 OSVersion.put("8", "2.2");
55 OSVersion.put("9", "2.3");
56 OSVersion.put("10", "2.3.3");
57 OSVersion.put("11", "3.0");
58 OSVersion.put("12", "3.1");
59 OSVersion.put("13", "3.2");
60 OSVersion.put("14", "4.0");
61 OSVersion.put("15", "4.0.3");
62 OSVersion.put("16", "4.1.1");
63 OSVersion.put("17", "4.2");
64 OSVersion.put("18", "4.3");
65 OSVersion.put("19", "4.4");
66 }
67
68 private static final String SPLIT_REGEX = "(: )|(=')|(' )|'";
69 private static final String FEATURE_SPLIT_REGEX = "(:')|(',')|'";
70 /**
71 * aapt所在的目录。
72 */
73 private String mAaptPath = "D:\\App\\";//winOS
74 //private String mAaptPath = ApkUtil.class.getClassLoader().getResource("").getPath();//linux
75
76 static String[] shellCommand;
77 static String softName = "";
78 static {
79 shellCommand = new String[2];
80 final String anOSName = System.getProperty("os.name");
81 if (anOSName.toLowerCase().startsWith("windows")) {
82 // Windows XP, Vista ...
83 shellCommand[0] = "cmd";
84 shellCommand[1] = "/C";
85 softName = "aapt.exe";
86 } else {
87 // Unix, Linux ...
88 shellCommand[0] = "/bin/sh";
89 shellCommand[1] = "-c";
90 softName = "aapt";
91 }
92 }
93
94 /***
95 * apkPath
96 */
97 static String apkPath = "C:/Users/win7/Desktop/Android/baiduyinyue_49.apk";
98
99 /**
100 * 返回一个apk程序的信息。
101 *
102 * @param apkPath
103 * apk的路径。
104 * @return apkInfo 一个Apk的信息。
105 */
106 public ApkInfo getApkInfo(String apkPath) throws Exception {
107 String command = mAaptPath + softName + " d badging \"" + apkPath
108 + "\"";
109 Process process;
110 try {
111 process = Runtime.getRuntime().exec(
112 new String[] {shellCommand[0], shellCommand[1], command});
113 } catch (IOException e) {
114 process = null;
115 throw e;
116 }
117
118 ApkInfo apkInfo = null;
119 if(process != null){
120 InputStream is = process.getInputStream();
121 BufferedReader br = new BufferedReader(
122 new InputStreamReader(is, "utf8"));
123 String tmp = br.readLine();
124 try {
125 if (tmp == null || !tmp.startsWith("package")) {
126 throw new Exception("参数不正确,无法正常解析APK包。输出结果为:\n" + tmp + "...");
127 }
128 apkInfo = new ApkInfo();
129 do {
130 setApkInfoProperty(apkInfo, tmp);
131 } while ((tmp = br.readLine()) != null);
132 } catch (Exception e) {
133 throw e;
134 } finally {
135 process.destroy();
136 closeIO(is);
137 closeIO(br);
138 }
139 }
140 return apkInfo;
141 }
142
143 /**
144 * 设置APK的属性信息。
145 *
146 * @param apkInfo
147 * @param source
148 */
149 private void setApkInfoProperty(ApkInfo apkInfo, String source) {
150 if (source.startsWith(PACKAGE)) {
151 splitPackageInfo(apkInfo, source);
152 } else if (source.startsWith(LAUNCHABLE_ACTIVITY)) {
153 apkInfo.setLaunchableActivity(getPropertyInQuote(source));
154 } else if (source.startsWith(SDK_VERSION)) {
155 apkInfo.setSdkVersion(getPropertyInQuote(source));
156 apkInfo.setMinOSVersion(OSVersion.get(getPropertyInQuote(source)));
157 } else if (source.startsWith(TARGET_SDK_VERSION)) {
158 apkInfo.setTargetSdkVersion(getPropertyInQuote(source));
159 } else if (source.startsWith(USES_PERMISSION)) {
160 apkInfo.addToUsesPermissions(getPropertyInQuote(source));
161 } else if (source.startsWith(APPLICATION_LABEL)) {
162 apkInfo.setApplicationLable(getPropertyInQuote(source));
163 } else if (source.startsWith(APPLICATION_ICON)) {
164 apkInfo.addToApplicationIcons(getKeyBeforeColon(source),
165 getPropertyInQuote(source));
166 } else if (source.startsWith(APPLICATION)) {
167 String[] rs = source.split("( icon=')|'");
168 apkInfo.setApplicationIcon(rs[rs.length - 1]);
169 } else if (source.startsWith(USES_FEATURE)) {
170 apkInfo.addToFeatures(getPropertyInQuote(source));
171 } else if (source.startsWith(USES_IMPLIED_FEATURE)) {
172 apkInfo.addToImpliedFeatures(getFeature(source));
173 } else {
174
175 }
176 try {
177 apkInfo.setApkFileSize(getFileSizes(new File(apkPath)));
178 } catch (Exception e) {
179 e.printStackTrace();
180 }
181 }
182
183 private ImpliedFeature getFeature(String source) {
184 String[] result = source.split(FEATURE_SPLIT_REGEX);
185 ImpliedFeature impliedFeature = new ImpliedFeature(result[1], result[2]);
186 return impliedFeature;
187 }
188
189 /**
190 * 返回出格式为name: 'value'中的value内容。
191 *
192 * @param source
193 * @return
194 */
195 private String getPropertyInQuote(String source) {
196 int index = source.indexOf("'") + 1;
197 return source.substring(index, source.indexOf('\'', index));
198 }
199
200 /**
201 * 返回冒号前的属性名称
202 *
203 * @param source
204 * @return
205 */
206 private String getKeyBeforeColon(String source) {
207 return source.substring(0, source.indexOf(':'));
208 }
209
210 /**
211 * 分离出包名、版本等信息。
212 *
213 * @param apkInfo
214 * @param packageSource
215 */
216 private void splitPackageInfo(ApkInfo apkInfo, String packageSource) {
217 String[] packageInfo = packageSource.split(SPLIT_REGEX);
218 apkInfo.setPackageName(packageInfo[2]);
219 apkInfo.setVersionCode(packageInfo[4]);
220 apkInfo.setVersionName(packageInfo[6]);
221 }
222
223 /**
224 * 释放资源。
225 *
226 * @param c
227 * 将关闭的资源
228 */
229 private final void closeIO(Closeable c) {
230 if (c != null) {
231 try {
232 c.close();
233 } catch (IOException e) {
234 e.printStackTrace();
235 }
236 }
237 }
238
239 public static void main(String[] args) {
240 try {
241 ApkInfo apkInfo = new ApkUtil().getApkInfo(apkPath);
242 System.out.println(apkInfo);
243 IconUtil.extractFileFromApk(apkPath, apkInfo.getApplicationIcon(),
244 "D:\\icon.png");
245 } catch (Exception e) {
246 e.printStackTrace();
247 }
248 }
249
250 public String getmAaptPath() {
251 return mAaptPath;
252 }
253
254 public void setmAaptPath(String mAaptPath) {
255 this.mAaptPath = mAaptPath;
256 }
257
258 // 取得文件大小
259 public static long getFileSizes(File f) throws Exception {
260 long s = 0;
261 if (f.exists()) {
262 FileInputStream fis = null;
263 fis = new FileInputStream(f);
264 s = fis.available();
265 } else {
266 System.out.println("文件不存在");
267 }
268 return s;
269 }
270 }
上面加上阴影的部分代码,我想基本都是很好理解的,获取当前运行的操作系统类型,适配上相应的指令和软件,这样可以跨平台操作,而不需要修改代码;
获取到相应软件后,对apk文件一行一行的进行解析,将相应的属性存到javaBean中,并将apk文件图片通过流的方式写出到文件系统中,
package com.apkutils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* 通过ApkInfo 里的applicationIcon从APK里解压出icon图片并存放到磁盘上
* @author @author tony
*/
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();
}
}
这里没什么好说的,就是用JavaIO中zip的方式解析文件,并拿出图标写出到指定文件目录
参考文献 :
纯java从apk文件里获取包名、版本号、icon