概述: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
Java解析apk
时间: 2024-10-28 23:44:31