您的位置:首页 > 移动开发 > Android开发

Android PackageManager 用法

2017-01-13 22:47 393 查看
参考资料:http://blog.csdn.net/qinjuning/article/details/6867806,有改动。

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

AnroidManifest.xml文件节点说明:



一 、相关类介绍

1. PackageItemInfo类

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

常用字段:

[java] view
plain copy

int icon 资源图片在R文件中的值 (对应于android:icon属性)

int labelRes label在R文件中的值(对应于android:label属性)

String name 节点的name值 (对应于android:name属性)

String packagename 应用程序的包名 (对应于android:packagename属性)

常用方法:

[java] view
plain copy

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>节点的信息。

字段说明:

[java] view
plain copy

flags字段:

FLAG_SYSTEM 系统应用程序

FLAG_EXTERNAL_STORAGE 表示该应用安装在sdcard中

5. ResolveInfo类

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

[java] view
plain copy

ActivityInfo activityInfo 获取 ActivityInfo对象,即<activity>或<receiver>节点信息

ServiceInfo serviceInfo 获取 ServiceInfo对象,即<service>节点信息

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

6. PackageInfo类

AndroidManifest.xml文件的信息

常用字段:



[java] view
plain copy

String packageName 包名

ActivityInfo[] activities 所有<activity>节点信息

ApplicationInfo applicationInfo <application>节点信息,只有一个

ActivityInfo[] receivers 所有<receiver>节点信息,多个

ServiceInfo[] services 所有<service>节点信息 ,多个

7. PackageManger 类

通过getPackageManager()方法获得。
常用方法:

[java] view
plain copy

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)。

常用字段:

[java] view
plain copy

long cachesize 缓存大小

long codesize 应用程序大小

long datasize 数据大小

String packageName 包名


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

二、常用代码片

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

[java] view
plain copy

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信息:

[java] view
plain copy

// APP包名

resolve.activityInfo.packageName;

// APP icon

resolve.loadIcon(packageManager);

// APP名称

resolve.loadLabel(packageManager).toString();

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

[java] view
plain copy

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信息:

[java] view
plain copy

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信息的两种方法:

[java] view
plain copy

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:

[java] view
plain copy

/** 判断是不是系统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信息:

[java] view
plain copy

/**

* 获取手机上安装的所有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:

[java] view
plain copy

/**

* 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() ,方法原型为:

[java] view
plain copy

/*

* @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中。

掌握这个调用知识链即可。

实现代码:

[java] view
plain copy

/** 获取指定包的大小信息 **/

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 文件:

[java] view
plain copy

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文件:

[java] view
plain copy

package android.content.pm;

parcelable PackageStats;

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

实现代码:

[java] view
plain copy

/** 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

实现代码:

[java] view
plain copy

/** 系统函数,字符串转换**/

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;

}

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

[html] view
plain copy

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

流程图如下:



五、Demo







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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: