How to check if activity is in foreground or in visible background?

我有一个计时器的启动画面。我的问题是,在 finish()之前,我的活动需要检查下一个活动是否已经开始,因为系统对话框弹出,我只想 finish(); 一旦用户从对话框中选择了一个选项?

我知道有很多问题,如何看到你的活动是在前台,但我不知道这是否允许对话框上的活动太多。

问题在这里,红色是我的活动,在背景中,而对话在前景中:

the red is my activity which is in the background while the dialogue is in the foreground

编辑: 我已经尝试过不使用 finish(),但是我的活动可以回到我试图避免的应用程序堆栈中。

240597 次浏览

你有没有试过不调用 Finish,并在清单中输入“ android: noHistory = “ true”?这将阻止活动进入堆栈。

我不得不说你的工作流程不是标准的 Android 方式。在 Android 中,如果你想打开另一个意图活动,你不需要 finish()你的活动。至于用户的便利性,Android 允许用户使用“后退”键回到你打开应用程序的活动。

因此,只要让系统停止您的活动,并保存任何需要当您的活动被调用回来。

这正是 活动类文档中描述的活动的 onPauseonStop事件之间的差异。

如果我没有理解错的话,您想要做的是从您的活动 onStop中调用 finish()来终止它。 请参阅所附的 活动生命周期演示应用程序图像。这是从活动 A 启动活动 B 时的样子。 事件的顺序是从底部到顶部的,因此您可以看到活动 A onStop是在已经调用活动 B onResume之后调用的。

Activity lifecycle demo

In case a dialog is shown your activity is dimmed in the background and only onPause is called.

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

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)活动生命周期回调

使用实现 活动生命周期回调申请,并使用它跟踪应用程序中的活动生命周期事件。请注意,ActivityLificycleCallback 针对的是 Android api > = 14。对于以前的 Android API,您需要在所有活动中自己实现它; -)

当需要跨活动共享/存储状态时,请使用 申请

2)检查正在运行的流程信息

您可以使用此类检查正在运行的进程的状态 RunningAppProcessInfo

使用 < a href = “ http://developer.android.com/reference/android/app/ActivityManager.html # getRunningAppProcises% 28% 29”> ActivityManager.getRunningAppProcises ()获取正在运行的进程列表 并过滤结果列表以检查所需的 RunningAppProcessInfo 并检查其“重要性”

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

例如,如果系统对话框是由某个按钮触发的,那么 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


}
};

在活动停止的时候:

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

为什么不用广播呢?第二个活动(需要启动的活动)可以发送这样的本地广播:

//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!
}
};

and register your new receiver with the LocalBroadcastManager to listen to the broadcast from your second activity:

//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"));

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

这就是 解决方案所推荐的:

正确的解决方案(学分归 Dan,CommonsWare 和 NeTeInStein) 自己跟踪应用程序的可见性 方法。存储“可见性”状态 好的选择是您自己的实现 应用程序或服务(也有一些这样的变体 解决方案,如果您想检查来自服务的活动可见性)。

例子 实现自定义 Application 类(注意 isActivityVisible ()静态方法) :

public class MyApplication extends Application {


public static boolean isActivityVisible() {
return activityVisible;
}


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


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


private static boolean activityVisible;
}

在 AndroidManifest.xml 中注册应用程序类:

<application
android:name="your.app.package.MyApplication"
android:icon="@drawable/icon"
android:label="@string/app_name" >

向项目中的每个活动添加 onPuse 和 onResume (您可以 如果你愿意,可以为你的活动创造一个共同的祖先,但是如果 你的活动已经从 MapActivity/ListActivity 等扩展了。 你仍须手写以下文件) :

@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}


@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}

In your finish() method, you want to use isActivityVisible() to check if the activity is visible or not. There you can also check if the user has selected an option or not. Continue when both conditions are met.

消息来源还提到了两个错误的解决方案... ... 所以避免这样做。

资料来源: 堆栈溢出

如果使用 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);

我以前经常这样,

如果该活动不在前台

GetInent ()

将返回 null。 : = P

使用暂停和从背景恢复之间的时间间隔来确定它是否从背景唤醒

在自定义应用程序中

private static boolean isInBackground;
private static boolean isAwakeFromBackground;
private static final int backgroundAllowance = 10000;


public static void activityPaused() {
isInBackground = true;
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (isInBackground) {
isAwakeFromBackground = true;
}
}
}, backgroundAllowance);
Log.v("activity status", "activityPaused");
}


public static void activityResumed() {
isInBackground = false;
if(isAwakeFromBackground){
// do something when awake from background
Log.v("activity status", "isAwakeFromBackground");
}
isAwakeFromBackground = false;
Log.v("activity status", "activityResumed");
}

在 BaseActivity 类中

@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}


@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}

如果目标 API 级别为14或更高,则可以使用 android.app.Application.ActivityLifecycleCallbacks

public class MyApplication extends Application implements ActivityLifecycleCallbacks {
private static boolean isInterestingActivityVisible;


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


// Register to be notified of activity state changes
registerActivityLifecycleCallbacks(this);
....
}


public boolean isInterestingActivityVisible() {
return isInterestingActivityVisible;
}


@Override
public void onActivityResumed(Activity activity) {
if (activity instanceof MyInterestingActivity) {
isInterestingActivityVisible = true;
}
}


@Override
public void onActivityStopped(Activity activity) {
if (activity instanceof MyInterestingActivity) {
isInterestingActivityVisible = false;
}
}


// Other state change callback stubs
....
}

我在 github 应用程序-前景-背景-听上创建了一个项目

它使用非常简单的逻辑,可以很好地适用于所有的 android API 级别。

I think I have better solution. Because you can build in simply MyApplication.activityResumed(); to every Activity by one extend.

首先你必须创建(像 CyberneticTwerkGuruOrc)

public class MyApplication extends Application {


public static boolean isActivityVisible() {
return activityVisible;
}


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


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


private static boolean activityVisible;
}

接下来,必须将 Application 类添加到 AndroidManifest.xml

<application
android:name="your.app.package.MyApplication"
android:icon="@drawable/icon"
android:label="@string/app_name" >

然后,创建类 ActivityBase

public class ActivityBase extends Activity {


@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}


@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
}

最后,当您创建新的 Activity 时,您可以简单地通过 ActivityBase 而不是 Activity 来扩展它。

public class Main extends ActivityBase {
@Override
protected void onResume() {
super.onResume();
}


@Override
protected void onPause() {
super.onPause();
}
}

对我来说,这是一个更好的方法,因为你必须记住 ActivityBase 的扩展。此外,您还可以在将来扩展您的基函数。在我的案例中,我为我的服务添加了接收器,并在一个类中添加了关于网络的警报。

如果您想检查应用程序的可见性,只需调用

MyApplication.isActivityVisible()

这可以通过使用 应用程序有效地实现

For example lets take Activity class name as ProfileActivity lets find whether its is in foreground or background

首先,我们需要通过扩展 申请类别来创建应用程序类

实现了

应用程序

让我们成为我的应用程序类,如下所示

应用类

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;
}
}

in the above class there is a override methord OnActivity 恢复活动 of 活动生命周期回调

 @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;
}

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

在 Manif.xml 中注册 Application 类

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

要检查天气活动是前景或背景根据上述解决方案调用以下方法在您需要检查的地方

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

Activity中使用这些方法。

isDestroyed()

在 Api 17中加入
如果最后的 onDestroy ()调用已经在 活动,所以这个实例现在已经死了。

isFinishing()

在 Api 1中添加
检查该活动是否在完成过程中, 要么是因为您调用了 Finish () ,要么是因为其他人请求了 这通常在 onPuse ()中使用,以确定是否 活动只是暂停或完全结束。


来自 内存泄漏文档

AsyncTask的一个常见错误是捕获对主机 Activity(或 Fragment)的强引用:

class MyActivity extends Activity {
private AsyncTask<Void, Void, Void> myTask = new AsyncTask<Void, Void, Void>() {
// Don't do this! Inner classes implicitly keep a pointer to their
// parent, which in this case is the Activity!
}
}

这是一个问题,因为 AsyncTask很容易比父 Activity活得长,例如,如果在任务运行时发生配置更改。

正确的方法是将您的任务设置为一个 static类,它不捕获父类,并将一个 弱参考保存到主机 Activity:

class MyActivity extends Activity {
static class MyTask extends AsyncTask<Void, Void, Void> {
// Weak references will still allow the Activity to be garbage-collected
private final WeakReference<MyActivity> weakActivity;


MyTask(MyActivity myActivity) {
this.weakActivity = new WeakReference<>(myActivity);
}


@Override
public Void doInBackground(Void... params) {
// do async stuff here
}


@Override
public void onPostExecute(Void result) {
// Re-acquire a strong reference to the activity, and verify
// that it still exists and is active.
MyActivity activity = weakActivity.get();
if (activity == null
|| activity.isFinishing()
|| activity.isDestroyed()) {
// activity is no longer valid, don't do anything!
return;
}


// The activity is still valid, do main-thread stuff here
}
}
}

下面是一个使用 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);

如果您有一个对所需活动的引用或者使用了该活动的规范名称,那么您就可以知道它是否在前台。这个解决方案可能并非万无一失。因此,我们非常欢迎您的意见。

我不知道为什么没有人讨论活动 A 的共享首选项,像这样设置一个共享首选项(例如在 onPuse ()中) :

SharedPreferences pref = context.getSharedPreferences(SHARED_PREF, 0);
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("is_activity_paused_a", true);
editor.commit();

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

Activity.onWindowFocusChanged(boolean hasFocus)在这里有用吗?再加上一个类级别的标志,比如 onWindowFocusChanged设置的 isFocused,这将是一个简单的方法,可以在你的活动中的任何一点判断它是否集中。从阅读文档来看,在任何活动不直接位于物理“前台”的情况下(比如显示对话框或者通知栏被拉下) ,它似乎都会正确地设置“ 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
}
}

Activity::hasWindowFocus() returns you the boolean you need.

public class ActivityForegroundChecker extends TimerTask
{
private static final long FOREGROUND_CHECK_PERIOD = 5000;
private static final long FIRST_DELAY             = 3000;


private Activity m_activity;
private Timer    m_timer;


public ActivityForegroundChecker (Activity p_activity)
{
m_activity = p_activity;
}


@Override
public void run()
{
if (m_activity.hasWindowFocus() == true) {
// Activity is on foreground
return;
}
// Activity is on background.
}


public void start ()
{
if (m_timer != null) {
return;
}
m_timer = new Timer();
m_timer.schedule(this, FIRST_DELAY, FOREGROUND_CHECK_PERIOD);
}


public void stop ()
{
if (m_timer == null) {
return;
}
m_timer.cancel();
m_timer.purge();
m_timer = null;
}
}

下面是一个示例类,用于检查活动在任何位置的可见性。

请记住,如果显示 对话框,结果将为 false,因为对话框将具有主要焦点。除此之外,它真的很方便,比建议的解决方案更可靠。

如果你想知道你的应用程序的任何活动是否显示在屏幕上,你可以这样做:

public class MyAppActivityCallbacks implements Application.ActivityLifecycleCallbacks {
private Set<Class<Activity>> visibleActivities = new HashSet<>();


@Override
public void onActivityResumed(Activity activity) {
visibleActivities.add((Class<Activity>) activity.getClass());
}


@Override
public void onActivityStopped(Activity activity) {
visibleActivities.remove(activity.getClass());
}


public boolean isAnyActivityVisible() {
return !visibleActivities.isEmpty();
}


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


@Override
public void onActivityStarted(Activity activity) {}


@Override
public void onActivityPaused(Activity activity) {}


@Override
public void onActivityDestroyed(Activity activity) {}


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

只需创建这个类的一个单例并在 Application 实例中设置它,如下所示:

class App extends Application{
@Override
public void onCreate() {
registerActivityLifecycleCallbacks(myAppActivityCallbacks);
}
}

然后您可以在任何地方使用 MyAppActivityCallback 实例的 isAnyActivityVisible ()方法!

UPD: updated to state Lifecycle.State.RESUMED. Thanks to @ htafoya for that.

2019年,在新的支持库 28+或 AndroidX 的帮助下,你可以简单地使用:

val isActivityInForeground = activity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)

您可以阅读 the documenation中的更多内容,以了解引擎盖下发生了什么。

如果您使用的是 活动巴士,它作为一种称为 hasSubscriberForEvent的方法,可以用来检查 Activity是否被聚焦。

从这里开始,您已经明确地要求了一个活动。我在 Utils 类中添加了一个简单的静态方法,通过传递活动来获取活动的状态。

    public static boolean isActivityVisible(Activity mActivity) {
if (mActivity != null) {
Class klass = mActivity.getClass();
while (klass != null) {
try {
Field field = klass.getDeclaredField("mResumed");
field.setAccessible(true);
Object obj = field.get(mActivity);
return (Boolean)obj;
} catch (NoSuchFieldException exception1) {
Log.e(TAG, exception1.toString());
} catch (IllegalAccessException exception2) {
Log.e(TAG, exception2.toString());
}
klass = klass.getSuperclass();
}
}
return false;
}

我想提一下修改。 Activity.onPause kicks in even if your app is still partly visible (may be a system dialog box over it or split screen).

也许您希望暂停仍然计数为可见,只有计数停止/销毁为不可见。

当您的活动可以重新启动时,您遇到了一个问题(我有一个重新启动按钮来反击一些不经常发生的错误)。

即使是同样的活动,它也不会在娱乐之前被销毁,而是像过渡到另一个应用程序一样被处理:

这不是对手头问题的直接回答,但是我注意到,如果您只是终止并重新启动相同的活动(在我的例子中使用片段中的重新启动按钮) ,那么上面显示的生命周期也是正确的。至少对于 Android 10来说是这样的。

重启过程看起来也像这样: MainActivity (old) MainActivity (new) . onCreate MainActivity (new) .onStart MainActivity (new) . onResume MainActivity (old) . onStop 主要活动(旧)

现在,如果您想在 onStop 中设置可见性,那么在活动的新实例的 onResume 之后就会发生这种情况,并且您错误地获得了可见性 false。

要计算您可以在 onCreate 中设置静态字符串 id:

private static String instanceId = MainActivity.this.toString();

然后在 onStop 中你可以使用

if(instanceId == null || instanceId.equals(MainActivity.this.toString()))
setVisibility(false);
//else: visibility stays unchanged