zoukankan      html  css  js  c++  java
  • 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"// 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;
    	}

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

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



















  • 相关阅读:
    wpf passwordbox控件 光标移到最后
    C#程序 给IE网页IFRAME控件中所嵌入网页的元素赋值
    C#客户端填充外部IE浏览器中网页文本(input)且不提交
    C# 获取当前网页HTML
    WPF 带有提示文本的透明文本框
    C# 导出Excel文件 所导出文件打开时提示“Excel文件格式与扩展名指定格式不一致”
    php生成验证码
    Python命名规范
    UE4碰撞规则详解
    四大编程思想简述
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3188544.html
Copyright © 2011-2022 走看看