在前段时间,公司要求做一个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")// 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
public 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
public 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;
}

同样也是用反射的机制来得到隐藏类。

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

Android学习-应用程序管理的更多相关文章

  1. CentOS学习笔记--程序管理

    程序管理 一个程序被加载到内存当中运行,那么在内存内的那个数据就被称为程序(process).程序是操作系统上非常重要的概念, 所有系统上面跑的数据都会以程序的型态存在.那么系统的程序有哪些状态?不同 ...

  2. Android学习--跨程序共享数据之内容提供其探究

    什么是内容提供器? 跨程序共享数据之内容提供器,这是个什么功能?看到这个名称的时候最能给我们提供信息的应该是“跨程序”这个词了,是的重点就是这个词,这个内容提供器的作用主要是用于在不同的引用程序之间实 ...

  3. 鸟哥的linux私房菜——第十六章学习(程序管理与 SELinux 初探)

    第十六章.程序管理与 SE Linux 初探 在 Linux 系统当中:"触发任何一个事件时,系统都会将他定义成为一个程序,并且给予这个程序一个 ID ,称为 PID,同时依据启发这个程序的 ...

  4. android学习-进程/线程管理-完整

    我们知道,应用程序的主入口都是main函数--"它是一切事物的起源" main函数工作也是千篇一律的, 初始化 比如ui的初始化,向系统申请资源等. 进入死循环 再循环中处理各种事 ...

  5. Android学习笔记_40_系统结构 目录结构

    1.系统结构: 一.应用程序层 Android平台不仅仅是操作系统,也包含了许多应用程序,诸如SMS短信客户端程序.电话拨号程序.图片浏览器.Web浏览器等应用程序.这些应用程序都是用Java语言编写 ...

  6. Android学习——第一个NDK程序

    在前面的学习中,我们已经讲解了关于NDK编程的环境搭建流程,简单的使用我们也通过官网本身自带的例子进行说明了.可是相信大家一定还存在这么的一个疑惑:“如果我要自己利用NDK编写一个Android应用, ...

  7. Android项目实战--手机卫士20--拿到已经安装了的程序以及程序管理主界面

    好了,之前我们就讲了高级工具里面的短信备份与还原,那么我们高级工具里面的功能就基本上完成的啦,还有一个叫程序锁的功能而已,但我们今天先不做它先,我们先把我们的程序管理这个功能完成先. 先让大家看一下我 ...

  8. Xamarin.Android学习之应用程序首选项

    Xamarin.Android学习之应用程序首选项 一.前言 任何App都会存在设置界面,如果开发者利用普通控件并绑定监听事件保存设置,这一过程会非常的枯燥,而且耗时.我们可以看到Android系统的 ...

  9. Android学习——移植tr069程序到Android平台

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...

随机推荐

  1. 第39届ACM亚洲区域赛牡丹江赛区赛后总结

    2014年10月10日,周五,早晨匆匆忙忙的出了寝室,直奔复印社去打了两份模板,然后直接就去上课了.第三节课下课,直接跟老师讲了一声,就去实验室跟学长们汇合了.12点半,踏上了开往牡丹江的列车,我们那 ...

  2. 使用IntelliLock加密授权你的.Net程序

    原文:使用IntelliLock加密授权你的.Net程序 转自:http://www.nsoff.com/post/2012/05/23/%E4%BD%BF%E7%94%A8IntelliLock%E ...

  3. Codeforces Round #267 (Div. 2)D(DFS+单词hash+简单DP)

    D. Fedor and Essay time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  4. YUV格式转换RGB(基于opencv)

    在编写代码将需要处理YUV格从每个视频帧中提取,然后将其保存为图片.有两种常见的方法在线,第一种是通过opencv自带cvCvtColor,可是这样的方法有bug.得到的图片会泛白.另外一种方法是公式 ...

  5. AutoMapper 创建嵌套对象映射(原创)

    之前在做DTO转换时,用到AutoMapper.但DTO的层次太深了,无奈官方没针对嵌套类型提供好的解决方案,于是自己实现了一下: 思路:采用递归和反射很好的避免手工创建嵌套对象的映射. 第一个版本, ...

  6. JQuery slideToggle 演示简单的 Slide Panel 效果。

    ------------------html--------------------------------- <html xmlns="http://www.w3.org/1999/ ...

  7. Callback

    由于 JavaScript 语句(指令)是逐一执行的 - 按照次序,动画之后的语句可能会产生错误或页面冲突,因为动画还没有完成. 为了避免这个情况,您可以以参数的形式添加 Callback 函数. j ...

  8. Visual Studio 如何给生成的exe加入多个图标资源

    Visual Studio 如何给生成的exe加入多个图标资源(快捷方式可调用) 方法: 打开你的VS, 文件—>新建—>文件 常规—>选择本机资源模板(rct文件) 如图一 新建后 ...

  9. 在ubuntu纯字符gdb界面下来开发调试嵌入式ARM

    前面一个帖子介绍了使用eclipse来开发STM32的固件,但有的时候使用Eclipse的GDB调试器会崩溃掉,反复这样造成我们开发的效率降低,信心也会受一打击. 最近接触到的许多源码,就是在linu ...

  10. hibernate在持久对象的生命周期(三州:自由状态,持久状态,自由状态 之间的转换)

    三种状态的基本概念: 1.  临时身份(Transient):也被称为自由状态,它只存在于内存中,并且在数据库中没有相应的数据. 使用new创建的对象,久化,没有处于Session中,处于此状态的对象 ...