zoukankan      html  css  js  c++  java
  • 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;  
    同样也是用反射的机制来得到隐藏类。

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

  • 相关阅读:
    HDU 1114 Piggy-Bank
    HDU 2955 Robberies
    NTOJ 290 动物统计(加强版)
    POJ 3624 Charm Bracelet
    HDU 2602 Bone Collector
    POJ 1523 SPF(无向图割顶)
    HDU 5311 Hidden String
    HDU 1421 搬寝室
    HDU 1058 Humble Numbers
    POJ 3259 Wormholes(spfa判负环)
  • 原文地址:https://www.cnblogs.com/bigben0123/p/4286485.html
Copyright © 2011-2022 走看看