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;  
    同样也是用反射的机制来得到隐藏类。

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

  • 相关阅读:
    TypeScript系列
    常用软件工具
    How to change Eclipse loading image
    【奥斯卡理财星体系 第五章】丨手把手教你从零开始搭建资产配置
    【奥斯卡理财星体系 第四章】丨你该如何选择适合的理财工具
    【奥斯卡理财星体系 第三章】丨你适合追求什么样的收益率
    ASP.NET Core使用Elasticsearch记录API请求响应日志实战演练
    C# Quartz.NET实现动态改变作业调度周期
    openssl 生成pfx
    netcore进程内(InProcess)托管和进程外(out-of-Process)托管
  • 原文地址:https://www.cnblogs.com/bigben0123/p/4286485.html
Copyright © 2011-2022 走看看