Android学习-应用程序管理

Android学习-应用程序管理

在前段时间,公司要求做一个Android系统的应用程序管理,要实现卸载程序、清除数据、停止正在运行的服务这几大模块,现在将代码粗略总结如下:

主要运用到的类有

PackageManager

ActivityManager

ApplicationInfo

RunningServiceInfo

Method

还有两个android.pm下的源文件用于生成桩,IPackageStatsObserver.java  和 IPackageDataObserver.java,由名字可以看出,他们是跟包的状态和大小有关的,在网上找到这两个文件的源码后,把他们放在工程src目录下的android.pm包下,自己建包。

首先要获得系统中已经装了的apk,apk分为两类第一是系统的apk,第二是第三方的apk,所以在获取apk时可以指定一个过滤器,见如下代码:

[java] view plaincopy
 
 
  1. // 添加过滤 ,得到全部程序,系统程序,用户自己安装的程序  
  2.     private List<AppInfo> queryFilterAppInfo(int filter) {  
  3.         // 查询所有已经安装的应用程序  
  4.         List<ApplicationInfo> listAppcations = pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);  
  5.         Collections.sort(listAppcations,new ApplicationInfo.DisplayNameComparator(pm));// 排序  
  6.         List<AppInfo> appInfos = new ArrayList<AppInfo>(); // 保存过滤查到的AppInfo  
  7.         // 根据条件来过滤  
  8.         switch (filter) {  
  9.         case FILTER_ALL_APP: // 所有应用程序  
  10.             appInfos.clear();  
  11.             for (ApplicationInfo app : listAppcations) {  
  12.                 if (app.packageName.equals("com.android.appmanager")) { // 过滤自己  
  13.                     continue;  
  14.                 }  
  15.                 appInfos.add(getAppInfo(app));  
  16.             }  
  17.             return appInfos;  
  18.         case FILTER_SYSTEM_APP: // 系统程序  
  19.             appInfos.clear();  
  20.             for (ApplicationInfo app : listAppcations) {  
  21.                 if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {  
  22.                     if (app.packageName.equals("com.android.appmanager"<span style="font-family:Arial, Helvetica, sans-serif;">)</span>// wifi { // 过滤自己  
  23.                             continue;  
  24.                         }  
  25.                     appInfos.add(getAppInfo(app));  
  26.                 }  
  27.             }  
  28.             return appInfos;  
  29.         case FILTER_THIRD_APP: // 第三方应用程序  
  30.             appInfos.clear();  
  31.             for (ApplicationInfo app : listAppcations) {  
  32.                 // 非系统程序  
  33.                 if ((app.flags & ApplicationInfo.FLAG_SYSTEM) <= 0) {  
  34.                     if (app.packageName.equals("com.android.appmanager"))  
  35.                             continue;  
  36.                         }  
  37.                     appInfos.add(getAppInfo(app));  
  38.                 }  
  39.                 // 本来是系统程序,被用户手动更新后,该系统程序也成为第三方应用程序了  
  40.                 else if ((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {  
  41.                     if (app.packageName.equals("geeya.android.appmanage")) { // 过滤自己  
  42.                         continue;  
  43.                     }  
  44.                     appInfos.add(getAppInfo(app));  
  45.                 }  
  46.             }  
  47.             break;  
  48.         default:  
  49.             return null;  
  50.         }  
  51.         return appInfos;  
  52.     }  

AppInfo是我自己定义的一个类,里面包含了应用程序的包名、数据区大小、代码区大小、等等一些属性。

好,现在我们来获取app包的数据区大小、缓存区大小、代码区大小,这里要用反射的机制去获取PackageManager类的隐藏方法getPackageSizeInfo(),这个方法的具体实现是通过回调函数来实现的,这里要用到IPackageStatsObserver这个类生成的桩。

[java] view plaincopy
 
 
  1. // aidl文件形成的Bindler机制服务类  
  2.     public class PkgSizeObserver extends IPackageStatsObserver.Stub {  
  3.         /*** 
  4.          * 回调函数, 
  5.          *  
  6.          * @param pStatus 
  7.          *            ,返回数据封装在PackageStats对象中 
  8.          * @param succeeded 
  9.          *            代表回调成功 
  10.          */  
  11.         @Override  
  12.         public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException {  
  13.             long cachesize; // 缓存大小  
  14.             long datasize; // 数据大小  
  15.             long codesize; // 应用程序大小  
  16.             long totalsize; // 总大小  
  17.             // System.out.println("data start init!");  
  18.             synchronized (Integer.class) {  
  19.                 cachesize = pStats.cacheSize; // 缓存大小  
  20.                 datasize = pStats.dataSize; // 数据大小  
  21.                 codesize = pStats.codeSize; // 应用程序大小  
  22.                 totalsize = cachesize + datasize + codesize;  
  23.                 Message msg = mHandler.obtainMessage();  
  24.   
  25.                 msg.what = MSG_SIZE_CHANGE;  
  26.                 Bundle bundle = new Bundle();  
  27.                 bundle.putLong("cachesize", cachesize);  
  28.                 bundle.putLong("datasize", datasize);  
  29.                 bundle.putLong("codesize", codesize);  
  30.                 bundle.putLong("totalsize", totalsize);  
  31.   
  32.                 bundle.putString("packageName", pStats.packageName);  
  33.                 msg.obj = bundle;  
  34.                 mHandler.sendMessage(msg);  
  35.             }  
  36.         }  
  37.     }  
  38.       
  39.     //获取每个apk的大小信息,包括数据区、代码区、缓存区  
  40.     // 查询包的大小信息  
  41.     public void queryPacakgeSize(String pkgName) throws Exception {  
  42.         if (pkgName != null) {  
  43.             // 使用放射机制得到PackageManager类的隐藏函数getPackageSizeInfo  
  44.             PackageManager pm = getPackageManager(); // 得到pm对象  
  45.             try {  
  46.                 // 通过反射机制获得该隐藏函数  
  47.                 Method getPackageSizeInfo = pm.getClass().getDeclaredMethod("getPackageSizeInfo", String.class,  
  48.                         IPackageStatsObserver.class);  
  49.                 getPackageSizeInfo.invoke(pm, pkgName, new PkgSizeObserver());  
  50.             } catch (Exception ex) {  
  51.                 // Log.e(TAG, "NoSuchMethodException");  
  52.                 ex.printStackTrace();  
  53.                 throw ex; // 抛出异常  
  54.             }  
  55.         }  
  56.     }  
或得到app的大小数据后,封装成消息发送出去,这是最好的方法!!

这里也介绍一个将long型数据转换成文件大小格式的数据。

[java] view plaincopy
 
 
  1. // 系统函数,字符串转换 long -String (kb)  
  2. private String formateFileSize(long size) {  
  3.     return Formatter.formatFileSize(MainActivity.this, size);  
  4. }  

好,现在我们来清除用户数据,这里要用到之前下载的那个文件IPackageDataObserver,跟获取app大小一样的,通过回调来实现。

[java] view plaincopy
 
 
  1.      // 清除用户数据的操作  
  2. class ClearUserDataObserver extends IPackageDataObserver.Stub {  
  3.     public void onRemoveCompleted(final String packageName,final boolean succeeded) {  
  4.         final Message msg = mHandler.obtainMessage();  
  5.         if (succeeded) {  
  6.             msg.what = CLEAR_USER_DATA;  
  7.         } else {  
  8.             msg.what = NOT_CLEAR_USER_DATA;  
  9.         }  
  10.         mHandler2.sendMessage(msg);  
  11.     }  
  12. }  
  13. // 清除apk的数据  
  14. public void clearAppUserData(String pkgname){  
  15.     // 经测试,该方法不能用反射得到,没办法,我只好这样写,只能在源码下编译。  
  16.     pm.clearApplicationUserData(pkgname, new ClearUserDataObserver());  
  17. }  

好,现在到卸载程序的时候了,看代码

[java] view plaincopy
 
 
  1.       // 卸载apk   
  2. ublic void unInstallApp(String pkgname) {  
  3. Log.e("unInstallApp(String pkgname)","pkgname  is"+ pkgname);  
  4. Intent intent = new Intent();  
  5. // 该动作是我在android框架层自己定义的一个动作,DELETE.HIDE,表明直接就卸载了。不经过系统原生的那一个对话。  
  6. intent.setAction("android.intent.action.DELETE.HIDE"); // 自己定义的动作,DELETE.HIDE,不需要经过系统的确认卸载界面。直接卸载!  
  7. Uri packageURI = Uri.parse("package:" + pkgname);  
  8. intent.setData(packageURI);  
  9. startActivity(intent);  

关于apk的管理就差不多了,现在来看看正在运行的服务的管理

首先,获取正在运行的服务:

这里我的RunningInfo是我自己定义的一个类,主要服务的一些属性,比如包名、uid、pid等等那些

[java] view plaincopy
 
 
  1. // 得到正在运行的服务  
  2. public List<RunningInfo> getRunningService() {  
  3.     List<RunningServiceInfo> runServiceList = am.getRunningServices(30);  
  4.     List<RunningInfo> Services_List = new ArrayList<RunningInfo>(); // 保存过滤查到的AppInfo  
  5.     Log.e("getRunningService.size = ",  
  6.             new Integer(runServiceList.size()).toString());  
  7.     String pkgname = "";  
  8.     ApplicationInfo appByPkgName = null;  
  9.     for (RunningServiceInfo info : runServiceList) {  
  10.         pkgname = info.service.getPackageName();  
  11.         // System.out.println("service package is :" + pkgname +  
  12.         // "   pid = "+ info.pid); // 程序的ID号  
  13.         // 过滤掉这些系统本身的服务。只显示用户安装的服务  
  14.         if (pkgname.equals("com.android.appmanager") ) { // 过滤自己  
  15.                 continue;  
  16.             }  
  17.         try {  
  18.             appByPkgName = pm.getApplicationInfo(pkgname,  
  19.                     PackageManager.GET_UNINSTALLED_PACKAGES); // 最后一个参数一般为0  
  20.                                                                 // 就好。  
  21.         } catch (NameNotFoundException e) {  
  22.             // Log.e("MainActivity 841 line","appByPkgName = pm.getApplicationInfo(pkgname, PackageManager.GET_UNINSTALLED_PACKAGES) Exception !");  
  23.             e.printStackTrace();  
  24.         }  
  25.   
  26.         Services_List.add(getRunningInfo(appByPkgName)); // 里面含有相同的包的服务。这里哦我们只要求显示一个即可。  
  27.     }  
  28.     // 放入set中过滤相同包名的, 这里我复写了RunningInfo 的compareTo方法你 规则是 pkgName相等两个对象就算相等!  
  29.     Set<RunningInfo> set = new HashSet<RunningInfo>();  
  30.     for (RunningInfo x : Services_List) {  
  31.         set.add(x);  
  32.     }  
  33.     for (RunningInfo y : set) {  
  34.         Services_List.add(y);  
  35.     }  
  36.     return Services_List;  
  37. }  


好,获取到了正在运行的服务之后,就可以随意停止服务了,停止服务的代码是:

[java] view plaincopy
 
 
  1.       // 强行停止一个app  
  2. ublic boolean stopApp(String pkgname) {  
  3. boolean flag = false;  
  4. ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);  
  5. try {  
  6.     Method forceStopPackage;  
  7.     forceStopPackage = am.getClass().getDeclaredMethod("forceStopPackage", String.class); // 反射得到隐藏方法(hide)  
  8.     forceStopPackage.setAccessible(true);//获取私有成员变量的值  
  9.     forceStopPackage.invoke(am, pkgname);  
  10.     flag = true;  
  11. catch (IllegalArgumentException e) {  
  12.     e.printStackTrace();  
  13.     flag = false;  
  14. catch (IllegalAccessException e) {  
  15.     e.printStackTrace();  
  16.     flag = false;  
  17. catch (InvocationTargetException e) {  
  18.     e.printStackTrace();  
  19.     flag = false;  
  20. catch (SecurityException e) {  
  21.     e.printStackTrace();  
  22.     flag = false;  
  23. catch (NoSuchMethodException e) {  
  24.     e.printStackTrace();  
  25.     flag = false;  
  26. }  
  27. return flag;  
同样也是用反射的机制来得到隐藏类。

到这里,应用程序管理的功能就差不多了,剩下就只是界面上的事情和程序的处理流程上的事情,应该还好!

原文地址:https://www.cnblogs.com/bigben0123/p/4286485.html