android学习笔记一 PackageManager
主要作用:管理应用程序包,通过它获取应用程序信息
摘自 https://blog.csdn.net/u012532559/article/details/52805632
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>节点的信息。
字段说明:
- 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 包名
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的信息 配合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>
流程图如下: