(转)Android学习-应用程序管理
在前段时间,公司要求做一个Android系统的应用程序管理,要实现卸载程序、清除数据、停止正在运行的服务这几大模块,现在将代码粗略总结如下:
主要运用到的类有
PackageManager
ActivityManager
ApplicationInfo
RunningServiceInfo
Method
还有两个android.pm下的源文件用于生成桩,IPackageStatsObserver.java 和 IPackageDataObserver.java,由名字可以看出,他们是跟包的状态和大小有关的,在网上找到这两个文件的源码后,把他们放在工程src目录下的android.pm包下,自己建包。
首先要获得系统中已经装了的apk,apk分为两类第一是系统的apk,第二是第三方的apk,所以在获取apk时可以指定一个过滤器,见如下代码:
- // 添加过滤 ,得到全部程序,系统程序,用户自己安装的程序
- private List<AppInfo> queryFilterAppInfo(int filter) {
- // 查询所有已经安装的应用程序
- List<ApplicationInfo> listAppcations = pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
- Collections.sort(listAppcations,new ApplicationInfo.DisplayNameComparator(pm));// 排序
- List<AppInfo> appInfos = new ArrayList<AppInfo>(); // 保存过滤查到的AppInfo
- // 根据条件来过滤
- switch (filter) {
- case FILTER_ALL_APP: // 所有应用程序
- appInfos.clear();
- for (ApplicationInfo app : listAppcations) {
- if (app.packageName.equals("com.android.appmanager")) { // 过滤自己
- continue;
- }
- appInfos.add(getAppInfo(app));
- }
- return appInfos;
- case FILTER_SYSTEM_APP: // 系统程序
- appInfos.clear();
- for (ApplicationInfo app : listAppcations) {
- if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- if (app.packageName.equals("com.android.appmanager"<span style="font-family:Arial, Helvetica, sans-serif;">)</span>// wifi { // 过滤自己
- continue;
- }
- appInfos.add(getAppInfo(app));
- }
- }
- return appInfos;
- case FILTER_THIRD_APP: // 第三方应用程序
- appInfos.clear();
- for (ApplicationInfo app : listAppcations) {
- // 非系统程序
- if ((app.flags & ApplicationInfo.FLAG_SYSTEM) <= 0) {
- if (app.packageName.equals("com.android.appmanager"))
- continue;
- }
- appInfos.add(getAppInfo(app));
- }
- // 本来是系统程序,被用户手动更新后,该系统程序也成为第三方应用程序了
- else if ((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
- if (app.packageName.equals("geeya.android.appmanage")) { // 过滤自己
- continue;
- }
- appInfos.add(getAppInfo(app));
- }
- }
- break;
- default:
- return null;
- }
- return appInfos;
- }
AppInfo是我自己定义的一个类,里面包含了应用程序的包名、数据区大小、代码区大小、等等一些属性。
好,现在我们来获取app包的数据区大小、缓存区大小、代码区大小,这里要用反射的机制去获取PackageManager类的隐藏方法getPackageSizeInfo(),这个方法的具体实现是通过回调函数来实现的,这里要用到IPackageStatsObserver这个类生成的桩。
- // aidl文件形成的Bindler机制服务类
- public class PkgSizeObserver extends IPackageStatsObserver.Stub {
- /***
- * 回调函数,
- *
- * @param pStatus
- * ,返回数据封装在PackageStats对象中
- * @param succeeded
- * 代表回调成功
- */
- @Override
- public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException {
- long cachesize; // 缓存大小
- long datasize; // 数据大小
- long codesize; // 应用程序大小
- long totalsize; // 总大小
- // System.out.println("data start init!");
- synchronized (Integer.class) {
- cachesize = pStats.cacheSize; // 缓存大小
- datasize = pStats.dataSize; // 数据大小
- codesize = pStats.codeSize; // 应用程序大小
- totalsize = cachesize + datasize + codesize;
- Message msg = mHandler.obtainMessage();
- msg.what = MSG_SIZE_CHANGE;
- Bundle bundle = new Bundle();
- bundle.putLong("cachesize", cachesize);
- bundle.putLong("datasize", datasize);
- bundle.putLong("codesize", codesize);
- bundle.putLong("totalsize", totalsize);
- bundle.putString("packageName", pStats.packageName);
- msg.obj = bundle;
- mHandler.sendMessage(msg);
- }
- }
- }
- //获取每个apk的大小信息,包括数据区、代码区、缓存区
- // 查询包的大小信息
- public void queryPacakgeSize(String pkgName) throws Exception {
- if (pkgName != null) {
- // 使用放射机制得到PackageManager类的隐藏函数getPackageSizeInfo
- PackageManager pm = getPackageManager(); // 得到pm对象
- try {
- // 通过反射机制获得该隐藏函数
- Method getPackageSizeInfo = pm.getClass().getDeclaredMethod("getPackageSizeInfo", String.class,
- IPackageStatsObserver.class);
- getPackageSizeInfo.invoke(pm, pkgName, new PkgSizeObserver());
- } catch (Exception ex) {
- // Log.e(TAG, "NoSuchMethodException");
- ex.printStackTrace();
- throw ex; // 抛出异常
- }
- }
- }
或得到app的大小数据后,封装成消息发送出去,这是最好的方法!!
这里也介绍一个将long型数据转换成文件大小格式的数据。
- // 系统函数,字符串转换 long -String (kb)
- private String formateFileSize(long size) {
- return Formatter.formatFileSize(MainActivity.this, size);
- }
好,现在我们来清除用户数据,这里要用到之前下载的那个文件IPackageDataObserver,跟获取app大小一样的,通过回调来实现。
- // 清除用户数据的操作
- class ClearUserDataObserver extends IPackageDataObserver.Stub {
- public void onRemoveCompleted(final String packageName,final boolean succeeded) {
- final Message msg = mHandler.obtainMessage();
- if (succeeded) {
- msg.what = CLEAR_USER_DATA;
- } else {
- msg.what = NOT_CLEAR_USER_DATA;
- }
- mHandler2.sendMessage(msg);
- }
- }
- // 清除apk的数据
- public void clearAppUserData(String pkgname){
- // 经测试,该方法不能用反射得到,没办法,我只好这样写,只能在源码下编译。
- pm.clearApplicationUserData(pkgname, new ClearUserDataObserver());
- }
好,现在到卸载程序的时候了,看代码
- // 卸载apk
- ublic void unInstallApp(String pkgname) {
- Log.e("unInstallApp(String pkgname)","pkgname is"+ pkgname);
- Intent intent = new Intent();
- // 该动作是我在android框架层自己定义的一个动作,DELETE.HIDE,表明直接就卸载了。不经过系统原生的那一个对话。
- intent.setAction("android.intent.action.DELETE.HIDE"); // 自己定义的动作,DELETE.HIDE,不需要经过系统的确认卸载界面。直接卸载!
- Uri packageURI = Uri.parse("package:" + pkgname);
- intent.setData(packageURI);
- startActivity(intent);
关于apk的管理就差不多了,现在来看看正在运行的服务的管理
首先,获取正在运行的服务:
这里我的RunningInfo是我自己定义的一个类,主要服务的一些属性,比如包名、uid、pid等等那些
- // 得到正在运行的服务
- public List<RunningInfo> getRunningService() {
- List<RunningServiceInfo> runServiceList = am.getRunningServices(30);
- List<RunningInfo> Services_List = new ArrayList<RunningInfo>(); // 保存过滤查到的AppInfo
- Log.e("getRunningService.size = ",
- new Integer(runServiceList.size()).toString());
- String pkgname = "";
- ApplicationInfo appByPkgName = null;
- for (RunningServiceInfo info : runServiceList) {
- pkgname = info.service.getPackageName();
- // System.out.println("service package is :" + pkgname +
- // " pid = "+ info.pid); // 程序的ID号
- // 过滤掉这些系统本身的服务。只显示用户安装的服务
- if (pkgname.equals("com.android.appmanager") ) { // 过滤自己
- continue;
- }
- try {
- appByPkgName = pm.getApplicationInfo(pkgname,
- PackageManager.GET_UNINSTALLED_PACKAGES); // 最后一个参数一般为0
- // 就好。
- } catch (NameNotFoundException e) {
- // Log.e("MainActivity 841 line","appByPkgName = pm.getApplicationInfo(pkgname, PackageManager.GET_UNINSTALLED_PACKAGES) Exception !");
- e.printStackTrace();
- }
- Services_List.add(getRunningInfo(appByPkgName)); // 里面含有相同的包的服务。这里哦我们只要求显示一个即可。
- }
- // 放入set中过滤相同包名的, 这里我复写了RunningInfo 的compareTo方法你 规则是 pkgName相等两个对象就算相等!
- Set<RunningInfo> set = new HashSet<RunningInfo>();
- for (RunningInfo x : Services_List) {
- set.add(x);
- }
- for (RunningInfo y : set) {
- Services_List.add(y);
- }
- return Services_List;
- }
好,获取到了正在运行的服务之后,就可以随意停止服务了,停止服务的代码是:
- // 强行停止一个app
- ublic boolean stopApp(String pkgname) {
- boolean flag = false;
- ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- try {
- Method forceStopPackage;
- forceStopPackage = am.getClass().getDeclaredMethod("forceStopPackage", String.class); // 反射得到隐藏方法(hide)
- forceStopPackage.setAccessible(true);//获取私有成员变量的值
- forceStopPackage.invoke(am, pkgname);
- flag = true;
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- flag = false;
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- flag = false;
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- flag = false;
- } catch (SecurityException e) {
- e.printStackTrace();
- flag = false;
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- flag = false;
- }
- return flag;
同样也是用反射的机制来得到隐藏类。
到这里,应用程序管理的功能就差不多了,剩下就只是界面上的事情和程序的处理流程上的事情,应该还好!
原文:http://www.ophonesdn.com/article/show/20
一、概述
07年,Google公司推出了基于Java语言的Android平台,引起了IT业界的一致关注,同年,播思通讯公司开始研发基于其的OPhone平台。
08年,第一款Android手机G1面世,这款Google为HTC量身打造的滑盖手机由T Mobile发行,获得了用户的极大好评,也为整个Android平台的正式商用作出了榜样。
09年,将是OPhone/Android手机的第一个井喷年,播思通讯联合中国移动和各个手机厂商即将推出多款触屏手机。同时,Android社区也日趋壮大,已拥有各种应用五百多个,并在快速增长之中。
本文将对Android应用结构及Android系统的应用管理加以介绍。
本文面对的读者是对Android应用的研发人员、或有过Android开发经验的入门者。
二、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平台的文件管理器中找到对应安装包,点击即可启动安装步骤。这里给出了相应的代码,有兴趣的开发者可以尝试使用。
- 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);
- 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平台提供的标识有:
标识本应用属于受限应用,应用将会被安装到保护目录,只有该应用可以访问代码段和不属于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中
- 以上步骤完成后将安装包拷到安装目录
- 加载该应用信息

浙公网安备 33010602011771号