參考資料: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



版權聲明:本文為博主原創文章,未經博主允許不得轉載。