Android PackageManager 用法

参考资料:http://blog.csdn.net/qinjuning/article/details/6867806,有改动。

PackageManger的主要职责是管理应用程序包,通过它可以获取应用程序信息。


AnroidManifest.xml文件节点说明:



一 、相关类介绍

1. PackageItemInfo类

AndroidManifest.xml文件中所有节点的基类,并不直接使用,而是由子类继承然后调用相应方法。

常用字段:

int icon             资源图片在R文件中的值 (对应于android:icon属性)
int labelRes         label在R文件中的值(对应于android:label属性)
String name          节点的name值 (对应于android:name属性)
String packagename   应用程序的包名 (对应于android:packagename属性)

常用方法:
Drawable loadIcon(PackageManager pm)          获得当前应用程序的图标
CharSequence loadLabel(PackageManager pm)     获得当前应用程序的label,从上图可知是app_name

PackageItemInfo继承关系图:


2. ActivityInfo类

<activity>或者 <receiver>节点信息 。可获取theme 、launchMode、launchmode等属性

常用方法继承自PackageItemInfo类,下同。

3. ServiceInfo类

<service>节点信息。


4. ApplicationInfo类

<application>节点的信息。

字段说明:

flags字段:
FLAG_SYSTEM 系统应用程序
FLAG_EXTERNAL_STORAGE 表示该应用安装在sdcard中

5. ResolveInfo类

根据<intent-filter>节点获取其上一层目录的信息,通常是<activity>、<receiver>、<service>节点信息。
常用字段:
ActivityInfo activityInfo    获取 ActivityInfo对象,即<activity>或<receiver>节点信息
ServiceInfo serviceInfo     获取 ServiceInfo对象,即<service>节点信息

ApplicationInfo与ResolveInfo比较:前者能够得到Icon、Label、meta-data、description。后者只能得到Icon、Label。


6. PackageInfo类

AndroidManifest.xml文件的信息

常用字段:

String packageName               包名
ActivityInfo[] activities        所有<activity>节点信息
ApplicationInfo applicationInfo  <application>节点信息,只有一个
ActivityInfo[] receivers         所有<receiver>节点信息,多个
ServiceInfo[] services           所有<service>节点信息 ,多个


7. PackageManger 类

通过getPackageManager()方法获得。
常用方法:
PackageManager getPackageManager();
// 获得一个PackageManger对象

Drawable getApplicationIcon(String packageName);
// 返回给定包名的图标,否则返回null

ApplicationInfo getApplicationInfo(String packageName, int flags);
// 返回该ApplicationInfo对象
// flags标记通常直接赋予常数0

List<ApplicationInfo> getInstalledApplications(int flags);
// 返回给定条件的所有ApplicationInfo
// flag为一般为GET_UNINSTALLED_PACKAGES,后续可进一步过滤结果

List<PackageInfo> getInstalledPackages(int flags);
// 返回给定条件的所有PackageInfo

ResolveInfo resolveActivity(Intent intent, int flags);
// 返回给定条件的ResolveInfo对象(本质上是Activity) 
// intent 是查询条件,Activity所配置的action和category
// 可选flags:
// MATCH_DEFAULT_ONLY :Category必须带有CATEGORY_DEFAULT的Activity,才匹配
// GET_INTENT_FILTERS :匹配Intent条件即可
// GET_RESOLVED_FILTER :匹配Intent条件即可

List<ResolveInfo> queryIntentActivities(Intent intent, int flags);
// 返回给定条件的所有ResolveInfo对象(本质上是Activity)

ResolveInfo resolveService(Intent intent, int flags);
// 返回给定条件的ResolveInfo对象(本质上是Service) 

List<ResolveInfo> queryIntentServices(Intent intent, int flags);
// 返回给定条件的所有ResolveInfo对象(本质上是Service),集合对象



8. PackageStats 类

安装包的大小信息。AndroidSDK中并没有显式提供方法获得PackageStats对象,只能通过反射机制来调用系统中隐藏的函数(@hide)。

常用字段:

long cachesize      缓存大小
long codesize       应用程序大小
long datasize       数据大小
String packageName  包名


PS:应用程序的总大小 = cachesize  + codesize  + datasize。


二、常用代码片

1.根据PackageInfo对象获取APP信息:

ApplicationInfo applicationInfo = packageInfo.applicationInfo;
// APP 包名
String packageName = packageInfo.packageName;
// APP icon
Drawable icon = packageManager.getApplicationIcon(applicationInfo);
// APP 名称
String appName = packageManager.getApplicationLabel(applicationInfo).toString();
// APP 权限
String[] permissions = packageManager.getPackageInfo(packageName,PackageManager.GET_PERMISSIONS).requestedPermissions;

2.根据ResolveInfo对象获取APP信息:

// APP包名
resolve.activityInfo.packageName;
// APP icon
resolve.loadIcon(packageManager);
// APP名称
resolve.loadLabel(packageManager).toString();

3.根据包名获取APP中的主Activity:

Intent intent = new Intent(Intent.ACTION_MAIN);    
intent.setPackage(packageName);  
intent.addCategory(Intent.CATEGORY_LAUNCHER); 
List<ResolveInfo> resolveInfos = pManager.queryIntentActivities(intent, 0);
// 一个App中只有一个主Activity,直接取出。注意不是任何包中都有主Activity
String mainActivityName = "";
if (resolveInfos != null && resolveInfos.size() >= 1) {
    mainActivityName = resolveInfos.get(0).activityInfo.name;
}

4.根据包名获取APP信息:

PackageManager pManager = context.getPackageManager();
PackageInfo packageInfo = pManager.getPackageInfo(packageName, 0);
ApplicationInfo appInfo = packageInfo.applicationInfo;

// 获取App名
String appName = pManager.getApplicationLabel(appInfo).toString();
//// 也可以使用如下方法
//String appName = appInfo.loadLabel(pManager).toString();
// 获取App Icon
Drawable icon = pManager.getApplicationIcon(appInfo);
//// 也可以用如下两种方法
//Drawable icon = pManager.getApplicationIcon(packageName);
//Drawable icon = appInfo.loadIcon(pManager);
// 获取App versionName
String versionName = packageInfo.versionName; // versionName在xml的根节点中,只能用PackageInfo获取
// 获取权限
PackageInfo pPermissionInfo = pManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); 
String[] permissions = pPermissionInfo.requestedPermissions;

5.批量获取App信息的两种方法:

PackageManager packageManager = getPackageManager();

// 法一:通过解析AndroidManifest.xml的<application>标签中得到,可获取所有的app。
List<ApplicationInfo> applicationList = packageManager
                  .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);

// 法二:通过Intent查找相关的Activity,更准确,但无法获取Provider等应用
// 通过解析<Intent-filter>标签得到
// <action android:name=”android.intent.action.MAIN”/>
// <action android:name=”android.intent.category.LAUNCHER”/>
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveList = packageManager.queryIntentActivities(intent, 0);

6.区分系统APP、第三方APP、安装在SDCard上的APP:

/** 判断是不是系统APP **/
// FLAG_SYSTEM = 1<<0,if set, this application is installed in the device's system image.
// 下面&运算有两种结果:
// 1,则flags的末位为1,即系统APP
// 0,则flags的末位为0,即非系统APP
if ( (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 1 ){
    ......
}

/** 判断是不是第三方APP **/
// FLAG_SYSTEM = 1<<0,同上
if ( (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
     ......
}
//本来是系统程序,被用户手动更新后,该系统程序也成为第三方应用程序了
// FLAG_UPDATED_SYSTEM_APP = 1<<7, this is set if this application has been 
// install as an update to a built-in system application.
else if ((applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 1) {
     ......
}

/** 判断是不是安装在SDCard的应用程序 **/
// FLAG_EXTERNAL_STORAGE = 1<<18,Set to true if the application is
// currently installed on external/removable/unprotected storage
if ( (applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 1) {
    ......
} 

三、工具类

获取APP信息:
/**
 * 获取手机上安装的所有APP的信息 配合AppInfo类使用
 */
public class AppInfoUtil {
    public static final int GET_ALL_APP = 0; // 所有APP
    public static final int GET_SYSTEM_APP = 1; // 系统预装APP
    public static final int GET_THIRD_APP = 2; // 第三方APP
    public static final int GET_SDCARD_APP = 3; // SDCard的APP

    private static AppInfoUtil infoUtil;

    private PackageManager pManager;

    // 所有应用
    private List<PackageInfo> allPackageList;

    // 筛选结果
    private List<PackageInfo> result;

    /** 私有构造器 **/
    private AppInfoUtil(Context context) {
        pManager = context.getPackageManager();
        result = new ArrayList<PackageInfo>();
    }

    /** 单例 **/
    public static AppInfoUtil getInstance(Context context) {
        if (infoUtil == null) {
            infoUtil = new AppInfoUtil(context);
        }
        return infoUtil;
    }

    /** 获取已安装的APP **/
    public List<AppInfo> getInstalledApps(int type) {
        // 0 表示不接受任何参数。其他参数都带有限制
        // 版本号、APP权限只能通过PackageInfo获取,故这里不使用getInstalledApplications()方法
        allPackageList = pManager.getInstalledPackages(0);
        if (allPackageList == null) {
            Log.e("AppInfoUtil类", "getInstalledApps()方法中的allPackageList为空");
            return null;
        }
        // 根据APP名排序
        Collections.sort(allPackageList, new PackageInfoComparator(pManager));
        // 筛选
        result.clear();
        switch (type) {
        case GET_ALL_APP:
            result = allPackageList;
            break;
        case GET_SYSTEM_APP: // 系统自带APP
            for (PackageInfo info : allPackageList) {
                // FLAG_SYSTEM = 1<<0,if set, this application is installed in
                // the device's system image.
                // 下面&运算有两种结果:
                // 1,则flags的末位为1,即系统APP
                // 0,则flags的末位为0,即非系统APP
                if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 1) {
                    result.add(info);
                }
            }
            break;
        case GET_THIRD_APP: // 第三方APP
            for (PackageInfo info : allPackageList) {
                // FLAG_SYSTEM = 1<<0,同上
                if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
                    result.add(info);
                }
                // 本来是系统程序,被用户手动更新后,该系统程序也成为第三方应用程序了
                // FLAG_UPDATED_SYSTEM_APP = 1<<7, this is set if this
                // application has been
                // install as an update to a built-in system application.
                else if ((info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 1) {
                    result.add(info);
                }
            }
            break;
        case GET_SDCARD_APP: // 安装在SDCard的应用程序
            for (PackageInfo info : allPackageList) {
                // FLAG_EXTERNAL_STORAGE = 1<<18,Set to true if the application
                // is
                // currently installed on external/removable/unprotected storage
                if ((info.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 1) {
                    result.add(info);
                }
            }
            break;
        }
        return getAppInfoByPackageInfo(result);
    }

    public List<AppInfo> getAppInfoByIntent(Intent intent) {
        List<ResolveInfo> resolveInfos = pManager.queryIntentActivities(intent,
                PackageManager.GET_INTENT_FILTERS);
        // 调用系统排序 , 根据name排序
        // 此排序会将系统自带App与用户安装的APP分开排序
        Collections.sort(resolveInfos, new ResolveInfo.DisplayNameComparator(
                pManager));
        // // 此排序会将系统自带App与用户安装的APP混合排序
        // Collections.sort(resolveInfos, new DisplayNameComparator(pManager));
        return getAppInfobyResolveInfo(resolveInfos);
    }

    /** 获取单个App图标 **/
    public Drawable getAppIcon(String packageName) throws NameNotFoundException {
        Drawable icon = pManager.getApplicationIcon(packageName);
        return icon;
    }

    /** 获取单个App名称 **/
    public String getAppName(String packageName) throws NameNotFoundException {
        ApplicationInfo appInfo = pManager.getApplicationInfo(packageName, 0);
        String appName = pManager.getApplicationLabel(appInfo).toString();
        return appName;
    }

    /** 获取单个App版本号 **/
    public String getAppVersion(String packageName)
            throws NameNotFoundException {
        PackageInfo packageInfo = pManager.getPackageInfo(packageName, 0);
        String appVersion = packageInfo.versionName;
        return appVersion;
    }

    /** 获取单个App的所有权限 **/
    public String[] getAppPermission(String packageName)
            throws NameNotFoundException {
        PackageInfo packageInfo = pManager.getPackageInfo(packageName,
                PackageManager.GET_PERMISSIONS);
        String[] permission = packageInfo.requestedPermissions;
        return permission;
    }

    /** 获取单个App的签名 **/
    public String getAppSignature(String packageName)
            throws NameNotFoundException {
        PackageInfo packageInfo = pManager.getPackageInfo(packageName,
                PackageManager.GET_SIGNATURES);
        String allSignature = packageInfo.signatures[0].toCharsString();
        return allSignature;
    }

    // /** 使用示例 **/
    // public static void main(String[] args) {
    // AppInfoUtil appInfoUtil = AppInfo.getInstance(context);
    //
    // // 获取所有APP
    // List<AppInfo> allAppInfo = appInfoUtil.getInstalledApps(AppInfoUtil.GET_ALL_APP);
    // for (AppInfo app : allAppInfo) {
    // String packageName = app.getPackageName();
    // String appName = app.getAppName();
    // Drawable icon = app.getIcon();
    // String versionName = app.getVersionName();
    // String[] permissions = app.getPermissions();
    // // 自由发挥...
    // }
    //
    // // 获取单个APP的信息
    // String appName = appInfoUtil.getAppName(packageName);
    // ...
    // }

    /** 从PackageInfo的List中提取App信息 **/
    private List<AppInfo> getAppInfoByPackageInfo(List<PackageInfo> list) {
        List<AppInfo> appList = new ArrayList<AppInfo>();
        for (PackageInfo info : list) {
            // 获取信息
            String packageName = info.applicationInfo.packageName;
            String appName = pManager.getApplicationLabel(info.applicationInfo)
                    .toString();
            Drawable icon = pManager.getApplicationIcon(info.applicationInfo);
            // // 也可以用如下方法获取APP图标,显然更烦琐
            // ApplicationInfo applicationInfo =
            // pManager.getApplicationInfo(packageName, 0);
            // Drawable icon = applicationInfo.loadIcon(pManager);
            String versionName = info.versionName;
            String[] permissions = info.requestedPermissions;
            String launchActivityName = getLaunchActivityName(packageName);
            // 储存信息
            AppInfo appInfo = new AppInfo();
            appInfo.setPackageName(packageName);
            appInfo.setAppName(appName);
            appInfo.setIcon(icon);
            appInfo.setVersionName(versionName);
            appInfo.setPermissions(permissions);
            appInfo.setLaunchActivityName(launchActivityName);
            appList.add(appInfo);
        }
        return appList;
    }

    /** 从ResolveInfo的List中提取App信息 **/
    private List<AppInfo> getAppInfobyResolveInfo(List<ResolveInfo> list) {
        List<AppInfo> appList = new ArrayList<AppInfo>();
        for (ResolveInfo info : list) {
            String packageName = info.activityInfo.packageName;
            String appName = info.loadLabel(pManager).toString();
            Drawable icon = info.loadIcon(pManager);
            String launchActivityName = getLaunchActivityName(packageName);
            AppInfo appInfo = new AppInfo();
            appInfo.setPackageName(packageName);
            appInfo.setAppName(appName);
            appInfo.setIcon(icon);
            appInfo.setLaunchActivityName(launchActivityName);
            appList.add(appInfo);
        }
        return appList;
    }

    /** 获取指定包中主Activity的类名,并不是所有包都有主Activity **/
    private String getLaunchActivityName(String packageName) {
        // 根据PackageInfo对象取不出其中的主Activity,须用Intent
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.setPackage(packageName);
        List<ResolveInfo> resolveInfos = pManager.queryIntentActivities(intent,
                0);
        String mainActivityName = "";
        if (resolveInfos != null && resolveInfos.size() >= 1) {
            mainActivityName = resolveInfos.get(0).activityInfo.name;
        }
        return mainActivityName;
    }

    /** 此比较器直接复制Android源码,但是却可以把系统APP与用户APP混合排列,何解? **/
    private static class DisplayNameComparator implements
            Comparator<ResolveInfo> {
        public DisplayNameComparator(PackageManager pm) {
            mPM = pm;
        }

        public final int compare(ResolveInfo a, ResolveInfo b) {
            CharSequence sa = a.loadLabel(mPM);
            if (sa == null)
                sa = a.activityInfo.name;
            CharSequence sb = b.loadLabel(mPM);
            if (sb == null)
                sb = b.activityInfo.name;
            return sCollator.compare(sa.toString(), sb.toString());
        }

        private final Collator sCollator = Collator.getInstance();
        private PackageManager mPM;
    }

    /** 自定义的PackageInfo排序器 **/
    private static class PackageInfoComparator implements
            Comparator<PackageInfo> {
        public PackageInfoComparator(PackageManager pm) {
            mPM = pm;
        }

        public final int compare(PackageInfo a, PackageInfo b) {
            CharSequence sa = mPM.getApplicationLabel(a.applicationInfo);
            CharSequence sb = mPM.getApplicationLabel(b.applicationInfo);
            return sCollator.compare(sa.toString(), sb.toString());
        }

        private final Collator sCollator = Collator.getInstance();
        private PackageManager mPM;
    }

}

配套Model,AppInfo.java:
/**
 * App信息类
 */
public class AppInfo {
    // 包名
    private String packageName;
    // APP名
    private String appName;
    // 图标
    private Drawable icon;
    // 版本号
    private String versionName;
    // 权限
    private String[] permissions;
    // 主Activity的类名
    private String launchActivityName; 
    
    public String getLaunchActivityName() {
        return launchActivityName;
    }

    public void setLaunchActivityName(String launchActivityName) {
        this.launchActivityName = launchActivityName;
    }

    public AppInfo() {}

    public String getPackageName() {
        return packageName;
    }

    public void setPackageName(String packageName) {
        this.packageName = packageName;
    }

    public String getAppName() {
        return appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

    public Drawable getIcon() {
        return icon;
    }

    public void setIcon(Drawable icon) {
        this.icon = icon;
    }

    public String getVersionName() {
        return versionName;
    }

    public void setVersionName(String versionName) {
        this.versionName = versionName;
    }

    public String[] getPermissions() {
        return permissions;
    }

    public void setPermissions(String[] permissions) {
        this.permissions = permissions;
    };
    
    
}


四、通过反射获取APP包的大小

AndroidSDK中并没有显式提供方法获得PackageStats对象,只能通过反射机制来调用系统中隐藏的函数(@hide)。

具体方法如下:
第一步、  通过反射机制调用getPackageSizeInfo() ,方法原型为:

/*
 * @param packageName 应用程序包名
 * @param observer 当查询包的信息大小操作完成后
 * 将回调给IPackageStatsObserver类中的onGetStatsCompleted()方法,
 * 并且我们需要的PackageStats对象也封装在其参数里.
 * @hide //隐藏函数的标记
 */
public abstract void getPackageSizeInfo(String packageName, IPackageStatsObserver observer);
{
    //
}

内部调用流程如下,这个知识点较为复杂,知道即可:
getPackageSizeInfo方法内部调用getPackageSizeInfoLI(packageName, pStats)方法来完成包状态获取。
getPackageSizeInfoLI方法内部调用Installer.getSizeInfo(String pkgName, String apkPath,String fwdLockApkPath, PackageStats
pStats),继而将包状态信息返回给参数pStats。
getSizeInfo这个方法内部是以本机Socket方式连接到Server,然后向server发送一个文本字符串命令,格式:getsize apkPath fwdLockApkPath 给server。
Server将结果返回,并解析到pStats中。
掌握这个调用知识链即可。


实现代码:

/** 获取指定包的大小信息 **/
public void queryPackageSize(String packageName) throws Exception {
    Log.i(TAG, "packageName:" + packageName);
    if (packageName != null) {
        // 使用反射机制得到PackageManager类的隐藏函数getPackageSizeInfo  
        PackageManager pManager = getPackageManager();
        //通过反射机制获得该隐藏函数  
        Method getPackageSizeInfo = pManager.getClass().getMethod("getPackageSizeInfo"
                                    , String.class,IPackageStatsObserver.class);  
        //调用该函数,并且给其分配参数 ,待调用流程完成后会回调PkgSizeObserver类的函数  
        getPackageSizeInfo.invoke(pManager, packageName,new PkgSizeObserver());  
    }
}


第二步、由于需要获得系统级的服务或类,我们必须加入Android系统形成的AIDL文件,共两个: IPackageStatsObserver.aidl 和 PackageStats.aidl文件。将它们放置在android.content.pm包路径下。 


IPackageStatsObserver.aidl 文件:

package android.content.pm;  
  
import android.content.pm.PackageStats;  
/** 
 * API for package data change related callbacks from the Package Manager. 
 * Some usage scenarios include deletion of cache directory, generate 
 * statistics related to code, data, cache usage(TODO) 
 * {@hide} 
 */  
oneway interface IPackageStatsObserver {  
      
    void onGetStatsCompleted(in PackageStats pStats, boolean succeeded);  
}  


PackageStats.aidl文件:

package android.content.pm;  
  
parcelable PackageStats; 

第三步、创建一个类继承至IPackageStatsObserver.Stub()它本质上实现了Binder机制。当我们把该类的一个实例通过getPackageSizeInfo()调用时,该函数启动中间流程去获取相关包的信息大小,扫描完成后,将查询信息回调至该类的onGetStatsCompleted(PackageStats pStats, boolean succeeded)方法,信息大小封装在此实例上。

实现代码:
/** aidl文件形成的Bindler机制服务类 **/
public class PkgSizeObserver extends IPackageStatsObserver.Stub{  
    /*** 回调函数, 
     * @param pStatus ,返回数据封装在PackageStats对象中 
     * @param succeeded  代表回调成功 
     */   
    @Override  
    public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException {  
        cachesize = pStats.cacheSize; //缓存大小  
        datasize = pStats.dataSize;  //数据大小   
        codesize = pStats.codeSize;  //应用程序大小  
        totalsize = cachesize + datasize + codesize; 
    }  
}  


第四步、获取pStats的属性值(代码见上),再转换为对应的以kb/mb为计量单位的字符串。下面代码中,1 << 10是二进制的左移,相当于乘以2的10次方,即乘1024

实现代码:

/** 系统函数,字符串转换**/
private String formateFileSize(long size){
    String str = "";
    double newSize = 0;
    if (size == 0) {
        str = "0.00 B";
    } else if (size < (1 << 10)) {
        newSize = size;
        str = newSize + " B";
    } else if (size < (1 << 20)){
        newSize = 1.0 * size / (1 << 10);
        str = String.format("%.2f", newSize) + " KB";
    } else if (size < (1 << 30)) {
        newSize = 1.0 * size / (1 << 20);
        str = String.format("%.2f", newSize) + " MB";
    }
    return str;   
}  

为了能够通过反射获取应用程序大小,必须加入以下权限,否则会出现警告且得不到实际值。

<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"></uses-permission> 

流程图如下:


五、Demo


代码见笔者的Github:https://github.com/kinglearnjava/AppInfo

版权声明:本文为博主原创文章,未经博主允许不得转载。

posted @ 2015-09-15 15:25  包清骏  阅读(3330)  评论(0编辑  收藏  举报