如何检查活动是否在前台或可见后台?

126

我有一个基于计时器的启动屏幕。我的问题是在调用 finish() 方法之前,我需要检查下一个活动是否已经启动,因为系统会弹出一个对话框,我只想在用户选择对话框选项后才调用 finish() 方法。

我知道有很多问题是关于如何判断活动是否在前台运行,但我不确定这是否适用于位于活动顶部的对话框。

这是问题所在,红色表示我的活动在后台,而对话框在前台:

红色表示我的活动在后台,而对话框在前台

编辑: 我尝试过不使用 finish() 方法,但是这样我的活动会在应用程序栈中被保留,我要避免这种情况发生。


1
可能相关:https://dev59.com/OG855IYBdhLWcg3wUSgW - jarmod
为了澄清一下,您想要启动一个意图选择器,并在用户点击其中一个选项之后等待您的应用程序完成()吗?听起来您需要使用Intent.createChooser()和startActivityForResult(),然后在接收到结果后调用finish()。 - alanv
可能是检查Android应用程序是否在后台运行的重复问题 - Douglas Nassif Roma Junior
ProcessLifecycleOwner 是最新的解决方案。 - S.R
@AlexMisiulia 不,我会让投票说明一切——如果你的回答获得更多的赞同,我很乐意改变已接受的答案。 - Nick
@Nick,我明白你的观点。但问题是,已接受的答案存在缺陷,正如在评论中提到的那样,并且在某些情况下会工作不正确。更多的人会犯错并浪费时间。无论如何,这是你的选择) - Alex Misiulia
26个回答

2

可以通过使用Application.ActivityLifecycleCallbacks的高效方式来实现这一点。

例如,假设Activity类名为ProfileActivity,我们需要查找它是在前台还是后台运行。

首先,我们需要通过扩展Application Class来创建我们的应用程序类。

它实现了:

Application.ActivityLifecycleCallbacks

让我们把我的应用程序类定义如下:

应用程序类

public class AppController extends Application implements Application.ActivityLifecycleCallbacks {


private boolean activityInForeground;

@Override
public void onCreate() {
    super.onCreate();

//register ActivityLifecycleCallbacks  

    registerActivityLifecycleCallbacks(this);
   
}



public static boolean isActivityVisible() {
    return activityVisible;
}

public static void activityResumed() {
    activityVisible = true;
}

public static void activityPaused() {
    activityVisible = false;
}

private static boolean activityVisible;

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

}

@Override
public void onActivityStarted(Activity activity) {

}

@Override
public void onActivityResumed(Activity activity) {
    //Here you can add all Activity class you need to check whether its on screen or not

    activityInForeground = activity instanceof ProfileActivity;
}

@Override
public void onActivityPaused(Activity activity) {

}

@Override
public void onActivityStopped(Activity activity) {

}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

}

@Override
public void onActivityDestroyed(Activity activity) {

}

public boolean isActivityInForeground() {
    return activityInForeground;
}
}

在上述类中,有一个重写了 ActivityLifecycleCallbacksonActivityResumed 方法。
 @Override
public void onActivityResumed(Activity activity) {
    //Here you can add all Activity class you need to check whether its on screen or not

    activityInForeground = activity instanceof ProfileActivity;
}

可以在屏幕上找到当前显示的所有活动实例,只需通过上述方法检查您的活动是否在屏幕上。

在manifest.xml中注册您的应用程序类。

<application
    android:name=".AppController" />

根据上述解决方案检查Activity是前台还是后台,请在需要检查的位置调用以下方法

AppController applicationControl = (AppController) getApplicationContext();
    if(applicationControl.isActivityInForeground()){
     Log.d("TAG","Activity is in foreground")
    }
    else
    {
      Log.d("TAG","Activity is in background")
    }

1

这里是否可以使用Activity.onWindowFocusChanged(boolean hasFocus)方法?再加上一个类级别的标志,例如isFocusedonWindowFocusChanged设置,这将是一种简单的方式,在您的活动的任何时刻告诉它是否处于焦点状态。从阅读文档来看,它似乎会在任何情况下正确地设置为“false”,其中活动不直接处于物理“前景”,例如如果显示对话框或下拉通知托盘。

示例:

boolean isFocused;
@Override
void onWindowFocusChanged (boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    isFocused = hasFocus;
}

void someMethod() {
    if (isFocused) {
        // The activity is the foremost object on the screen
    } else {
        // The activity is obscured or otherwise not visible
    }
}

此答案应该被接受。onResume()在活动可见之前被调用。 - Cactusroot

1

如果您已暂停或恢复,请保存一个标志。如果您已恢复,则表示您在前台。

boolean  isResumed = false;

@Override
public void onPause() {
  super.onPause();    
  isResumed = false;
}

@Override
public void onResume() {
  super.onResume();    
  isResumed = true;
}

private void finishIfForeground() {
  if (isResumed) {
    finish();
  }
}

1
如果您正在使用EventBus,它有一个名为hasSubscriberForEvent的方法,可以用来检查是否有一个Activity被关注。

该项目似乎不再维护了。 - Akito
我认为没有太多需要维护的,因为它只做一件特定的事情。不过它仍然能够工作。 - Kris B

1
我想提到一项修改。 即使您的应用程序仍然部分可见(可能是系统对话框或拆分屏幕),Activity.onPause也会启动。
也许您希望暂停仍然被视为可见,只计算已停止/销毁的为不可见。
当您的活动可以重新启动时,您会遇到问题(我有一个重新启动按钮来对抗某些不经常发生的错误)。
即使它是相同的活动,在重新创建之前不会被销毁,而是像转换到另一个应用程序一样处理:
这不是直接回答手头的问题,但我注意到上面显示的生命周期也适用于仅终止并重新启动相同的活动(在我的情况下使用片段中的重新启动按钮)。这至少适用于Android 10。
重启过程也如下所示: MainActivity(旧).onPause MainActivity(新).onCreate MainActivity(新).onStart MainActivity(新).onResume MainActivity(旧).onStop MainActivity(旧).onDestroy
现在,如果您在onStop中设置了可见性,则在新活动实例的onResume之后发生了这种情况,您会错误地得到不可见性。
为了解决这个问题,您可以在onCreate中设置静态字符串id。
private static String instanceId = MainActivity.this.toString();

然后在 onStop 中,您可以使用。
if(instanceId == null || instanceId.equals(MainActivity.this.toString()))
    setVisibility(false);
//else: visibility stays unchanged

0
我不知道为什么没有人谈论sharedPreferences,对于Activity A,像这样设置一个SharedPreference(例如在onPause()中):
SharedPreferences pref = context.getSharedPreferences(SHARED_PREF, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("is_activity_paused_a", true);
editor.commit();

我认为这是跟踪活动可见性的可靠方法。


没有人喜欢在检查实时事件时使用SharedPreferences。我认为处理时间有点长。 - صلي علي محمد - Atef Farouk

0
这是一个使用Application类的解决方案。
public class AppSingleton extends Application implements Application.ActivityLifecycleCallbacks {

private WeakReference<Context> foregroundActivity;


@Override
public void onActivityResumed(Activity activity) {
    foregroundActivity=new WeakReference<Context>(activity);
}

@Override
public void onActivityPaused(Activity activity) {
    String class_name_activity=activity.getClass().getCanonicalName();
    if (foregroundActivity != null && 
            foregroundActivity.get().getClass().getCanonicalName().equals(class_name_activity)) {
        foregroundActivity = null;
    }
}

//............................

public boolean isOnForeground(@NonNull Context activity_cntxt) {
    return isOnForeground(activity_cntxt.getClass().getCanonicalName());
}

public boolean isOnForeground(@NonNull String activity_canonical_name) {
    if (foregroundActivity != null && foregroundActivity.get() != null) {
        return foregroundActivity.get().getClass().getCanonicalName().equals(activity_canonical_name);
    }
    return false;
}
}

您可以像下面这样简单地使用它:

((AppSingleton)context.getApplicationContext()).isOnForeground(context_activity);

如果您有所需Activity的引用或使用Activity的规范名称,您可以找出它是否在前台。但是这种解决方案可能不是万无一失的,因此非常欢迎您的评论。

0
为什么不使用广播呢?第二个需要启动的活动可以发送本地广播,如下所示:
//put this in onCreate(..) or any other lifecycle method that suits you best
//notice the string sent to the intent, it will be used to register a receiver!
Intent result = new Intent("broadcast identifier");
result.putString("some message");//this is optional
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(result);

然后在启动页活动中编写一个简单的接收器:

//this goes on the class level (like a class/instance variable, not in a method) of your splash activity:
private BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        //kill activity here!!!
        //mission accomplished!
    }
};

然后,使用LocalBroadcastManager注册新接收器以侦听来自第二个活动的广播:

//notice the string sent to the intent filter, this is where you tell the BroadcastManager which broadcasts you want to listen to!
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(receiver, new IntentFilter("broadcast identifier"));

请注意,您可以使用常量或字符串资源作为“广播标识符”字符串。

为了更好的安全性和效率,请在此处使用 LocalBroadcastManager - Alexander Farber

0
如果您只是为了避免新应用程序在您的应用程序堆栈(任务)中启动而使用finish(),则可以在启动新应用程序时使用Intent.FLAG_ACTIVITY_NEW_TASK标志,并且根本不调用finish()。根据文档,这是要用于实现“启动器”样式行为的标志。
// just add this line before you start an activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

0

可能的解决方案之一是在显示系统对话框时设置一个标志,然后在活动生命周期的onStop方法中检查该标志,如果为真,则结束活动。

例如,如果系统对话框是由某个按钮点击触发的,则onclick监听器可能如下:

private OnClickListener btnClickListener = new OnClickListener() {

    @Override
    public void onClick(View v) {           
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_SEND);
        intent.setType("text/plain");
        CheckActivity.this.startActivity(Intent.createChooser(intent, "Complete action using"));
        checkFlag = true;  //flag used to check

    }
};

并且在活动的 onStop 方法中:

@Override
protected void onStop() {
    if(checkFlag){
        finish();
    }
    super.onStop();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接