持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情
判断App是否在前台的几种方式
平常的开发中常用到这种判断,例如我们IM的消息,如果我们收到别人发给我的消息了,是否应该在顶部通知栏展示,如果在前台,那么就不需要展示通知,如果在后台就需要展示通知。
如何判断应用在前台还是在后台,目前比较好用的有三种方式:
一、RunningTaskInfo的判断
当App在前台的时候,就会在栈顶,我们通过ActivityManager拿到运行中的栈,拿到栈顶的Activity,并判断当前运行的的App包名是否相同。
public static boolean isBackground(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = ((ActivityManager.RunningTaskInfo) tasks.get(0)).topActivity;
return topActivity != null && !TextUtils.isEmpty(topActivity.getPackageName()) &&
!topActivity.getPackageName().equals(context.getPackageName());
}
return false;
}
使用的时候, 先点test1 再点test2 然后返回homne页面等待Log。
fun test1() {
YYLogUtils.w("Check Background1: " + BackgroundCheck.isBackground(commContext()))
}
fun test2() {
CommUtils.getHandler().postDelayed({
YYLogUtils.w("Check Background2:" + BackgroundCheck.isBackground(commContext()))
},3000)
}
二、RunningAppProcessInfo的判断
通过 ActivityManager 获取当前运行的进程,遍历找到我们当前应用的进程。判断这个进程是否处于前台。
public static boolean isBackgroundProcess(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
boolean isBackground = true;
String processName = "empty";
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.processName.equals(context.getPackageName())) {
processName = appProcess.processName;
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED) {
isBackground = true;
} else if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|| appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
isBackground = false;
} else {
isBackground = true;
}
}
}
YYLogUtils.i("是否在后台:" + isBackground + " processName:" + processName);
return isBackground;
}
使用的时候, 同样的先点test1 再点test2 然后返回homne页面等待Log。
fun test1() {
YYLogUtils.w("Check BackgroundProcess1: " + BackgroundCheck.isBackgroundProcess(commContext()))
}
fun test2() {
CommUtils.getHandler().postDelayed({
YYLogUtils.w("Check BackgroundProcess2:" + BackgroundCheck.isBackgroundProcess(commContext()))
},3000)
}
三、ActivityLifecycleCallbacks的回调
通过监听Application中注册Activity的生命周期回调,通过记录Activity的状态,并通过count来计数,判断是否在前台
public class ForegroundCheck implements Application.ActivityLifecycleCallbacks {
//单例
private static final ForegroundCheck instance = new ForegroundCheck();
private static final int CHECK_DELAY = 500;
//用于判断是否程序在前台
private boolean foreground = false, paused = true;
//handler用于处理切换activity时的短暂时期可能出现的判断错误
private final Handler handler = new Handler();
private Runnable check;
public static void init(Application app) {
app.registerActivityLifecycleCallbacks(instance);
}
public static ForegroundCheck get() {
return instance;
}
private ForegroundCheck() {
}
@Override
public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
}
@Override
public void onActivityPaused(@NonNull Activity activity) {
paused = true;
if (check != null)
handler.removeCallbacks(check);
handler.postDelayed(check = () -> {
if (foreground && paused) {
foreground = false;
}
}, CHECK_DELAY);
}
@Override
public void onActivityResumed(@NonNull Activity activity) {
paused = false;
foreground = true;
if (check != null)
handler.removeCallbacks(check);
}
@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
}
@Override
public void onActivityStarted(@NonNull Activity activity) {
if (count == 0) {
//后台切换到前台
YYLogUtils.i("监听到应用进入前台");
}
count++;
}
@Override
public void onActivityStopped(@NonNull Activity activity) {
count--;
if (count == 0) {
//前台切换到后台
YYLogUtils.i("监听到应用进入后台了");
}
}
//记录Activity的总个数,用于判断监听是在前后还是后台
public int count = 0;
/**
* 判断当前应用在前台还是在后台,如果在后台会启动推送
*/
public boolean isForeground() {
return foreground;
}
}
使用: 必须在Application中初始化,传入application对象去注册生命周期回调。
@HiltAndroidApp
class DemoApplication : BaseApplication() {
override fun onCreate() {
super.onCreate()
ForegroundCheck.init(this)
}
}
测试:同样的先点test1 再点test2 然后返回homne页面等待Log。
fun test1() {
YYLogUtils.w("ForegroundCheck isForeground: " + ForegroundCheck.get().isForeground)
}
fun test2() {
CommUtils.getHandler().postDelayed({
YYLogUtils.w("ForegroundCheck isForeground: " + ForegroundCheck.get().isForeground)
},3000)
}
四、DefaultLifecycleObserver 实现监听
基于 lifecycle 高版本实现的监听,
需要版本2.4.0及以上版本,依赖版本如下:
androidx.lifecycle:lifecycle-process:2.4.1
androidx.lifecycle:lifecycle-runtime-ktx:2.4.1
然后非常简单的即可实现同样的逻辑
@HiltAndroidApp
class DemoApplication : BaseApplication() {
override fun onCreate() {
super.onCreate()
//必须process版本为2.4.0版本以上 (集成process默认添加startup库)
ProcessLifecycleOwner.get().lifecycle.addObserver(AutoForegroundObserver())
}
//必须common为2.4.0版本以上
inner class AutoForegroundObserver: DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
//应用进入前台
YYLogUtils.w("DemoApplication-应用进入前台")
}
override fun onStop(owner: LifecycleOwner) {
//应用进入后台
YYLogUtils.w("DemoApplication-应用进入后台")
}
}
}
效果:
其本质还是调用的 registerActivityLifecycleCallbacks 监听了所有Activity的onStart onStop ,注意 process 库是在 Application 的 onCreate 方法之前的,内部又依赖到了StartUp库。
总结
目前的几种方法都可以使用,测试平台 Android12 三星A14手机 完美运行。
个人是比较推荐第三种方式,没有使用系统服务,相对更健壮一点。并且我们还能在监听的方法中管理 Activity 对 Activity 的数量统计之类的做操作。不需要高版本的 lifecycle 库。
比如我们定义一个类 ActivityManage 我们可以使用栈 Stack<Activity> 来管理当前的Activity。 在 ForegroundCheck 类中 onActivityCreated 的时候 添加到栈,在 onActivityDestroyed 的时候移除栈。同样的可以实现Activity 的栈管理。
相关的源码都已经在上面贴出,如果还想更方便的可以看源码。
完结!