參考資料: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
2. ActivityInfo類
<activity>或者 <receiver>節點信息 。可獲取theme 、launchMode、launchmode等屬性
3. ServiceInfo類
<service>節點信息。
4. ApplicationInfo類
<application>節點的信息。
字段說明:
flags字段: FLAG_SYSTEM 系統應用程序 FLAG_EXTERNAL_STORAGE 表示該應用安裝在sdcard中
5. ResolveInfo類
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 類
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 包名
二、常用代碼片
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的信息 配合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);
}
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
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
