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

232,179

Solution 1

This is what is recommended as the right solution:

The right solution (credits go to Dan, CommonsWare and NeTeInStEiN) Track visibility of your application by yourself using Activity.onPause, Activity.onResume methods. Store "visibility" status in some other class. Good choices are your own implementation of the Application or a Service (there are also a few variations of this solution if you'd like to check activity visibility from the service).

Example Implement custom Application class (note the isActivityVisible() static method):

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

Register your application class in AndroidManifest.xml:

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

Add onPause and onResume to every Activity in the project (you may create a common ancestor for your Activities if you'd like to, but if your activity is already extended from MapActivity/ListActivity etc. you still need to write the following by hand):

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

The source also mentions two wrong solutions...so avoid doing that.

Source: stackoverflow

Solution 2

If targeting API level 14 or above, one can use 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
    ....
}

Solution 3

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

In 2019 with help of new support library 28+ or AndroidX you can simply use:

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

You can read more in the documenation to understand what happened under the hood.

Solution 4

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

Here is an example class to check your activites' visibility from wherever you are.

Remember that if you show a dialog, the result will be false since the dialog will have the main focus. Other than that it's really handy and more reliable than suggested solutions.

Solution 5

That's exactly the difference between onPause and onStop events of the activity as described in the Activity class documentation.

If I understand you correctly, what you want to do is call finish() from your activity onStop to terminate it. See the attached image of the Activity Lifecycle Demo App. This is how it looks like when Activity B is launched from Activity A. The order of events is from bottom to top so you can see that Activity A onStop is called after Activity B onResume was already called.

Activity lifecycle demo

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

Share:
232,179

Related videos on Youtube

Nick
Author by

Nick

Web dev is my jam, though I also dabble in Android and iOS native development. Run my own development firm and have been doing web for nearly 10 years now

Updated on April 30, 2021

Comments

  • Nick
    Nick about 3 years

    I have a splash screen on a timer. My problem is that before I finish() my activity I need to check that the next activity has started because a system dialogue box pops-up and I only want to finish(); once the user has selected an option from the dialogue box?

    I know that there are many questions on how to see if your activity is in the foreground but I do not know if this allows for dialogue boxes on top of the activity too.

    Here is the problem, the red is my activity which is in the background while the dialogue is in the foreground:

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

    EDIT: I have tried just not using finish() but then my activity can be gone back to in the stack of applications which I am trying to avoid.

    • jarmod
      jarmod over 10 years
    • alanv
      alanv over 10 years
      To clarify, you want to launch an intent chooser and wait for your app to finish() until after the user has tapped one of the choices? It sounds like you need Intent.createChooser() and startActivityForResult() followed by finish() when the result is received.
    • Douglas Nassif Roma Junior
      Douglas Nassif Roma Junior about 9 years
    • S.R
      S.R about 5 years
      ProcessLifecycleOwner is the newest solution
    • Nick
      Nick over 2 years
      @AlexMisiulia No, I'll let the votes do the talking - if your answer gets more votes I'll be happy to change the accepted answer.
    • Alex Misiulia
      Alex Misiulia over 2 years
      @Nick, I got your point. But the problem is that accepted answer is buggy as mentioned in comment to the answer and will work incorrectly for some cases. And more people will make mistakes and loose their time. But anyway, this is your choice)
  • sagus_helgy
    sagus_helgy almost 10 years
    There is one little moment between finish and start activity and i thin need to add some delay and counter
  • Lassi Kinnunen
    Lassi Kinnunen about 9 years
    "buu this is not the android" way answers are tiresome and don't answer the question posed in the first place. furthermore, there are valid reasons to finish(); - for example it's conceivable that going back to it once the action has taken hold serves no purpose whatsoever. in other words, do you think they put finish() in there for fun? it staying on the stack is exactly what the question asker wanted to avoid
  • Joris Weimar
    Joris Weimar about 9 years
    This doesn't work reliably. You might have the following situation: Resume A Resume B Pause A. Now activityVisible is false whereas the application is visible. Perhaps you use a visibility counter: visibleCounter ++ in onResume and visibleCounter -- in onPause.
  • Admin
    Admin almost 9 years
    Agreed with Joris Weimar that this is not a foolproof solution. One scenario is if the user pulled down the notification panel, then neither the onPause, onStop, nor the onResume event is called. So what do you do then if none of these events are fired?!
  • Admin
    Admin almost 9 years
    In fact, none of the other answers work 100% either.
  • Alexander Farber
    Alexander Farber over 8 years
    For better security ad efficiency use LocalBroadcastManager here
  • Daniel Wilson
    Daniel Wilson over 8 years
    You could just do this in the regular activity lifecycle callbacks (onResume(), onStop()) too I would say.
  • ruX
    ruX about 8 years
    If app has more than one Activity this scheme won't work. Replace with counters at least
  • winklerrr
    winklerrr about 7 years
    What if I need my Activities to extend the AppCombatActivity?
  • David Berry
    David Berry almost 7 years
    This also doesn't work in the Android N split view case. One or the other of the activities will be paused, but visible.
  • Burak Day
    Burak Day over 6 years
    Case: As a library, you want to unregister your sensorlistener when the activity of a project, which imported you, is on background. Solution: Not approved since you can't control activities.
  • Nick
    Nick over 6 years
    Thank you for amending the answer @Burak Day, it is actually an answer now
  • Chandler
    Chandler over 5 years
    This does not work, I would rather use a boolean property in the class, set to true in OnResume, and set to false in OnPause();
  • Burak Day
    Burak Day over 5 years
    @Chandler what's the exact issue you are having with this code? Also which version?
  • Burak Day
    Burak Day over 5 years
    @Chandler also, what if you don't have access to activity lifecycle methods. Consider you are just checking activity's visibility from a library.
  • Chandler
    Chandler over 5 years
    The real problem of this answer is it does NOT work, activity.hasWindowFocus is true can not guarantee the activity is between onResume and onPause state. I would rather recommend add an bool isResumed property in that activity, manually set the value and add a get method.
  • Jeffrey Blattman
    Jeffrey Blattman about 5 years
    @DanielWilson I think the point it is not to build a system for doing something where one already exists. IMHO this should be the accepted answer.
  • htafoya
    htafoya almost 5 years
    Not really, probably it is better to place activity.lifecycle.currentState.isAtLeast(Lifecycle.State.RE‌​SUMED) or STARTED. INITIALIZED doesn't guarantee that it is in foreground.
  • Owen Zhao
    Owen Zhao over 3 years
    "buu this is not the android" way answers are tiresome and don't answer the question posed in the first place. Your commend was unfair. Although I pointed it out this was not Android way, I gave the answer after this sentence instead of nothing. I just didn't give the answer in code as that was unnecessary. So it was unfair saying I didn't answer the question in the first place.
  • Tyler
    Tyler over 3 years
    This is great for checking if any of our activities are open. Far better than writing code in each one and risking missing something. Reliable and simple. Thanks!
  • Cactusroot
    Cactusroot almost 3 years
    @Chandler onResule is called slightly BEFORE the activity is turned visible.
  • Cactusroot
    Cactusroot almost 3 years
    This answer should be the accepted one. onResume() is called before the activity is visible.
  • Akito
    Akito over 2 years
    Project does not seem to be maintained, anymore.
  • Kris B
    Kris B over 2 years
    I don't think there is much to maintain since it does one specific thing. It still works though.
  • Raphael C
    Raphael C about 2 years
    I think this is a nice solution, although why the need to keep a set of the activity classes? why not simply use a counter and increase/decrease it on resume/paused, and then check if == 0?