83

I am doing a status bar notification in my android app that is triggered by c2dm. I don't want to display the notification if the app is running. How do you determine if the app is running and is in the foreground?

Jonik
  • 80,077
  • 70
  • 264
  • 372
Andrew Thomas
  • 2,482
  • 3
  • 25
  • 29
  • possible duplicate of [How to detect if any of my activity is front-most and visible to user?](http://stackoverflow.com/questions/3136187/how-to-detect-if-any-of-my-activity-is-front-most-and-visible-to-user) – Daniel DiPaolo Mar 31 '11 at 18:46
  • It is a similar question... although I had tried a flag on onStart/onStop and it didn't work. I still don't get the difference between stop/start and pause/resume. – Andrew Thomas Mar 31 '11 at 20:29
  • 1
    You should use this solution: http://stackoverflow.com/questions/3667022/android-is-application-running-in-background/5862048#5862048 – Informatic0re May 09 '14 at 07:54
  • Since API 16 there is a simpler way using [ActivityManager.getMyMemoryState](http://stackoverflow.com/a/39589149/64605) – Juozas Kontvainis Sep 20 '16 at 08:41
  • Since support library version 26 you simply just have to query ProcessLifecycleOwner whenever you want. Check https://www.stackoverflow.com/a/52678290/6600000 – Keivan Esbati May 07 '19 at 06:12

18 Answers18

112

Alternately, you can check with the ActivityManager what tasks are running by getRunningTasks method. Then check with the first task(task in the foreground) in the returned List of tasks, if it is your task.
Here is the code example:

public Notification buildNotification(String arg0, Map<String, String> arg1) {

    ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> services = activityManager
            .getRunningTasks(Integer.MAX_VALUE);
    boolean isActivityFound = false;

    if (services.get(0).topActivity.getPackageName().toString()
            .equalsIgnoreCase(appContext.getPackageName().toString())) {
        isActivityFound = true;
    }

    if (isActivityFound) {
        return null;
    } else {
        // write your code to build a notification.
        // return the notification you built here
    }

}

And don't forget to add the GET_TASKS permission in the manifest.xml file in order to be able to run getRunningTasks() method in the above code:

<uses-permission android:name="android.permission.GET_TASKS" />

p/s : If agree this way, please to note that this permission now is deprecated.

Huy Tower
  • 7,769
  • 16
  • 61
  • 86
Gadenkan
  • 1,691
  • 2
  • 17
  • 29
  • 24
    If you want to use this code dont forget to add – Lakshmanan Dec 26 '12 at 07:13
  • @VikasGupta sure, you can. – Gadenkan Jul 10 '13 at 18:53
  • What happens if the app is in background but not killed entirely? – AjOnFire Sep 13 '13 at 14:01
  • What do you mean by not killed entirely? – Gadenkan Sep 13 '13 at 22:05
  • Why do you get all the services to only check the service at index 0? – ingh.am Oct 25 '13 at 14:48
  • 13
    Nitpicks: Calling `toString()` on a String returned by `getPackageName()` is redundant. Also, as we're interested only in the first task returned by `getRunningTasks()`, we can pass `1` instead of `Integer.MAX_VALUE`. – Jonik Nov 20 '13 at 13:19
  • 14
    Note: this method is only intended for debugging and presenting task management user interfaces. This should never be used for core logic in an application, such as deciding between different behaviors based on the information found here. Such uses are not supported, and will likely break in the future. For example, if multiple applications can be actively running at the same time, assumptions made about the meaning of the data here for purposes of control flow will be incorrect. – Informatic0re May 09 '14 at 07:38
  • 5
    Unfortunately, getRunningTasks() has been deprecated since Android L(API 20). As of L, this method is no longer available to third party applications: the introduction of document-centric recents means it can leak person information to the caller. For backwards compatibility, it will still return a small subset of its data: at least the caller's own tasks, and possibly some other tasks such as home that are known to not be sensitive. – Sam Lu Jun 29 '14 at 06:08
  • 2
    Presumably if it only shows the caller's tasks, then the first task and top activity will always be yours, meaning this will always return true post-lollipop. – Mike Holler Jan 16 '15 at 16:34
  • 2
    `activityManager .getRunningTasks` has been depreciated from Lollipop, and should not be used for any application building logic. Recommended by the Google itself. So now what should be use? – jitain sharma Mar 09 '15 at 09:12
  • 1
    for Android L i'm using the answer suggested on this post: http://stackoverflow.com/a/28702860/2347363 – Antilope Jun 29 '15 at 14:31
  • 1
    From Android M onward this will not work as per change: https://android.googlesource.com/platform/frameworks/base/+/aaa0fee – 3c71 Aug 09 '15 at 07:54
  • Is this getting for other application too – Bhanu Sharma Sep 22 '16 at 07:53
  • this is deprecated – cesards Nov 25 '16 at 10:44
  • This solution no longer works on newer versions, but since support library version 26 you can simply query ProcessLifecycleOwner whenever you want. Check http://www.stackoverflow.com/a/52678290/6600000 – Keivan Esbati May 07 '19 at 06:11
55

Make a global variable like private boolean mIsInForegroundMode; and assign a false value in onPause() and a true value in onResume().

Sample code:

private boolean mIsInForegroundMode;

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

@Override
protected void onResume() {
    super.onResume();
    mIsInForegroundMode = true;
}

// Some function.
public boolean isInForeground() {
    return mIsInForegroundMode;
}
Wroclai
  • 26,835
  • 7
  • 76
  • 67
  • 2
    @MurVotema: Yeah, it is. But we are free to pass this variable around, e.g to preferences or a database whenever a change takes place. – Wroclai Aug 25 '12 at 21:06
  • 19
    @Shelly But your variable would go True/False/True a lot while switching through activities within the same app. This means you have to have a hysteresis to really detect when your app loses foreground. – Radu Dec 05 '12 at 09:17
  • 10
    This is not the best solution. Check @Gadenkan solution below. – Felipe Lima Aug 12 '13 at 23:52
  • It works for simple scenarios, but if you have an onActivityResult() on your activity things get complicated. I want to run a background service if and only if all the activities of my app are in the background, and this solution above falls short. – Josh Mar 11 '15 at 16:18
  • @Josh 1. when you have onActivityResult(), onResume() is called immediately after that (docu: _You will receive this call immediately before onResume() when your activity is re-starting._). 2. You can create a common base activity that implements the functionality and stored the result in an instance variable of your application. – Simon Warta Oct 06 '16 at 22:53
  • 1
    @Tima If you want to make it available to all activities, you can create a new MyOwnActivity extends AppCompatActivity... Then inside MyOwnActivity, override both onResume() / onPause(), and lastly you **extends** all you app activities from MyOwnActivity instead of AppCompatActivity. Problem solved. ; ) – elliotching Apr 27 '17 at 05:55
  • Although this can be extended to applicaiton level there is no need to implement this yourself, google already added ProcessLifecycleOwner doing the same thing for you using LifecycleObserver. Check https://stackoverflow.com/a/52678450/6600000 – Keivan Esbati Oct 06 '18 at 11:13
46

This is a pretty old post but still quite relevant. The above accepted solution may work but is wrong. As Dianne Hackborn wrote:

These APIs are not there for applications to base their UI flow on, but to do things like show the user the running apps, or a task manager, or such.

Yes there is a list kept in memory for these things. However, it is off in another process, managed by threads running separately from yours, and not something you can count on (a) seeing in time to make the correct decision or (b) have a consistent picture by the time you return. Plus the decision about what the "next" activity to go to is always done at the point where the switch is to happen, and it is not until that exact point (where the activity state is briefly locked down to do the switch) that we actually know for such what the next thing will be.

And the implementation and global behavior here is not guaranteed to remain the same in the future.

The correct solution is to implement : ActivityLifeCycleCallbacks.

This basically needs an Application Class and the handler can be set in there to identify the state of your activities in the app.

Axarydax
  • 16,353
  • 21
  • 92
  • 151
Vinay
  • 505
  • 5
  • 8
  • 7
    Here is an example how to use it http://baroqueworksdev.blogspot.com/2012/12/how-to-use-activitylifecyclecallbacks.html – Kaloyan Roussev Nov 01 '14 at 10:30
  • 2
    I think this is the best solution and will probably won't break in the future. Wonder why it didn't get enough votes. – Rohan Kandwal Mar 24 '15 at 06:20
  • 2
    If you write a example code, you will get more votes because some people skip the answers without example... but it's the best solution... – Saman Salehi Oct 26 '16 at 05:20
  • how is this different than the overriding `onPause` and `onResume` method used in the accepted answer? – Saitama Aug 07 '17 at 05:46
24

As Vinay says, probably the best solution (to support newer android versions, 14+) is to use ActivityLifecycleCallbacks in the Application class implementation.

package com.telcel.contenedor.appdelegate;

import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;

/** Determines global app lifecycle states. 
 * 
 * The following is the reference of activities states:
 * 
 * The <b>visible</b> lifetime of an activity happens between a call to onStart()
 * until a corresponding call to onStop(). During this time the user can see the
 * activity on-screen, though it may not be in the foreground and interacting with 
 * the user. The onStart() and onStop() methods can be called multiple times, as 
 * the activity becomes visible and hidden to the user.
 * 
 * The <b>foreground</b> lifetime of an activity happens between a call to onResume()
 * until a corresponding call to onPause(). During this time the activity is in front
 * of all other activities and interacting with the user. An activity can frequently
 * go between the resumed and paused states -- for example when the device goes to
 * sleep, when an activity result is delivered, when a new intent is delivered -- 
 * so the code in these methods should be fairly lightweight. 
 * 
 * */
public class ApplicationLifecycleManager implements ActivityLifecycleCallbacks {

    /** Manages the state of opened vs closed activities, should be 0 or 1. 
     * It will be 2 if this value is checked between activity B onStart() and
     * activity A onStop().
     * It could be greater if the top activities are not fullscreen or have
     * transparent backgrounds.
     */
    private static int visibleActivityCount = 0;

    /** Manages the state of opened vs closed activities, should be 0 or 1
     * because only one can be in foreground at a time. It will be 2 if this 
     * value is checked between activity B onResume() and activity A onPause().
     */
    private static int foregroundActivityCount = 0;

    /** Returns true if app has foreground */
    public static boolean isAppInForeground(){
        return foregroundActivityCount > 0;
    }

    /** Returns true if any activity of app is visible (or device is sleep when
     * an activity was visible) */
    public static boolean isAppVisible(){
        return visibleActivityCount > 0;
    }

    public void onActivityCreated(Activity activity, Bundle bundle) {
    }

    public void onActivityDestroyed(Activity activity) {
    }

    public void onActivityResumed(Activity activity) {
        foregroundActivityCount ++;
    }

    public void onActivityPaused(Activity activity) {
        foregroundActivityCount --;
    }


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

    public void onActivityStarted(Activity activity) {
        visibleActivityCount ++;
    }

    public void onActivityStopped(Activity activity) {
        visibleActivityCount --;
    }
}

And in Application onCreate() method:

registerActivityLifecycleCallbacks(new ApplicationLifecycleManager());

Then ApplicationLifecycleManager.isAppVisible() or ApplicationLifecycleManager.isAppInForeground() would be used to know the desired state.

htafoya
  • 18,261
  • 11
  • 80
  • 104
20

Since API 16 you can do it like this:

static boolean shouldShowNotification(Context context) {
    RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
    ActivityManager.getMyMemoryState(myProcess);
    if (myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
        return true;

    KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
    // app is in foreground, but if screen is locked show notification anyway
    return km.inKeyguardRestrictedInputMode();
}
Juozas Kontvainis
  • 9,461
  • 6
  • 55
  • 66
15

FYI, if you use Gadenkan solution (which is great!!) don't forget to add

<uses-permission android:name="android.permission.GET_TASKS" />

to the manifest.

bertz94
  • 353
  • 2
  • 3
14

Slightly cleaned up version of Gadenkan's solution. Put it any Activity, or maybe a base class for all your Activities.

protected boolean isRunningInForeground() {
    ActivityManager manager = 
         (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1);
    if (tasks.isEmpty()) {
        return false;
    }
    String topActivityName = tasks.get(0).topActivity.getPackageName();
    return topActivityName.equalsIgnoreCase(getPackageName());
}

To be able to call getRunningTasks(), you need to add this in your AndroidManifest.xml:

<uses-permission android:name="android.permission.GET_TASKS"/>

Do note what ActivityManager.getRunningTasks() Javadoc says though:

Note: this method is only intended for debugging and presenting task management user interfaces. This should never be used for core logic in an application, such as deciding between different behaviors based on the information found here. Such uses are not supported, and will likely break in the future.

Update (Feb 2015)

Note that getRunningTasks() was deprecated in API level 21!

As of LOLLIPOP, this method is no longer available to third party applications: the introduction of document-centric recents means it can leak person information to the caller. For backwards compatibility, it will still return a small subset of its data: at least the caller's own tasks, and possibly some other tasks such as home that are known to not be sensitive.

So what I wrote earlier is even more relevant:

In many cases you can probably come up with a better solution. For example, doing something in onPause() and onResume(), perhaps in a BaseActivity for all your Activities.

(In our case we didn't want an offline alert activity to be launched if we are not in the foreground, so in BaseActivity onPause() we simply unsubscribe from the RxJava Subscription listening for "went offline" signal.)

Community
  • 1
  • 1
Jonik
  • 80,077
  • 70
  • 264
  • 372
  • can you please post the RxJava code that you have used ? I wanna do use RxJava and i dont wanna spend lots of time for learning RxJava at this moment :( – MBH Jan 21 '16 at 20:01
  • @MBH: Using RxJava probably won't be very fruitful without spending some time getting familiar with the basic concepts... :-) In any case, here's a [small example](https://gist.github.com/jonikarppinen/6578deb5c1615e03a6627e18f6c2ac3c). – Jonik May 15 '16 at 17:33
9

Following up on Gadenkan's reply I needed something like this so I could tell if my app wasn't running in the foreground, but I needed something that was app wide and didn't require me setting/unsetting flags throughout my application.

Gadenkan's code pretty much hit the nail on the head but it wasn't in my own style and felt it could be tidier, so in my app its condensed down to this.

if (!context.getPackageName().equalsIgnoreCase(((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1).get(0).topActivity.getPackageName()))
{
// App is not in the foreground
}

(Side note: You can just remove the ! if you want the check to work the other way around)

Although with this approach you need the GET_TASKS permission.

biegleux
  • 13,179
  • 11
  • 45
  • 52
CrosseyeJack
  • 154
  • 1
  • 5
7

Starting support library version 26 you can use ProcessLifecycleOwner to determine app current state, just add it to your dependencies like described here, for example:

dependencies {
    def lifecycle_version = "1.1.1"

    // ViewModel and LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    // alternatively - Lifecycles only (no ViewModel or LiveData).
    //     Support library depends on this lightweight import
    implementation "android.arch.lifecycle:runtime:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}

, Now you can query ProcessLifecycleOwner whenever you want to check app state, for example to check if app is running in foreground you just have to do this:

 boolean isAppInForeground = ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
 if(!isAppInForeground)
    //Show Notification in status bar
Keivan Esbati
  • 3,376
  • 1
  • 22
  • 36
  • 1
    This is the right answer. A lot of the other ones should be considered deprecated – 11m0 Nov 17 '19 at 05:12
  • For AndroidX Java use ```implementation 'androidx.lifecycle:lifecycle-process:2.2.0'``` in your project gradle. – Jon Oct 14 '20 at 22:44
1

Based on the various answers and comments, here is a more inlined version that you can add to a helper class:

public static boolean isAppInForeground(Context context) {
  List<RunningTaskInfo> task =
      ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
          .getRunningTasks(1);
  if (task.isEmpty()) {
    return false;
  }
  return task
      .get(0)
      .topActivity
      .getPackageName()
      .equalsIgnoreCase(context.getPackageName());
}

As mentioned in other answers you need to add the following permission to your AndroidManifest.xml .

<uses-permission android:name="android.permission.GET_TASKS"/>
Pierre-Antoine
  • 7,939
  • 6
  • 28
  • 36
1

I would like to add that a safer way to do this - than checking if your app is in the background before creating a notification - is to just disable and enable the Broadcast Receiver onPause() and onResume() respectively.

This method gives you more control in the actual application logic and is not likely to change in the future.

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

@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION));
}
d4c0d312
  • 748
  • 8
  • 24
1

I found a more simpler and accurate way to check if the application is in foreground or background by mapping the activities to boolean.

Check the complete gist here

mipreamble
  • 1,415
  • 12
  • 17
1

This is useful only when you want to perform some action just when your activity starts and its where you want to check if app is in foreground or background.

Instead of using Activity manager there is a simple trick which you can do through code. If you observe the activity cycle closely, the flow between two activities and foreground to background is as follows. Suppose A and B are two activities.

When transition from A to B: 1. onPause() of A is called 2. onResume() of B is called 3. onStop() of A is called when B is fully resumed

When app goes into background: 1. onPause() of A is called 2. onStop() of A is called

You can detect your background event by simply putting a flag in activity.

Make an abstract activity and extend it from your other activities, so that you wont have to copy paste the code for all other activities wherever you need background event.

In abstract activity create flag isAppInBackground.

In onCreate() method: isAppInBackground = false;

In onPause() method: isAppInBackground = false;

In onStop() method: isAppInBackground = true;

You just to need to check in your onResume() if isAppInBackground is true. n after you check your flag then again set isAppInBackground = false

For transition between two activities since onSTop() of first will always called after second actvity resumes, flag will never be true and when app is in background, onStop() of activity will be called immediately after onPause and hence the flag will be true when you open the app later on.

There is one more scenario though in this approach. If any of your app screen is already open and you put the mobile idle then after some time mobile will go into sleep mode and when you unlock mobile, it will be treated at background event.

Ankita
  • 313
  • 1
  • 8
1

Here's the code for nice simple solution described above by @user2690455 . Although it looks a bit verbose, you'll see overall it's actually quite light-weight

In my case we also use AppCompatActivity, so I had to have 2 base classes.

public class BaseActivity extends Activity {

    /**
     * Let field be set only in base class
     * All callers must use accessors,
     * and then it's not up to them to manage state.
     *
     * Making it static since ..
     * 1. It needs to be used across two base classes
     * 2. It's a singleton state in the app
     */
    private static boolean IS_APP_IN_BACKGROUND = false;

    @Override
    protected void onResume() {
        super.onResume();

        BaseActivity.onResumeAppTracking(this);

        BaseActivity.setAppInBackgroundFalse();
    }

    @Override
    protected void onStop() {
        super.onStop();

        BaseActivity.setAppInBackgroundTrue();
    }

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

        BaseActivity.setAppInBackgroundFalse();
    }

    protected static void onResumeAppTracking(Activity activity) {

        if (BaseActivity.isAppInBackground()) {

            // do requirements for returning app to foreground
        }

    }

    protected static void setAppInBackgroundFalse() {

        IS_APP_IN_BACKGROUND = false;
    }

    protected static void setAppInBackgroundTrue() {

        IS_APP_IN_BACKGROUND = true;
    }

    protected static boolean isAppInBackground() {

        return IS_APP_IN_BACKGROUND;
    }
}
Gene Bo
  • 11,284
  • 8
  • 90
  • 137
1

Here is a method that I use (and supporting method):

private boolean checkIfAppIsRunningInForeground() {
    ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
    for(ActivityManager.RunningAppProcessInfo appProcessInfo : activityManager.getRunningAppProcesses()) {
        if(appProcessInfo.processName.contains(this.getPackageName())) {
            return checkIfAppIsRunningInForegroundByAppImportance(appProcessInfo.importance);
        }
    }
    return false;
}

private boolean checkIfAppIsRunningInForegroundByAppImportance(int appImportance) {
    switch (appImportance) {
        //user is aware of app
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE:
            return true;
        //user is not aware of app
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE:
        default:
            return false;
    }
}
Droid Chris
  • 3,455
  • 28
  • 31
  • looks like I got down voted for solution that was posted two years ago. This method no longer works after various security updates in Android. – Droid Chris Nov 18 '19 at 19:12
0

There is no global callback for this, but for each activity it is onStop(). You don't need to mess with an atomic int. Just have a global int with the number of started activities, in every activity increment it in onStart() and decrement it in onStop().

Follow this

Community
  • 1
  • 1
Kishan Vaghela
  • 7,678
  • 5
  • 42
  • 67
0
     public static boolean isAppRunning(Context context) {

 // check with the first task(task in the foreground)
 // in the returned list of tasks

   ActivityManager activityManager = (ActivityManager)
   context.getSystemService(Context.ACTIVITY_SERVICE);
 List<RunningTaskInfo> services =
 activityManager.getRunningTasks(Integer.MAX_VALUE);
     if
     (services.get(0).topActivity.getPackageName().toString().equalsIgnoreCase(context.getPackageName().toString()))
     {
     return true;
     }
     return false;
     }
Ashish Soni
  • 286
  • 3
  • 4
  • getRunningTasks isn't an option anymore - the newer security model makes this mostly useless, and it's deprecated. – Tony Maro Apr 28 '16 at 19:40
  • @AnaghHegde See the answer above by Gadenkan by pulling the list of running activities from the system. – Tony Maro Dec 05 '16 at 13:09
0

The previous approaches mentioned here are not optimal. The task based approach requires a permission that might not be desired and "Boolean" approach is prone to concurrent modification mess ups.

The approach I use and which (I believe) works quite well in most cases:

Have a "MainApplication" class to track activity count in AtomicInteger:

import android.app.Application;

import java.util.concurrent.atomic.AtomicInteger;

public class MainApplication extends Application {
    static class ActivityCounter {
        private static AtomicInteger ACTIVITY_COUNT = new AtomicInteger(0);

        public static boolean isAppActive() {
            return ACTIVITY_COUNT.get() > 0;
        }

        public static void activityStarted() {
            ACTIVITY_COUNT.incrementAndGet();
        }

        public static void activityStopped() {
            ACTIVITY_COUNT.decrementAndGet();
        }
    }
}

And create a base Activity class that other activities would extend:

import android.app.Activity;
import android.support.annotation.CallSuper;

public class TestActivity extends Activity {
    @Override
    @CallSuper
    protected void onStart() {
        MainApplication.ActivityCounter.activityStarted();
        super.onStart();
    }

    @Override
    @CallSuper
    protected void onStop() {
        MainApplication.ActivityCounter.activityStopped();
        super.onStop();
    }
}
Tadas Šubonis
  • 1,570
  • 2
  • 16
  • 21