要实现卸载程序、清除数据、停止正在运行的服务这几大模块,现在将代码粗略总结如下:
主要运用到的类有
PackageManager
ActivityManager
ApplicationInfo
RunningServiceInfo
Method
还有两个android.pm下的源文件用于生成桩,IPackageStatsObserver.java 和 IPackageDataObserver.java,由名字可以看出,他们是跟包的状态和大小有关的,在网上找到这两个文件的源码后,把他们放在工程src目录下的android.pm包下,自己建包。
首先要获得系统中已经装了的apk,apk分为两类第一是系统的apk,第二是第三方的apk,所以在获取apk时可以指定一个过滤器,见如下代码:
java代码
1. // 添加过滤 ,得到全部程序,系统程序,用户自己安装的程序
2.
3. private List queryFilterAppInfo(int filter) {
4. // 查询所有已经安装的应用程序
5. List listAppcations =
6. pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
7. Collections.sort(listAppcations,new
8. ApplicationInfo.DisplayNameComparator(pm));// 排序
9. List appInfos = new ArrayList(); // 保存过滤查到的AppInfo
10. // 根据条件来过滤
11. switch (filter) {
12. case FILTER_ALL_APP: // 所有应用程序
13. appInfos.clear();
14. for (ApplicationInfo app : listAppcations) {
15. if (app.packageName.equals("com.android.appmanager")) { // 过滤自己
16. continue;
17. }
18. appInfos.add(getAppInfo(app));
19. }
20. return appInfos;
21. case FILTER_SYSTEM_APP: // 系统程序
22. appInfos.clear();
23. for (ApplicationInfo app : listAppcations) {
24. if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
25. if (app.packageName.equals("com.android.appmanager""font-family:Arial,
26. Helvetica, sans-serif;">)// wifi { // 过滤自己
27. continue;
28. }
29. appInfos.add(getAppInfo(app));
30. }
31. }
32. return appInfos;
33. case FILTER_THIRD_APP: // 第三方应用程序
34. appInfos.clear();
35. for (ApplicationInfo app : listAppcations) {
36. // 非系统程序
37. if ((app.flags & ApplicationInfo.FLAG_SYSTEM) <= 0) {
38. if (app.packageName.equals("com.android.appmanager"))
39. continue;
40. }
41. appInfos.add(getAppInfo(app));
42. }
43. // 本来是系统程序,被用户手动更新后,该系统程序也成为第三方应用程序了
44. else if ((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)
45. {
46. if (app.packageName.equals("geeya.android.appmanage")) { // 过滤自己
47. continue;
48. }
49. appInfos.add(getAppInfo(app));
50. }
51. }
52. break;
53. default:
54. return null;
55. }
56. return appInfos;
57. }
复制代码
AppInfo是我自己定义的一个类,里面包含了应用程序的包名、数据区大小、代码区大小、等等一些属性。
好,现在我们来获取app包的数据区大小、缓存区大小、代码区大小,这里要用反射的机制去获取PackageManager类的隐藏方法getPackageSizeInfo(),这个方法的具体实现是通过回调函数来实现的,这里要用到IPackageStatsObserver这个类生成的桩。
java代码
1. // aidl文件形成的Bindler机制服务类
2.
3. public class PkgSizeObserver extends IPackageStatsObserver.Stub {
4. /***
5. * 回调函数,
6. *
7. * @param pStatus
8. * ,返回数据封装在PackageStats对象中
9. * @param succeeded
10. * 代表回调成功
11. */
12. @Override
13. public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
14. throws RemoteException {
15. long cachesize; // 缓存大小
16. long datasize; // 数据大小
17. long codesize; // 应用程序大小
18. long totalsize; // 总大小
19. // System.out.println("data start init!");
20. synchronized (Integer.class) {
21. cachesize = pStats.cacheSize; // 缓存大小
22. datasize = pStats.dataSize; // 数据大小
23. codesize = pStats.codeSize; // 应用程序大小
24. totalsize = cachesize + datasize + codesize;
25. Message msg = mHandler.obtainMessage();
26. msg.what = MSG_SIZE_CHANGE;
27. Bundle bundle = new Bundle();
28. bundle.putLong("cachesize", cachesize);
29. bundle.putLong("datasize", datasize);
30. bundle.putLong("codesize", codesize);
31. bundle.putLong("totalsize", totalsize);
32. bundle.putString("packageName", pStats.packageName);
33. msg.obj = bundle;
34. mHandler.sendMessage(msg);
35. }
36. }
37. }
38. //获取每个apk的大小信息,包括数据区、代码区、缓存区
39. // 查询包的大小信息
40. public void queryPacakgeSize(String pkgName) throws Exception {
41. if (pkgName != null) {
42. // 使用放射机制得到PackageManager类的隐藏函数getPackageSizeInfo
43. PackageManager pm = getPackageManager(); // 得到pm对象
44. try {
45. // 通过反射机制获得该隐藏函数
46. Method getPackageSizeInfo =
47. 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代码
1. // 系统函数,字符串转换 long -String (kb)
2.
3. private String formateFileSize(long size) {
4. return Formatter.formatFileSize(MainActivity.this, size);
5. }
复制代码
好,现在我们来清除用户数据,这里要用到之前下载的那个文件IPackageDataObserver,跟获取app大小一样的,通过回调来实现。
java代码
1. // 清除用户数据的操作
2.
3. class ClearUserDataObserver extends IPackageDataObserver.Stub {
4. public void onRemoveCompleted(final String packageName,final boolean
5. succeeded) {
6. final Message msg = mHandler.obtainMessage();
7. if (succeeded) {
8. msg.what = CLEAR_USER_DATA;
9. } else {
10. msg.what = NOT_CLEAR_USER_DATA;
11. }
12. mHandler2.sendMessage(msg);
13. }
14. }
15. // 清除apk的数据
16. public void clearAppUserData(String pkgname){
17. // 经测试,该方法不能用反射得到,没办法,我只好这样写,只能在源码下编译。
18. pm.clearApplicationUserData(pkgname, new ClearUserDataObserver());
19. }
复制代码
好,现在到卸载程序的时候了,看代码
java代码
1. // 卸载apk
2.
3. ublic void unInstallApp(String pkgname) {
4. Log.e("unInstallApp(String pkgname)","pkgname is"+ pkgname);
5. Intent intent = new Intent();
6. // 该动作是我在android框架层自己定义的一个动作,DELETE.HIDE,表明直接就卸载了。不经过系统原生的那一个对话。
7. intent.setAction("android.intent.action.DELETE.HIDE"); //
8. 自己定义的动作,DELETE.HIDE,不需要经过系统的确认卸载界面。直接卸载!
9. Uri packageURI = Uri.parse("package:" + pkgname);
10. intent.setData(packageURI);
11. startActivity(intent);
复制代码
关于apk的管理就差不多了,现在来看看正在运行的服务的管理
首先,获取正在运行的服务:
这里我的RunningInfo是我自己定义的一个类,主要服务的一些属性,比如包名、uid、pid等等那些
java代码
1. // 得到正在运行的服务
2.
3. public List getRunningService() {
4. List runServiceList = am.getRunningServices(30);
5. List Services_List = new ArrayList(); //
6. 保存过滤查到的AppInfo
7. Log.e("getRunningService.size = ",
8. new Integer(runServiceList.size()).toString());
9. String pkgname = "";
10. ApplicationInfo appByPkgName = null;
11. for (RunningServiceInfo info : runServiceList) {
12. pkgname = info.service.getPackageName();
13. // System.out.println("service package is :" + pkgname +
14. // " pid = "+ info.pid); // 程序的ID号
15. // 过滤掉这些系统本身的服务。只显示用户安装的服务
16. if (pkgname.equals("com.android.appmanager") ) { // 过滤自己
17. continue;
18. }
19. try {
20. appByPkgName = pm.getApplicationInfo(pkgname,
21. PackageManager.GET_UNINSTALLED_PACKAGES); // 最后一个参数一般为0
22. // 就好。
23. } catch (NameNotFoundException e) {
24. // Log.e("MainActivity 841 line","appByPkgName =
25. pm.getApplicationInfo(pkgname, PackageManager.GET_UNINSTALLED_PACKAGES)
26. Exception !");
27. e.printStackTrace();
28. }
29. Services_List.add(getRunningInfo(appByPkgName)); //
30. 里面含有相同的包的服务。这里哦我们只要求显示一个即可。
31. }
32. // 放入set中过滤相同包名的, 这里我复写了RunningInfo 的compareTo方法你 规则是
33. pkgName相等两个对象就算相等!
34. Set set = new HashSet();
35. for (RunningInfo x : Services_List) {
36. set.add(x);
37. }
38. for (RunningInfo y : set) {
39. Services_List.add(y);
40. }
41. return Services_List;
42. }
复制代码
好,获取到了正在运行的服务之后,就可以随意停止服务了,停止服务的代码是:
java代码
1. // 强行停止一个app
2.
3. ublic boolean stopApp(String pkgname) {
4. boolean flag = false;
5. ActivityManager am = (ActivityManager)
6. mContext.getSystemService(Context.ACTIVITY_SERVICE);
7. try {
8. Method forceStopPackage;
9. forceStopPackage = am.getClass().getDeclaredMethod("forceStopPackage",
10. String.class); // 反射得到隐藏方法(hide)
11. forceStopPackage.setAccessible(true);//获取私有成员变量的值
12. forceStopPackage.invoke(am, pkgname);
13. flag = true;
14. } catch (IllegalArgumentException e) {
15. e.printStackTrace();
16. flag = false;
17. } catch (IllegalAccessException e) {
18. e.printStackTrace();
19. flag = false;
20. } catch (InvocationTargetException e) {
21. e.printStackTrace();
22. flag = false;
23. } catch (SecurityException e) {
24. e.printStackTrace();
25. flag = false;
26. } catch (NoSuchMethodException e) {
27. e.printStackTrace();
28. flag = false;
29. }
30. return flag;
复制代码
同样也是用反射的机制来得到隐藏类。
到这里,应用程序管理的功能就差不多了,
剩下就只是界面上的事情和程序的处理流程上的事情,应该还好!