Android green figure, next to its original packaging

Android: Determine when App is Opened or Closed

The Problem

Inevitably when developing an Android application there will come a time when you need to detect when it comes into the foreground, and when the user leaves.  Surprisingly, there is no easy way to do this! It isn’t hard to detect when the application is first opened, but its not so simple to determine when it is re-opened or closed.

This post will lay out a technique that can be used to detect when an application has been opened, re-opened, or closed.

Lets Get Started

At the core of determining if an application is opened or closed is if any of its activities are currently being displayed. So lets take a very simple example; an application that has exactly one Activity, and does not support landscape mode. All it would take to determine when the application opened or closed would be the Activity’s onStart and onStop methods.

The problem is this approach quickly breaks down as soon as you add landscape support.  If we rotate the device then this Activity will be re-created and onStart will run a second time resulting in incorrectly detecting the app was opened a second time.

In order to handle device rotation we need to add a validation step. This validation needs to launch a timer whenever the Activity stops in order to detect if we quickly see another Activity in the app start. If we do not, then the user has really exited the app, if we do then we know that the user is still within the app.

This validation also allows supporting an application with multiple Activities. Since transitioning from one Activity to another is handled by this validation as well.

So using this technique I created a manager class that all Activities report to when they become visible and are no longer visible. This manager class handles the validation step for each Activity to avoid accidental detection. It also utilizes a publisher-subscriber (Observer) pattern to allow any interested parties to be notified when the application opens or closes.

To use this manager there are three steps:

1) Add it to your codebase

2) Activities Must Notify of Visibility Changes

Have all of your Activities implement the following code to notify the manager when they become visible and when they are no longer visible. This is best achieved by adding this code to your base Activity class.

3) Subscribe to Be Notified of Foreground Changes

Subscribe to be notified of app foreground state changes from somewhere of interest. The application class onCreate method is a prime placement to ensure that you get notified every time the app enters or leaves the foreground.

Further thought

There are a few details that need further discussion.  These changes may need to be made in order to calibrate this for your particular application.

Validation Time

How long should this validation timer be that is used to detect that your app has really entered the background? In the code above it set to 30 seconds for a few reasons.

There are third party Activities that can appear full screen when the application is running. A few common examples of these are in Google In App Purchasing or Facebook signup. Both of these essentially send the application into the background since none of you applications Activities are in the foreground when those are displaying. It is not desirable for those to count as the user “leaving the app” since they really haven’t. The 30 second timeout helps to catch this case. For example if a user completes an in app purchase in under 30 seconds, which the vast majority of users will, then they will not be counted accidentally as “leaving the app”.

If this situation doesn’t apply to you, then I recommend setting your validation time to around four seconds. That is enough of a safety margin for slow devices to create the next Activity when rotating.

CPU Sleep

A potential issue is there is no guarantee that the CPU will still be running long enough for the background detection to occur if the user locks their phone quickly after exiting the app (or locks the phone with the app still open).  In order to guarantee this always works as expected you would need to take a wake lock until the app closed event can be validated to prevent the CPU from sleeping. In practice however this did not seem to be a big enough issue to warrant use of a wake lock.

Determining How The App Was Launched

Great so now we know how to detect when the application was opened or closed, but we don’t know how the application was opened. Did the user click on a notification? Did they click on a link? Or did they just launch the app from the icon or recents tray?

Tracking Launch Mechanism

First we need somewhere to track how the application was opened. In this code I have added an enum to the application class to track how the application was opened. This builds upon the last example so we can print a log when the application is opened, along with how the application was opened.

Setting the Launch Mechanism

Great so now we can print off the launch mechanism when the app is opened, but we never actually set it! So the next step is to set it when the user opens the app via a link or a notification. If its not set to one of those two, then the user must have opened the app directly.

Tracking Link Clicks

To track the user clicking a link that caused the app to open find the place in your code where you intercept your links and place the following code to track the launch mechanism. Make sure this is called before your Activity’s onStart(). This could be needed in many places or just one common link intercepter depending on your architecture.

Tracking Notifications

Unfortunately tracking notifications is a bit trickier. Notifications are shown and when clicked a PendingIntent that you previously bound to them is opened. The trick here is to add a flag to all PendingIntents that you make for a notification that indicates that this Intent is an Intent from a Notification. In other words, when the intent eventually opens some Activity, we need to be able to detect that this intent was created from a Notification.

So for example when creating any PendingIntent for a notification add the following to each intent:

Now all we need to do is check for that flag in every single Activity (add to your base Activity class). If we detect the flag then we know this Activity was launched via a notification and we can set the launch mechanism to notification. This must be done in onCreate so that it is set prior to the application coming into the foreground (which will trigger the printing of the launch mechanism).

And there you have it! You can now detect not only when the app is opened or closed, but how it was opened as well.

Interesting?

Do you find this sort of work interesting? Do you love quaint Bucks County Pennsylvania? Are you an amazing Android Developer? Then we would love to hear from you!

Featured image by Dsimic – license via CC BY-SA 3.0

14 thoughts on “Android: Determine when App is Opened or Closed”

    1. This is a good solution but has a few limitations that the presented solution does not. It requires a minimum API level of 11, and the presented solution goes all the way down to API level 1. Also it does not have any sort of grace period for when the user leaves your app for something like in app purchase.

      The last shortfall I see is it does not have an easy way to register for a notification when the app enters or leaves the foreground. It would be very helpful to be able to register to receive notifications of open/closed in multiple places in the app.

      If your API level is 11 or greater then this may be a direction worth pursuing because it simplifies the implementation a bit. But for me its lacking some of the required features that I need. I would recommend taking some of the pub/sub and debounce code from here and adding it to that to make an ideal API 11+ solution.

      1. His solution could easily be modified for the grace period, I think its preferable so long as you don’t want to support pre-API 11 for the simple reason you don’t have the chance of forgetting to add it to an activity.

        1. I think it would be much easier to use the existing implementation as just notify it using the ActivityLifecycleCallbacks technique. The AppForegroundStateManager can be notified via any technique you want when an Activity enters or leaves the foreground. So if you want to avoid having those calls in your base activity I would implement the ActivityLifecycleCallbacks, without the rotation handling, and just call into AppForegroundStateManager during the correct events.

          @Override public void onActivityStarted(Activity activity) {
          AppForegroundStateManager.getInstance().onActivityVisible(this);
          }
           
          @Override public void onActivityStopped(Activity activity) {
          AppForegroundStateManager.getInstance().onActivityNotVisible(this);
          }

          But to me this is more work then just calling from your base classes (assuming you already have one). If its in a base Activity class that all your Activities extend then there really ins’t any risk of forgetting to call it.

  1. Hey,

    Can’t you use a class that implements “Application.ActivityLifecycleCallbacks” and then maintain a counter which you increase by 1 when “onActivityStarted” is called and decrement by 1 when “onActivityStopped” is called?

  2. This can be easily achieved by implementing “Application.ActivityLifecycleCallbacks” as highlighted by John and Ido

  3. What if the user is getting a phone call while using the app and then go back using the app once the phone call is finished? It would probably be counted as two app starts.

    1. Try using this to detect whether or not the app is going into the background:

      public static boolean isApplicationGoingToBackground(final Context context) {

      ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
      List tasks = am.getRunningTasks(1);
      if (!tasks.isEmpty()) {
      ComponentName topActivity = tasks.get(0).topActivity;
      if (!topActivity.getPackageName().equals(context.getPackageName())) {
      setLaunchMechanism(LaunchMechanism.BACKGROUND);
      return true;
      }
      }

      setLaunchMechanism(LaunchMechanism.DIRECT);
      return false;
      }

  4. I find your method of validating Activity exits using a timer kinda inefficient. Why not simply use isChangingConfigurations() to determine if the device is changing its orientation?

    1. The main reason I did not use isChangingConfigurations is because it is only available in API level 11 and I need a solution that works on older devices. The secondary reason for the timers is to prevent for instance a user doing an in App Purchase to count as them “leaving the app”.

      1. I’d say that covering 94% of the devices (>API 11) with a solid API is always a better solution that using timers to extend it for the rest 6%. 🙂

        Also, “The secondary reason for the timers is to prevent for instance a user doing an in App Purchase to count as them “leaving the app””

        I’m sorry if I misunderstood you, but isChangingConfigurations() only returns true when an Activity is being destroyed due to an orientation change. Making an in-app purchase shouldn’t trigger this flag.

Leave a Reply

Your email address will not be published. Required fields are marked *