在前段时间,公司要求做一个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; }
同样也是用反射的机制来得到隐藏类。
到这里,应用程序管理的功能就差不多了,剩下就只是界面上的事情和程序的处理流程上的事情,应该还好!