(转)Android学习-应用程序管理

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

主要运用到的类有android

PackageManager数据库

ActivityManager缓存

ApplicationInfo服务器

RunningServiceInfo数据结构

Methodapp

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

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

 

[java]  view plain copy
 
 
  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 plain copy
 
 
  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 plain copy
 
 
  1. // 系统函数,字符串转换 long -String (kb)  
  2. private String formateFileSize(long size) {  
  3.     return Formatter.formatFileSize(MainActivity.this, size);  
  4. }  

 

 

好,如今咱们来清除用户数据,这里要用到以前下载的那个文件IPackageDataObserver,跟获取app大小同样的,经过回调来实现。

 

[java]  view plain copy
 
 
  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 plain copy
 
 
  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 plain copy
 
 
  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 plain copy
 
 
  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;  

一样也是用反射的机制来获得隐藏类。

 

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

 

 

 

 

 

原文:http://www.ophonesdn.com/article/show/20

 

 

1、概述

      07年,Google公司推出了基于Java语言的Android平台,引发了IT业界的一致关注,同年,播思通信公司开始研发基于其的OPhone平台。

      08年,第一款Android手机G1面世,这款Google为HTC量身打造的滑盖手机由T Mobile发行,得到了用户的极大好评,也为整个Android平台的正式商用做出了榜样。

      09年,将是OPhone/Android手机的第一个井喷年,播思通信联合中国移动和各个手机厂商即将推出多款触屏手机。同时,Android社区也日趋壮大,已拥有各类应用五百多个,并在快速增加之中。

      本文将对Android应用结构及Android系统的应用管理加以介绍。

      本文面对的读者是对Android应用的研发人员、或有过Android开发经验的入门者。

2、Android应用结构 

什么是Android应用?

      理解什么是Android应用是Android入门所必须的,也是了解应用管理的前提。

      那什么是Android应用呢?即指基于Android开发,编译,运行在Android平台的应用。这种应用在编译阶段被打成一个jar包,以.apk结尾,包内包含了全部运行阶段须要的代码与资源,其中主要分为三块:描述文件AndroidManifest.xml、代码段和运行时资源。

AndroidManifest.xml

      AndroidManifest.xml是Android应用的描述文件,它描述了该应用的相关信息,主要包括如下各个元素。

包名(package)

      指定本应用内java主程序包的包名。当没有指定apk的文件名时,编译后产生程序包将以此命名。本包名应当在Android系统运行时惟一。

认证(certificate)

      指定本应用程序所授予的信任级别,目前有的认证级别有platform(system)、shared、media以及应用自定义的认证。不一样的认证能够享受不一样的权限。

权限组(permission-group)

      权限组的定义是为了描述一组具备共同特性的权限。Android系统中预订了一些组,它们是:

  • android.permission-group.COST_MONEY
  • android.permission-group.MESSAGES
  • android.permission-group.MESSAGES
  • android.permission-group.PERSONAL_INFO
  • android.permission-group.LOCATION
  • android.permission-group.NETWORK
  • android.permission-group.ACCOUNTS
  • android.permission-group.HARDWARE_CONTROLS
  • android.permission-group.PHONE_CALLS
  • android.permission-group.SYSTEM_TOOLS
  • android.permission-group.DEVELOPMENT_TOOLS

      从字面意思咱们就能够理解每一个组的特性。具体含义能够参考SDK文档。

权限(permission)

      权限用来描述是否拥有作某件事的权力。Android系统中权限是分级的,前分为普通级别(Normal),危险级别(dangerous),签名级别(signature)和系统/签名级别(signature or system)。

      系统中全部预约义的权限根据做用的不一样,分别属于不一样的级别。

      对于普通和危险级别的权限,咱们称之为低级权限,应用申请即授予。其余两级权限,咱们称之为高级权限或系统权限,应用拥有platform级别的认证才能申请。

      当应用试图在没有权限的状况下作受限操做,应用将被系统杀掉以警示。

      系统应用可使用任何权限。权限的声明者可无条件使用该权限。

      目前Android系统定义了许多权限,经过SDK文档用户能够查询到哪些操做须要哪些权限,而后按需申请。

权限树(permission-tree)

      权限树的设置是为了统一管理一组权限,声明于该树下的权限全部者归属该应用。系统提供了API,应用能够在运行时动态添加。
     PackageManager.addPermission()
 

使用权限(uses-permission)

      应用须要的权限应当在此处申请,所申请的权限应当被系统或某个应用所定义,不然视为无效申请。
      同时,使用权限的申请须要遵循权限授予条件,非platform认证的应用没法申请高级权限。
 

SDK(uses-sdk)

      标识本应用运行的SDK版本。高兼容性的应用能够忽略此项。

application

      application是Android应用内最高级别(top level)的模块,每一个应用内最多只能有一个application,若是应用没有指定该模块,一个默认的application将被启用。

      application将在应用启动时最早被加载,并存活在应用的整个运行时生命周期。所以一些初始化的工做适合在本模块完成。

      Application元素有许多属性,其中:“persistent”表示本应用是否为常驻内存,“enable”表示本应用当前是否应当被加载。

      其它相关属性请参考SDK文档,开发者能够根据须要添加。

      在AndroidManifest.xml文件中,运行时模块的定义都做为本模块的子元素。

      当运行时模块被调度时,若是应用没有启动,将首先启动应用进行初始化,而后调度对应模块。

activity

      activity是application模块的运行时子元素,标识了一个UI。除了application,一个应用能够声明并实现零至多个其它运行时模块,activity也一样。

      activity也包含了许多定义它工做状态的属性,其中:“name”是必须的,它指定了该activity所在的文件名,若是该文件所属包不一样于该应用的包名(即本描述文件的最开始处),那么名字前面须要加入所在包名。

      activity经过增长intent-fliter来标识哪些intent能够被处理,同时intent也是调度activity的主要参数。

      做为一个运行时的对象,activity的调度方式大体分为两种:一种是指定activit所在类名直接调度,另外一种是利用activity能够处理的intent进行调度。

      同一种intent能够被不一样应用的不一样activity处理。当出现此种状况时,用户须要选择具体的activity。

receiver

      receiver也是application的运行时子元素。

      receiver经过增长intent-fliter来标识它须要接受哪些intent。当收到intent后,receiver将根据不一样的intent进行不一样的处理。

      当一个Intent发出后,全部注册了该intent的receiver都将会收到,系统会根据receiver在系统中的注册次序顺序发送。当一个receiver处理完该Intent后,系统才会向下一个receiver发送。

      当一个receiver有多个未接收的intent时,将按照intent发送的次序顺序接收。

service

      service也是application的运行时子元素。Service属于后台模块,启动后将长时间运行,除非中止该service或所在应用进程被杀死。

      service从功能上分为两种,一种是服务于本应用,此时的service是一个普通的运行时模块,另外一种是服务于全部应用或对应应用,此时须要定义API并将之公布来与其它应用进行交互。

      service须要经过API:startService()添加到service管理器中,添加后即在后台运行。它接受外界信息的方式分两种:一种是增长IntentFilter来接收intent,一种是外界应用调用该service所定义的API。

provider

      provider也是application的运行时子元素。它继承于ContentProvider,是对该应用管理的用户数据的结构化接入,是基于数据库操做方式的封装。

      若是应用容许外部应用访问/管理它的用户数据,provider是Android平台提供的最佳方式。

activity-alias

      顾名思义,是已有activity的别名。

uses-library

      标识应用启动所必须的共享库。

代码段

      应用全部的java文件被放入一个包结构,该包命名为classes.dex。

运行时资源

      Android应用运行时所需的各类资源有layout,drawable,string,style等类型。编译后全部资源统一存放在项目路径/res里,按照用途的不一样存放在各个子文件夹中。

      编译阶段,全部resource将被排序,每一个resource在程序包中都拥有惟一的标识,同时一个名为resources.arsc的文件生成并被置入安装包中,该文件包含了全部索引以供运行时快速查询。而resource以一样目录结构组织放入安装包中。

应用管理

      应用管理是对系统中全部应用整个生命周期的管理。Android系统中,应用都是以.apk文件的形式存在,所以应用管理也能够简单理解为对.apk文件的管理。

      从一个应用在系统中的生命周期来看,应用管理能够分为安装,卸载和使用等部分。

应用安装

      Android系统中,安装应用时不能指定安装目录,全部的应用都只能装在预置文件夹下。

      Android系统支持数种安装方式,而播思通信的Ophone平台在此基础上支持更多方式。

<使用预置安装工具

      Android系统中,应用安装包.apk文件属于默认支持的文件类型,它的的mime type被定义为"application/vnd.android.package-archive"。

      系统内置了安装工具来解析并安装.apk文件。你能够从OPhone平台的文件管理器中找到对应安装包,点击便可启动安装步骤。这里给出了相应的代码,有兴趣的开发者能够尝试使用。

  1. Intent apkintent = new Intent(Intent.ACTION_VIEW);                 
  2. final Uri puri = Uri.fromFile(new File(path));    //path is the path of source apk   
  3. apkintent.setDataAndType(puri, "application/vnd.android.package-archive");   
  4. startActivity(apkintent);  
Java代码   收藏代码
  1. Intent apkintent = new Intent(Intent.ACTION_VIEW); final Uri puri = Uri.fromFile(new File(path)); //path is the path of source apk apkintent.setDataAndType(puri, "application/vnd.android.package-archive"); startActivity(apkintent);  
 

使用API安装

      Android系统提供了安装应用的API:PackageManager.installPackage(final Uri packageURI, 
     final IPackageInstallObserver observer, final int flags); 

  • uri给出了原文件的地址
  • observer实现了回调函数packageInstalled(final String packageArchiveName, final int returnCode) 以检查安装是否成功
  • flags提供安装标识。Android平台提供的标识有: 
1.PackageManager.FORWARD_LOCK_PACKAGE = 0x00000001
标识本应用属于受限应用,应用将会被安装到保护目录,只有该应用能够访问代码段和不属于resource的asset资源。
2.PackageManager.REPLACE_EXISTING_PACKAGE = 0x00000002;
应用如已存在,则先删除再安装。
3.PackageManager.SDCARD_PACKAGE = 0x00000004;
 

      由OPhone平台扩展,标识该应用安装目录为存储卡,默认为data分区。

      由于手机中data分区大小有限,所以OPhone平台默认将应用装到存储卡上。

      本API须要"android.permission.INSTALL_PACKAGES"权限,该权限属于签名级别,所以只有系统级别的应用程序才能使用。

拷贝即安装

      Android支持拷贝即安装的方式,即将安装包拷贝到预置安装目录便可自动安装,但全部的预置目录对于用户不可见。

      OPhone平台扩展后,用户可将安装包拷入存储卡的app目录下,系统将监控该文件夹,检测到新的安装文件后自动安装。

      这种方式适合一次安装大量应用,可是也有坏处。一是没法知道哪些应用安装成功,哪些安装失败, 二是安装失败的应用将会做为垃圾存放在预置目录中,在系统启动时延长启动时间,三是过多的应用可能将data分区的空间占用过多,影响系统的正常使用。

安装步骤

  • 解析安装包是否合法,若是合法即加载应用,若是非法即放弃安装
  • 取出代码段,放入/data/dalvik-cache目录
  • 创建/data/data/包名的文件夹,以存放用户数据
  • 将安装包携带的lib放入/data/data/包名/lib中
  • 以上步骤完成后将安装包拷到安装目录
  • 加载该应用信息 
相关文章
相关标签/搜索