114

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

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

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

红色是我在后台的活动,而对话在前台

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

4

25 回答 25

196

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

正确的解决方案(感谢 Dan、CommonsWare 和 NeTeInStEiN)使用 Activity.onPause、Activity.onResume 方法自行跟踪应用程序的可见性。将“可见性”状态存储在其他类中。好的选择是您自己实现的应用程序或服务(如果您想检查服务的活动可见性,此解决方案也有一些变体)。

示例 实现自定义应用程序类(注意 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()检查活动是否可见。在那里,您还可以检查用户是否选择了一个选项。满足两个条件时继续。

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

资料来源:堆栈溢出

于 2013-08-27T15:25:10.303 回答
73

如果目标 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
    ....
}
于 2014-07-24T13:57:50.830 回答
51

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

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

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

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

于 2019-02-08T09:07:26.407 回答
14

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

这是一个示例类,用于检查您的活动的可见性,无论您身在何处。

请记住,如果您显示一个对话框,结果将是错误的,因为对话框将具有主要焦点。除此之外,它确实比建议的解决方案更方便且更可靠。

于 2018-01-05T11:17:05.667 回答
10

这正是Activity 类文档中描述的ActivityonPause和事件之间的区别。onStop

如果我对您的理解正确,您想要做的是finish()从您的活动中调用onStop以终止它。请参阅Activity Lifecycle Demo App的附图。这是从 Activity A 启动 Activity B 时的样子。事件的顺序是从下到上,因此您可以看到 Activity AonStop在 Activity BonResume已被调用之后被调用。

活动生命周期演示

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

于 2013-08-26T14:52:10.503 回答
8

两种可能的解决方案:

1) 活动生命周期回调

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

当您需要跨活动共享/存储状态时使用应用程序。

2) 检查运行进程信息

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

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

于 2013-08-27T06:33:56.033 回答
4

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

它使用非常简单的逻辑并且适用于所有 android API 级别。

于 2015-08-06T10:07:54.890 回答
3

使用 pause 和 resume from background 之间的时间间隔来确定是否从后台唤醒

在自定义应用程序中

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();
}
于 2014-06-25T03:56:08.803 回答
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()
于 2016-02-26T11:04:24.940 回答
2

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

例如,让 Activity 类名作为ProfileActivity让我们找出它是在前台还是后台

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

哪个实现

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

在上面的类中有一个覆盖方法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")
    }
于 2016-03-08T13:30:13.980 回答
2

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

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

只需创建此类的单例并将其设置在您的应用程序实例中,如下所示:

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

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

于 2018-06-15T17:07:14.317 回答
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
    }
}
于 2017-11-16T18:59:39.533 回答
1

如果您使用的是EventBus,则它作为一个被调用的方法hasSubscriberForEvent,可用于检查 anActivity是否获得焦点。

于 2020-04-29T22:15:14.117 回答
0

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

于 2013-08-20T23:41:41.593 回答
0

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

因此,只需让系统停止您的活动并在您的活动被回调时保存任何需要的东西。

于 2013-08-24T11:59:25.450 回答
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();
  }
}
于 2013-08-26T15:10:58.877 回答
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

    }
};

并在活动开始时:

@Override
protected void onStop() {
    if(checkFlag){
        finish();
    }
    super.onStop();
}
于 2013-08-27T07:15:27.690 回答
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"));

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

于 2013-08-27T12:55:17.527 回答
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);
于 2013-08-27T16:00:53.583 回答
0

Activity.

isDestroyed()

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

isFinishing()

在 Api 1 中添加
检查以查看此活动是否正在完成,或者是因为您在其上调用了 finish() 或者其他人已请求它完成。这通常用于 onPause() 来确定活动是简单地暂停还是完全结束。


来自内存泄漏文档

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

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它很容易比 parent 寿命更长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
    }
  }
}
于 2017-03-15T05:04:18.893 回答
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 的规范名称,则可以确定它是否在前台。这个解决方案可能不是万无一失的。因此,非常欢迎您提出意见。

于 2017-09-29T11:14:54.040 回答
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();

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

于 2017-10-31T22:40:38.657 回答
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;
}
于 2021-03-30T17:35:45.490 回答
0

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

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

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

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

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

重启过程也是这样:MainActivity(旧).onPause MainActivity(新).onCreate MainActivity(新).onStart MainActivity(新).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
于 2021-04-29T10:27:19.540 回答
-4

我以前喜欢,

如果活动不在前台

获取意图()

将返回空值。:=P

于 2014-06-23T14:37:24.140 回答