如何检查活动是在前景还是在可见背景中?

时间:2013-08-03 23:45:56

标签: android android-activity dialog activity-finish

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

我知道有很多关于如何查看您的活动是否在前台的问题,但我不知道这是否允许在活动之上设置对话框。

这是问题,红色是我的活动,在对话在前台时在后台:

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

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

25 个答案:

答案 0 :(得分:174)

这是推荐的正确解决方案:

  

正确的解决方案(积分归Dan,CommonsWare和NeTeInStEiN)   使用自己跟踪应用程序的可见性   Activity.onPause,Activity.onResume方法。存储“可见性”状态   在其他一些班级。好的选择是你自己的实施   应用程序或服务(也有一些变体   解决方案,如果您想检查服务的活动可见性。)

     

示例   实现自定义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" >
  

将onPause和onResume添加到项目中的每个Activity(您可以   如果您愿意,可以为您的活动创建一个共同的祖先,但是如果   您的活动已从MapActivity / ListActivity等扩展。   你还需要手工编写以下内容):

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

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

finish()方法中,您希望使用isActivityVisible()来检查活动是否可见。在那里,您还可以检查用户是否选择了选项。在满足两个条件时继续。

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

来源:stackoverflow

答案 1 :(得分:61)

如果定位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
    ....
}

答案 2 :(得分:10)

这正是Activity class documentation中描述的活动的onPauseonStop事件之间的差异。

如果我理解正确,您要执行的操作是从您的活动finish()拨打onStop来终止它。 请参阅Activity Lifecycle Demo App的附图。这是从活动A启动活动B时的样子。 事件的顺序是从下到上,因此您可以看到在调用活动B onStop之后调用活动A onResume

Activity lifecycle demo

如果显示对话框,您的活动在后台变暗,只调用onPause

答案 3 :(得分:8)

Activity::hasWindowFocus()会返回您需要的布尔值。

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,因为对话框将具有主要焦点。除此之外,它比建议的解决方案更方便,更可靠。

答案 4 :(得分:7)

两种可能的解决方案:

1)活动LifeCycle回调

使用实现ApplicationActivityLifecycleCallbacks并使用它来跟踪应用程序中的活动生命周期事件。请注意,ActivityLifecycleCallbacks适用于Android api&gt; = 14.对于以前的Android API,您需要在所有活动中自行实现; - )

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

2)检查运行过程信息

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

使用ActivityManager.getRunningAppProcesses()获取正在运行的进程列表 并筛选结果列表以检查所需的RunningAppProcessInfo并检查其“重要性”

答案 5 :(得分:5)

在2019年借助新的支持库28+或AndroidX,您可以轻松使用:

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

您可以在the documenation中阅读更多内容,以了解幕后发生的事情。

答案 6 :(得分:4)

我在github上创建了项目app-foreground-background-listen

它使用非常简单的逻辑,适用于所有Android API级别。

答案 7 :(得分:3)

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

在自定义应用程序

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

答案 8 :(得分:2)

这可以通过使用Application.ActivityLifecycleCallbacks

以有效的方式实现这一目标

例如,让我们将活动类名称作为 ProfileActivity ,让我们找出它是在前景还是背景中

首先我们需要通过扩展Application Class

来创建我们的应用程序类

实现

  

Application.ActivityLifecycleCallbacks

让我的Application类如下

申请类

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

在上面的课程中有一个覆盖 ActivityLifecycleCallbacks onActivityResumed

 @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类

<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")
    }

答案 9 :(得分:2)

我想我有更好的解决方案。因为你可以简单地构建MyApplication.activityResumed();每个活动一个延伸。

首先你必须创建(如Cyber​​neticTwerkGuruOrc)

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()

答案 10 :(得分:1)

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

因此,只需让系统停止您的活动并保存调用活动时所需的任何内容。

答案 11 :(得分:1)

如果您使用的是EventBus,它是一种称为hasSubscriberForEvent的方法,可用于检查Activity是否聚焦。

答案 12 :(得分:1)

如果您想知道屏幕上是否显示您的应用的任何活动,您可以执行以下操作:

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

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

答案 13 :(得分:1)

您是否尝试不调用finish,并在清单中放置“android:noHistory =”true“?这将阻止活动进入堆栈。

答案 14 :(得分:0)

如果您仅使用finish()来避免新应用在应用的堆栈(任务)中启动,则可以在启动新应用时使用Intent.FLAG_ACTIVITY_NEW_TASK标志,而不要调用{{1}完全没有。根据{{​​3}},这是用于实现“启动器”样式行为的标志。

finish()

答案 15 :(得分:0)

Activity内使用这些方法。

isDestroyed()

  

在Api 17中添加
  如果已对最终的onDestroy()调用进行调用,则返回true   活动,所以这个实例现在已经死了。

isFinishing()

  

在Api 1中添加
  检查此活动是否正在完成,   要么是因为你在上面调用了finish(),要么就是其他人要求的   它完成了。这通常用于onPause()来确定是否   活动只是暂停或完全结束。


  

来自Memory Leaks Documentation

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课程,该课程不会捕获父级,并将weak reference保留给主持人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
    }
  }
}

答案 16 :(得分: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);

如果您有对所需活动的引用或使用活动的规范名称,您可以查明它是否在前台。这个解决方案可能不是万无一失的。因此,您的评论非常受欢迎。

答案 17 :(得分: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();

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

答案 18 :(得分:0)

Activity.onWindowFocusChanged(boolean hasFocus)会在这里有用吗?这个加上一个类级别的标志,如isFocused设置onWindowFocusChanged之类的东西,如果它是关注的话,将是一个简单的方法来告诉您活动中的任何一点。通过阅读文档,看起来在活动不直接位于物理“前景”的任何情况下都会正确设置“假”,就像正在显示对话框或拉下通知托盘一样。

示例:

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

答案 19 :(得分: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"));

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

答案 20 :(得分:0)

一种可能的解决方案可能是在显示系统对话框时设置标志,然后在活动生命周期的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();
}

答案 21 :(得分:0)

如果暂停或恢复,请保存标记。如果你被恢复,这意味着你在前台

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

答案 22 :(得分:0)

从这里开始,您已经明确要求一个活动。我在我的 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;
}

答案 23 :(得分:0)

我想提一个修改。 即使您的应用仍然部分可见(可能是系统对话框或分屏),Activity.onPause 也会启动。

也许您希望暂停仍然算作可见,而仅将停止/破坏算作不可见。

他们在您的活动可以重新启动时遇到了问题(我有一个重新启动按钮来应对一些不经常发生的错误)。

即使是同一个活动,它也不会在重新创建之前被销毁,而是像转换到另一个应用程序一样处理:

这不是手头问题的直接答案,但我注意到,如果您只是终止并重新启动 SAME 活动(在我的情况下使用片段中的重新启动按钮),上面显示的生命周期也是如此。至少对于 Android 10 来说是这样。

重启过程也如下所示: MainActivity(旧).onPause MainActivity (new) .onCreate MainActivity (new) .onStart MainActivity (new) .onResume MainActivity(旧).onStop MainActivity(旧).onDestroy

现在,如果您要在 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

答案 24 :(得分:-3)

我以前喜欢这样做,

如果活动不在前台

  

getIntent()

将返回null。 := P