首页 > 代码库 > Java解析apk

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 apkPath103      *            apk的路径。104      * @return apkInfo 一个Apk的信息。105      */106     public ApkInfo getApkInfo(String apkPath) throws Exception {107         String command = mAaptPath + softName + " d badging \"" + apkPath108                 + "\"";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 apkInfo147      * @param source148      */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 source193      * @return194      */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 source204      * @return205      */206     private String getKeyBeforeColon(String source) {207         return source.substring(0, source.indexOf(‘:‘));208     }209 210     /**211      * 分离出包名、版本等信息。212      * 213      * @param apkInfo214      * @param packageSource215      */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 c227      *            将关闭的资源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