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

Android PackageManager 用法

2015-09-15 15:25 507 查看
参考资料: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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: