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

 

posted @ 2014-07-31 17:18  ^_TONY_^  阅读(12956)  评论(3编辑  收藏  举报