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


104

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

我知道关于如何查看您的活动是否在前台有很多问题,但是我不知道这是否也允许在活动顶部显示对话框。

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

红色是我的活动,背景是背景,而对话是前景

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



要澄清一下,您要启动一个意图选择器并等待您的应用完成(直到用户点击其中一个选择)?听起来您需要Intent.createChooser()和startActivityForResult(),然后在接收到结果时需要finish()。
alanv


ProcessLifecycleOwner是最新的解决方案
SR,

Answers:


189

建议将以下建议作为正确的解决方案:

正确的解决方案(贷记给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(如果您愿意,可以为您的Activity创建一个共同的祖先,但是如果您的Activity已经从MapActivity / ListActivity等扩展了,您仍然需要手工编写以下内容) :

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

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

在您的finish()方法中,您想用来isActivityVisible()检查活动是否可见。您还可以在此处检查用户是否选择了一个选项。满足两个条件时继续。

消息人士还提到了两个错误的解决方案...因此请避免这样做。

资料来源:stackoverflow


在结束和开始活动之间只有一小会儿,我需要增加一些延迟和反击
sagus_helgy 2014年

28
这不能可靠地工作。您可能会遇到以下情况:恢复A恢复B暂停A。现在activityVisible为false,而应用程序可见。也许您使用了可见性计数器:onResume中的visibleCounter ++和onPause中的visibleCounter。
Joris Weimar 2015年

4
Joris Weimar达成协议,这不是万无一失的解决方案。一种情况是,如果用户下拉通知面板,然后既没有onPauseonStop也不该onResume事件被调用。那么,如果这些事件均未触发,您该怎么办?

1
实际上,其他任何答案都不是100%有效的。

2
如果应用程序有多个“活动”,则此方案将不起作用。至少用柜台取代
ruX

70

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

18
您也可以在常规活动生命周期回调(onResume(),onStop())中执行此操作。
丹尼尔·威尔逊

5
@DanielWilson我认为关键不是要构建一个已经存在的系统来做某事。恕我直言,这应该是公认的答案。
Jeffrey Blattman

26

UPD:已更新为状态Lifecycle.State.RESUMED。感谢@htafoya

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

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

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


2
并非如此,可能最好放置activity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED) 或开始。 INITIALIZED不保证它处于前台。
htafoya

11

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


1
感谢您修改@Burak Day的答案,这实际上是现在的答案
Nick

这是行不通的,我宁愿在类中使用布尔值属性,在OnResume中将其设置为true,在OnPause()中将其设置为false。
钱德勒

@Chandler您对这段代码有什么确切的疑问?还有哪个版本?
布拉克节,

@Chandler,如果您无法访问活动生命周期方法,该怎么办。考虑到您只是从库中检查活动的可见性。
布拉克节,

这个答案的真正问题是它不起作用,activity.hasWindowFocus为true不能保证活动在onResume和onPause状态之间。我宁愿建议在该活动中添加一个bool isResumed属性,手动设置值并添加get方法。
钱德勒

10

正如Activity类文档中所述,这恰好是ActivityonPauseonStop事件之间的区别。

如果我对您的理解正确,那么您想要做的就是finish()从活动中致电onStop以终止该活动。请参阅活动生命周期演示应用程序的附件图像。这是从活动A启动活动B时的样子。事件的顺序是从下到上,因此您可以看到在已经调用onStop活动B之后调用了活动A。onResume

活动生命周期演示

如果显示对话框,则您的活动在后台变暗并且仅onPause被调用。


7

两种可能的解决方案:

1)活动生命周期回调

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

需要共享/存储各个活动的状态时,请使用“ 应用程序”

2)检查运行过程信息

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

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



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

3

我想我有更好的解决方案。因为您可以简单地构建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()

如果我需要我的活动来扩展AppCombatActivity怎么办?
winklerrr

2

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

例如,让Activity类名称为ProfileActivity,让其查找在前景还是背景中

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

实施

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

在上面的类有一个覆盖methord onActivityResumedActivityLifecycleCallbacks

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

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()方法!


0

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


0

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

因此,只要让系统停止您的活动并在调用活动时保存一切即可。


我真的不知道为什么这被降低了。至少要发表评论,因为我想要的是原因。
Owen Zhao

3
“ buu这不是机器人”的答案令人厌烦,不回答最初提出的问题。此外,有正当的理由需要finish();。-例如,可以想象一旦采取行动后回到原先的目的毫无用处。换句话说,您认为他们在其中放置了finish()吗?它停留在堆栈上正是发问者想要避免的问题
Lassi Kinnunen 2015年

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

0

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

例如,如果系统对话框是由某些buttonclick触发的,则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();
}

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这里
亚历山大·法伯

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

Activity。内使用这些方法。

isDestroyed()

在Api 17
添加如果对Activity进行了最后的onDestroy()调用,则返回true,因此该实例现在已失效。

isFinishing()

在Api 1中添加。
检查此活动是否正在完成中,这是因为您在此活动上调用了finish()还是其他人已要求完成该活动。通常在onPause()中使用它来确定活动是只是暂停还是完全结束。


内存泄漏文档

一个常见的错误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
    }
  }
}

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

如果您对必需的活动具有引用,或者使用该活动的规范名称,则可以查明该活动是否在前台。此解决方案可能并非万无一失。因此,欢迎您提出意见。


0

我不知道为什么没有人谈论活动A的sharedPreferences,设置这样的SharedPreference(例如在onPause()中):

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

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


0

Activity.onWindowFocusChanged(boolean hasFocus)是有用的吗?那个,加上一个类级别的标志,像isFocused这样onWindowFocusChanged设置,将是一种很容易的方法,可以在活动的任何时候告诉活动是否集中。通过阅读文档,在活动未直接位于物理“前景”中的任何情况下(例如正在显示对话框或下拉通知栏),它似乎都可以正确设置为“ 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
    }
}


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.