38

I am trying to automatically capture and log Android lifecycle events using ActivityLifecycleCallbacks, however documentation on this matter is scarce, to say the least:

    public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)

I don't want to have to extend the Activity class or override the existing lifecycle methods (onCreate, onResume, etc...) I'm looking to have a separate class listening for these events and acting accordingly.

Does anyone have any experience in this, or have links to good solid documentation or tutorials on how this works? Specifically, how to register for ActivityLifecycleCallbacks, and how to handle them?

AWT
  • 3,657
  • 5
  • 32
  • 60
  • 1
    UPDATE: The standard API has now been around long enough that @ClarkXP's roll-your-own is overkill for most apps. Instead see @Jeroen's answer. Which I summarize/clarify as "In your callback class (that has `implements Application.ActivityLifecycleCallbacks`), in its constructor do `getApplication().registerActivityLifecycleCallbacks(this);` If your class has an `onCreate` or `init` or similar method that runs when the instance becomes active, put that line there rather than in the constructor (because the class presumably does not want to receive callbacks until it is fully ready)." – ToolmakerSteve Oct 08 '15 at 01:37

4 Answers4

59

I don't have any firsthand experience but judging from the API you can just write your own class that implements the Application.ActivityLifecycleCallbacks interface and register that class on the provided Application class instance

getApplicaton().registerActivityLifecycleCallbacks(yourCustomClass);

This class will receive the same callbacks as your individual activities. Good luck.

PS. This is API level 14 btw, so it won't work on older phones.

Daniel Imms
  • 47,944
  • 19
  • 150
  • 166
Jeroen
  • 3,399
  • 1
  • 22
  • 25
  • 1
    @ClarkXP yes but the inheritance link is too strong for the application class. This is not modular enough to be turned into a lib. – Snicolas Aug 21 '13 at 01:22
  • To clarify the `register` line: In your callback class (that has `implements Application.ActivityLifecycleCallbacks`), in its constructor do `getApplication().registerActivityLifecycleCallbacks(this);` If your class has an `onCreate` or `init` or similar method that runs when the instance becomes active, put that line there rather than in the constructor (because the class presumably does not want to receive callbacks until it is fully ready). – ToolmakerSteve Oct 08 '15 at 01:30
  • 7
    This looks perfect to me but I wonder where to call unregisterActivityLifecycleCallbacks () method. Is it not required? – Amit Apr 15 '16 at 09:15
  • @Amit that depends on what is listening for the callbacks and what the lifecycle of that object is. Obviously you should unregister once that object should be disposed of otherwise you would leak it via the application class. – Jeroen Jun 29 '17 at 23:56
  • For `unregisterActivityLifecyleCallbacks` to avoid memory leaking I guess you can do `override fun onActivityDestroyed(activity: Activity?) { AppController.appInstance.registerActivityLifecycleCallbacks(this) }` in `registerActivityLifecycleCallbacks`. But not yet prove it by myself. Sorry this is Kotlin though. – Arst Aug 03 '17 at 00:07
37

I did my own implementation of Application.ActivityLifecycleCallbacks. I'm using SherlockActivity, but for normal Activity class might work.

First, I'm creating an interface that have all methods for track the activities lifecycle:

public interface ActivityLifecycleCallbacks{
    public void onActivityStopped(Activity activity);
    public void onActivityStarted(Activity activity);
    public void onActivitySaveInstanceState(Activity activity, Bundle outState);
    public void onActivityResumed(Activity activity);
    public void onActivityPaused(Activity activity);
    public void onActivityDestroyed(Activity activity);
    public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}

Second, I implemented this interface in my Application's class:

public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{

    @Override
    public void onCreate() {
        super.onCreate();           
    }

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());

    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
    }
}

Third, I'm creating a class that extends from SherlockActivity:

public class MySherlockActivity extends SherlockActivity {

    protected MyApplication nMyApplication;

    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        nMyApplication = (MyApplication) getApplication();
        nMyApplication.onActivityCreated(this, savedInstanceState);
    }

    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        nMyApplication.onActivityResumed(this);
    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        nMyApplication.onActivityPaused(this);
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        nMyApplication.onActivityDestroyed(this);
    }

    @Override
    protected void onStart() {
        super.onStart();
        nMyApplication.onActivityStarted(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        nMyApplication.onActivityStopped(this);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        nMyApplication.onActivitySaveInstanceState(this, outState);
    }   
}

Fourth, all class that extend from SherlockActivity, I replaced for MySherlockActivity:

public class MainActivity extends MySherlockActivity{

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

}

Now, in the logcat you will see the logs programmed in the Interface implementation made in MyApplication.

UPDATE

This implementation was tested from API Level 9 (Gingerbread), API Level 12 (Honeycomb) and API Level 17 (Jelly Bean) and works fine. Might works in Android's older versions.

Tom Taylor
  • 3,344
  • 2
  • 38
  • 63
ClarkXP
  • 1,223
  • 10
  • 23
  • 2
    For completeness, consider adding onActivityRestart() to the interface and the appropriate methods to MyApplication and MySherlockActivity. I realize that this method is _not_ part of Android's ActivityLifecycleCallbacks, but onRestart _is_ part of an Activity's lifecycle. – Jelle Fresen Jul 05 '13 at 09:59
  • 1
    Useful for older devices. NOTE: If you (anyone) are looking to use Application.ActivityLifecycleCallbacks, then you are assuming a recent enough API, and you can omit the custom interface and the Activity-extending. Just do (1) the first code section, which shows how to extend a class (in this case, Application) to implement the interface, but do `implements Application.ActivityLifecycleCallbacks`. And (2) `getApplication().registerActivityLifecycleCallbacks(this);` in your callback class' constructor or onCreate. – ToolmakerSteve Oct 08 '15 at 01:22
5

Try this: http://engineering.meetme.com/2015/04/android-determine-when-app-is-opened-or-closed/#comment-202

It proposes an AppForegroundStateManager to which each activity reports through its onStop() and onStart() functions like this:

@Override
protected void onStart() {
    super.onStart();
    AppForegroundStateManager.getInstance().onActivityVisible(this);
}

@Override
protected void onStop() {
    AppForegroundStateManager.getInstance().onActivityNotVisible(this);
    super.onStop();
}

Your Application class implements a listener like this:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        AppForegroundStateManager.getInstance().addListener(this);
    }

    @Override
    public void onAppForegroundStateChange(AppForegroundStateManager.AppForegroundState newState) {
        if (AppForegroundStateManager.AppForegroundState.IN_FOREGROUND.equals(newState)) {
            // App just entered the foreground. Do something here!
            Log.i(TAG, "App Just Entered the Foreground with launch mechanism of: " + mLaunchMechanism);
        } else {
            // App just entered the background. Set our launch mode back to the default of direct.
            mLaunchMechanism = LaunchMechanism.DIRECT;
        }
    }
}

It also includes tips and tricks for determining how the app was opened - from a notification, a URL opening your app or directly from the Apps menu. This is done through an Enum in the Application class:

public enum LaunchMechanism {
    DIRECT,
    NOTIFICATION,
    URL,
    BACKGROUND
}

private LaunchMechanism mLaunchMechanism = LaunchMechanism.DIRECT;

public void setLaunchMechanism(LaunchMechanism launchMechanism) {
    mLaunchMechanism = launchMechanism;
}

In our implementation of this, we have flags for when we start an activity that will launch a third-party activity, like if the user makes a phone call from our app or if a browser is launched. In the launching activity's onStop() we then do a check like this to only report the activity's not-visibility when those flags are false:

if(!flag_userLaunchedThirdPartyActivity){
     AppForegroundStateManager.getInstance().onActivityNotVisible(this);
     }

For checking whether or not the application goes into the background - for example when the device's screen goes dark or the user receives a phone call - it works like this:

public static boolean isApplicationGoingToBackground(final Context context) {

    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> 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;
}

This solution is not dependent on an API level, so it should work all the way back to API level 1.

marienke
  • 2,465
  • 4
  • 34
  • 66
5
@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(MyApplication.this/*(Your Application Name)*/);
}

Only add this line on Application class and all works well.

Mohamed Nageh
  • 1,963
  • 1
  • 19
  • 27
Anand Raj Mehta
  • 437
  • 5
  • 4