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