Browsing Category

Open Source

Detecting Android App Starts and Stops

By: Sky Kelsey


tl;dr

Tracking app uses on Android is a pain because the Android SDK doesn’t provide hooks into the application lifecycle. We’ve fixed that. We created a class you can use to accomplish this. You can get it off github at apptentive/ApplicationSessionActivity. Integration is an easy 3 steps.

An Android Shortcoming

As most Android developers have, or will come to realize, there are many desired features that the Android SDK simply does not provide to the developer. The stumbling block we recently came across was how to track Application uses.

The use case seems obvious: You want to run some code when the user starts or stops using your app. The Android SDK doesn’t provide an easy way to do that. A developer can tell when the application is launched from a dead stop by hooking into the main Activity’s onCreate() method. They can also tell when an Activity is put into the background by hooking into the onStop() method. But for apps with multiple Activities, you can’t distinguish an Activity stopping because the app is going into the background, or simply because another Activity within the app is taking focus.

Devising a Solution

I first tried to look at the Application class, to see if it had any useful methods for tracking the application lifecycle. Unfortunately, it doesn’t.

Then, I ran across the ignition framework, which provides a method for detecting when an application is going to the background. It does this by checking the package name of the Activity that is about to start, and comparing it to the package name defined in the application’s AndroidManifest.xml. If they differ, then the app is in fact going to the background.

I soon realized that this was a flawed approach. If the app is integrating a third party SDK, such as Apptentive, then not all of the Activities included in the app are guaranteed to have the same package name. So I made a tweak, which compares the starting Activity name to the list of Activities defined in the manifest. If they match, then the app is not going to the background, else it is. Called from an Activity’s onStop() method, I can now tell if the Activity is giving way to another Activity defined in the app, or the app is stopping.

How we know when the app goes to the background

private static boolean isApplicationBroughtToBackground(final Activity activity) {
  ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
  List tasks = null;
  try {
    tasks = activityManager.getRunningTasks(1);
  } catch (SecurityException e) {
    Log.e(TAG, "Missing required permission: \"android.permission.GET_TASKS\".", e);
    return false;
  }
  if (tasks != null && !tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      Log.e(TAG, "Package name not found: " + activity.getPackageName());
      return false; // Never happens.
    }
  }
  return true;
}

Introducing ApplicationSessionActivity

However, that only solved part of the problem.

    • An Android application can have an arbitrary number of Activities defined in it. I therefore need to check whether the app is going to background from each Activity.
    • I need to keep track of when the app is in the background so that I know a new use occurs when an Activity starts.
    • I need to know whether the Activity we are leaving is the main Activity or one of the child Activities.

It turned out that this was a little verbose in code, so I decided to create a new class, ApplicationSessionActivity, that developers can use to accomplish this goal. The integration is very simple:

1. Add the GET_TASKS permission to your AndroidManifest.xml.

<!--?xml version="1.0" encoding="utf-8"?-->
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.apptentive.android.example"
          android:versionCode="1"
          android:versionName="1.0">
    <uses-sdk android:minSdkVersion="7"/>
    <uses-permission android:name="android.permission.GET_TASKS"/>
    <application android:label="Session Example">
        <activity android:name=".MainExampleActivityApplication"
                  android:label="Session Example">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity android:name=".ChildExampleActivityApplication"/>
    </application>
</manifest>

2. Make all of your Activities inherit from SessionLifecycleActivity.

import com.apptentive.android.sessionlifecycle.SessionLifecycleActivity;

public class MainExampleActivity extends SessionLifecycleActivity {

  @Override
  public void onCreate(Bundle savedInstanceState) {

3. Create listeners to get session start and stop notifications.

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  // Pass a listener in so we can be notified of session starts.
  setOnSessionStartedListener(new SessionStartedListener() {
    public void onSessionStarted() {
      Log.e("SessionExample", "Starting session.");
    }
  });

  // Pass a listener in so we can be notified of session stops.
  setOnSessionStoppedListener(new SessionStoppedListener() {
    public void onSessionStopped() {
      Log.e("SessionExample", "Stopping session.");
    }
  });
}

The listeners will now be called when the app leaves the foreground, or comes back.

The code is available in a public git repo apptentive/ApplicationSessionActivity, and is free to use in your apps. Please let me know if you have any suggestions or feedback, and share with your fellow Android developers if you’ve found this to be useful.

[Note: Keep in mind that you may get weird results if you use an IDE to launch your app. The IDE launches the app differently than touching the app's icon on your launcher screen. A good practice is to always exit the app (press the back button) after launching from an IDE, and then start it up again and go about your testing.]

Improve Customer Retention for Mobile Apps

Announcing PrefixedJSONKit

By: Andrew Wooster

tl;dr

We’re happy to announce the release of PrefixedJSONKit, a version of JSONKit which makes it easier to use JSONKit in libraries without worrying about conflicting with other libraries or the apps which use them.

This library works by prefixing the symbols in JSONKit at build time to avoid conflicts with other users of JSONKit or PrefixedJSONKit.

The Full Story

When we set out to make the ApptentiveConnect library for iOS, we settled on JSON for our client-server communication, and JSONKit as our library for handling that on iOS and OS X. JSONKit is a small, high performance library which was easy to integrate into our library.

One problem on iOS, however, is that all the symbols from the app and the libraries the app uses live in the same namespace. Any conflicts in that namespace cause an error when you try to build the app. So, for example, if an app has a class named “Cheeseburger” and a library the app is using has a class named “Cheeseburger”, the app won’t build. For that reason, developers add prefixes to their code to avoid conflicts. The prefix we use at Apptentive is “AT”, so in our library a class might be named “ATCheeseburger”, which wouldn’t conflict with the app’s use of “Cheeseburger”.

The one exception to our prefixing so far has been our use of JSONKit, which uses the “JSON” and “JK” prefixes. Over the last year, more apps and libraries have started using JSONKit, and we’ve started seeing more conflicts between their use and ours. This is a problem for us because it makes it harder for developers to integrate ApptentiveConnect into their apps, and forces a decision on them over whether to use our version of JSONKit, modify our library to use their JSONKit, or modify both to accommodate another library’s use of JSONKit.

For example, we have a customer who uses RestKit in their app. Trying to link both RestKit and ApptentiveConnect in their app results in a build error:

Build Error

Our solution, available now, is PrefixedJSONKit. We are maintaining a fork of JSONKit which prefixes the symbols exported by JSONKit. So, in ApptentiveConnect, we’ll be using the “AT” prefix for JSONKit symbols, as well. For example, the JSONDecoder symbol becomes ATJSONDecoder in our library, and will no longer conflict with an app or another library’s use of JSONKit. Another library, such as RestKit, could choose to start using PrefixedJSONKit with the “RK” prefix, making their use of the JSONDecoder symbol become RKJSONDecoder. This makes it much easier for multiple libraries to play nicely with each other.

To use PrefixedJSONKit, first add it to your project in Xcode. Then, define your prefix as a preprocessor macro for JSONKIT_PREFIX, as seen below:

PrefixedJSONKit build settings

So where before instantiating a JSON decoder object would be:

JSONDecoder *decoder = [JSONDecoder decoder];

it would now be:

ATJSONDecoder *decoder = [ATJSONDecoder decoder];

Category methods are also prefixed, so the -JSONStringWithOptions:error: category method on NSDictionary would become -ATJSONStringWithOptions:error:. Easy!

In our example above, not only does RestKit now build nicely with ApptentiveConnect, another library could use PrefixJSONKit and have it play nicely with both, as well:

Working Build Configuration

PrefixedJSONKit is available now on GitHub. We hope you find it as useful as we have!