Android开发笔记(八十)运行状态检查

大家都知道刻舟求剑的寓言故事,说的是事物是发展变化着的,如果拘泥于原来的情况,那随着情况的改变,就不会得到预期的结果。同样,影响app运行的因素,并不只是外部环境(如硬件、系统、权限等等),还包括app自身的运行信息。如果app的运行状态发生了变化,那么原先处理正确的逻辑也可能处理失败,所以在特定的情况下,我们得对app的运行情况进行检查。


apk安装信息

启动app的时候,常常会检查当前apk的安装信息,以此判断接下来要做哪些准备工作,举例如下:
1、获得apk的版本号,针对不同版本,分别对SQLite的数据库表做相应的变更操作。
2、获得apk的签名,据此判断安装包是否为合法来源。签名的介绍参见《 Android开发笔记(七十三)代码混淆与反破解》。
3、获得apk的申请权限,从而判断app是否申请了相应的权限。权限的介绍参见《 Android开发笔记(七十九)资源与权限校验》。


PackageManager类便是获取apk安装信息的工具,该类的getPackageInfo方法即可获得相应的安装信息。下面是getPackageInfo方法中常用标志位参数的说明:
GET_ACTIVITIES : 获取活动Activity列表。列表信息是PackageInfo对象的activities参数。
GET_RECEIVERS : 获取广播接收器Receiver列表。列表信息是PackageInfo对象的receivers参数。
GET_SERVICES : 获取服务Service列表。列表信息是PackageInfo对象的services参数。
GET_SIGNATURES : 获取签名列表。列表信息是PackageInfo对象的signatures参数。
GET_PERMISSIONS : 获取权限列表。列表信息是PackageInfo对象的requestedPermissions参数。


下面是PackageManager类常用的通用参数说明:
packageName : 包名
versionName : 版本名称
versionCode : 版本代码
firstInstallTime : 首次安装时间
lastUpdateTime : 最后更新时间


多进程时判断是否为主进程

通常我们会在Application的扩展类中初始化全局变量,方便后续的信息交互,Application的介绍参见《 Android开发笔记(二十八)利用Application实现内存读写》。有时候我们又会使用多进程模式,让服务运行在单独的进程中,这样就造成一个问题:新进程是由主进程原样fork出来,即新进程也会执行Application的onCreate方法。但是按照我们的本意,新进程只是为了运行单独的服务,并不需要初始化不相关的全局变量,因此这时候就得判断当前进程是否为主进程。
下面是判断是否为主进程的示例代码:
	public static boolean isMainProcess(Context ctx) {
		String processName = "";
		ActivityManager actMgr = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
		for (ActivityManager.RunningAppProcessInfo appProcess : actMgr.getRunningAppProcesses()) {
			if (appProcess.pid == Process.myPid()) {
				processName = appProcess.processName;
				Log.d(TAG, "packageName=" + ctx.getPackageName());
				Log.d(TAG, "processName=" + processName);
			}
		}
		if (ctx.getPackageName().equals(processName)) {
			return true;
		} else {
			return false;
		}
	}


下面是Application扩展类中的调用代码:
public class MainApplication extends Application {
	private final static String TAG = "MainApplication";
	
	@Override
	public void onCreate() {
		super.onCreate();
		if (ActivityUtil.isMainProcess(getApplicationContext()) == true) {
			//下面初始化主进程的参数
			Log.d(TAG, "当前是主进程");
		} else {
			//这里是分支进程,不处理主进程的参数
			Log.d(TAG, "当前是分支进程");
		}
	}
	
}


Activity是否存在

一般在Activity代码中都是直接操作页面上的元素,不过有时候得小心,页面在操作UI前就不存在了,其中的一个例子可见《 Android开发笔记(七十五)内存泄漏的处理》。在上面这篇文章中,我们为了防止Handler的内存泄漏,给Activity加了个弱引用对象,由于弱引用是可以被回收的,因此在使用前得判断弱引用对象是否为空,只有对象非空,才能操作其上的UI元素。


除了上面的例子,还有其它情况可能导致操作UI时的页面丢失,基本上都是些异步操作造成的,比如说处理线程消息的Handler,以及广播接收器BroadcastReceiver。这些情况要想判断Activity页面是否存在,就得借助于ActivityManager的getRunningTasks方法,详细代码如下:
	public static boolean isActivityWork(Context ctx, String activityName) {
	    boolean isWork = false;
	    ActivityManager actMgr = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
	    List<RunningTaskInfo> taskList = actMgr.getRunningTasks(1024);
	    for (int i = 0; i < taskList.size(); i++) {
	        String name = taskList.get(i).topActivity.getClassName();
	        Log.d(TAG, "i="+i+", activity name="+name);
	        if (name.equals(activityName) == true) {
	            isWork = true;
	            break;
	        }
	    }
	    return isWork;
	}


Service是否存在

与Activity类似,Service也可能被安全软件杀死,导致使用该服务报空指针异常(比如接收广播后调用startForeground方法)。判断后台服务是否存在,与活动的判断一样,都是采用ActivityManager工具类,不同的是,该工具获取服务列表调用的是getRunningServices方法,详细代码如下:
	public static boolean isServiceWork(Context ctx, String serviceName) {
	    boolean isWork = false;
	    ActivityManager actMgr = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
	    List<RunningServiceInfo> serviceList = actMgr.getRunningServices(1024);
	    for (int i = 0; i < serviceList.size(); i++) {
	        String name = serviceList.get(i).service.getClassName().toString();
	        Log.d(TAG, "i="+i+", service name="+name);
	        if (name.equals(serviceName) == true) {
	            isWork = true;
	            break;
	        }
	    }
	    return isWork;
	}





点此查看Android开发笔记的完整目录

你可能感兴趣的:(android,apk,activitymanager,packagemanager,运行状态)