在某些特定场景下,我们需要判断App是否处于后台运行状态,常见如:推送或聊天消息是否需要显示到通知栏中、设置手势密码状态下应用是否显示解锁界面等。
Android系统中判断App的前后台运行状态,大家常用的就是ActivityManager
这个类,获取RunningAppProcessInfo
或者RunningTaskInfo
这两种方式,代码如下:
/**
* 判断app是否处于前台
* @param context
* @return
*/
public static boolean isAppForeground(Context context){
ActivityManager activityManager = (ActivityManager) context.getSystemService(Service.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfoList = activityManager.getRunningAppProcesses();
if (runningAppProcessInfoList==null){
return false;
}
for (ActivityManager.RunningAppProcessInfo processInfo : runningAppProcessInfoList) {
if (processInfo.processName.equals(context.getPackageName()) &&
processInfo.importance==ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
return true;
}
}
return false;
}
和
/**
* 判断app是否处于前台
* @param context
* @return
*/
public static boolean isRunningForeground (Context context) {
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
String currentPackageName = cn.getPackageName();
if(!TextUtils.isEmpty(currentPackageName) && currentPackageName.equals(context.getPackageName())) {
return true ;
}
return false ;
}
抛开性能问题不谈,这两种方式看上去貌似没有问题。但是请注意我用了“貌似”这个词呢,说明还是有问题的。
通常,我们会在Activity的onStop生命周期方法中判断应用是否进入后台。而用户在进行应用间切换、按HOME键进入桌面、按开机键进入锁屏状态会影响到应用的前后台运行状态。通过Debug可以看到,切换到其他应用(按HOME键也是切换到其他应用)时,这两种方法返回false,表明应用进入后台运行状态,但按关机键进入锁屏状态时,返回true,也就是说App依然处于前台运行状态。拿手势密码案例来说,如果用户按开机键进入锁屏状态,当再次按开机键回到应用时,当然希望应用之前也算进入后台状态而显示解锁界面,显然,上面两种方法已经不满足我们的需求了。
那么有没有一种更好的方式也能监听到用户使用开机键进入手机锁屏状态下呢?答案当然是有!通过Activity的生命周期方法我们可以实现这样的功能。
我们知道,如果应用处于前台,当前Activity一定调用了onResume方法,而应用进入后台,当前Activity一定调用了onStop方法。根据这个原理,如果我们使用一个计数变量activityCount,初始化为0,在我们自定义的所有Activity基类BaseActivity对应的生命周期方法中做加减计数处理,那么,当activityCount为1时,则表示应用处于前台运行状态,反之,为0时表示应用处于后台运行状态。
利用Activity生命周期来判断应用的前后台运行状态,很轻松的获取了各种情境下应用的前后台状态变换。但是有个细节需要注意,activityCount的加减处理应该加在Activity的哪个生命周期方法里面。
很多朋友会不假思索的选择在onPause方法里面减一,在onResume方法里面加一。这种情况下,会出现一个短暂的间隔问题。
比如从Activity A跳转至Activity B,A和B的生命周期变化经历了如下一个过程:
A onPause ——> B onCreate ——> B onStart ——> B onResume ——> A onStop
可以看出,在A onPause到 B onResume之间,根据我们对activityCount的判断,表示在此短暂的阶段应用处于后台运行状态,而实际情况是前台运行状态,显然是有问题的。
所以,正确的方式应该是在onStart里面对activityCount加一,在onStop里面对Activity减一,而这个阶段的时间小到可以忽略不计。
上述这种间隔问题主要考虑到诸如消息推送这场景下的一个应用前后台状态的判断,而在手势密码这种使用场景下,无需考虑,也可以在其他生命周期方法中处理,只需要在onResume方法中判断是否需要展示解锁解界面。
补充:应用进入后台再回到前台时,当前Activity生命周期的变化过程如下,通常往往容易忽视在onRestart之后的onStart方法:
onPause ——> onStop ——> onRestart ——> onStart ——> onResume