Session handling in Android

Session Handling, We often come across a use-case where we need to handle a session in the Application. Such use-cases are most common in the Banking domain where the user should be logged off after a defined period(say 5 mins) of inactivity.

To achieve this we need to keep in mind a couple of things:

  1. The session should be expired when the application is foreground but not in use.
  2. The Session should get expired when the application is running but is in the background.

To address the above two conditions, we need to keep track of when the application goes into the background and when it comes in the foreground. Additionally, we also need to take care of every user interaction with the application.

Let’s look into the following piece of code:

public class AppLifeCycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private AppLifeCycleCallback appLifeCycleCallback;

    private boolean appInForeground;

    public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
        this.appLifeCycleCallback = appLifeCycleCallback;
    }

    @Override
    public void onActivityResumed(Activity activity) {
        if (!appInForeground) {
            appInForeground = true;
            appLifeCycleCallback.onAppForeground();
        }
    }

    @Override
    public void onTrimMemory(int i) {
        if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            appInForeground = false;
            appLifeCycleCallback.onAppBackground();
        }
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {

    }

    @Override
    public void onLowMemory() {

    }

    public interface AppLifeCycleCallback {

        void onAppBackground();

        void onAppForeground();
    }

}

By using “Application.ActivityLifecycleCallbacks, ComponentCallbacks2”; we can determine when the application goes into the background and when the application comes in the foreground and accordingly we can make use of this class in our Application class like this:

public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback {
    private static BaseApplication sInstance;
    private boolean isAppInBackground;

    public static BaseApplication getInstance() {
        return sInstance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        sInstance = this;
        AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
        registerActivityLifecycleCallbacks(appLifeCycleHandler);
        registerComponentCallbacks(appLifeCycleHandler);
    }

    @Override
    public void onAppBackground() {
        isAppInBackground = true;
    }


    public boolean isApplicationInBackground() {
        return isAppInBackground;
    }

    @Override

    public void onAppForeground() {
        isAppInBackground = false;
        SessionTimeoutManager.getInstance().resetTimer();
    }
}

The above code addresses our first two points i.e when the application goes into the background and when the application comes in the foreground.

Now, the question arises how will we address the issue of User Interaction with our application. For this android has provided has a callback method “onUserInteraction()” which according to official documentation states “This method is called whenever a key, touch, or trackball event is dispatched to the activity.”

Hence, we override this method in our BaseActivity.

@Override
public void onUserInteraction() {
    SessionTimeoutManager.getInstance().resetTimer();
}

You might be thinking what the hell is this SessionTimeoutManager, so Let’s look into it

public class SessionTimeoutManager {
    private static final long INTERVAL = 1 * 1000;
    private static final long START_TIME = 1 * 60 * 1000; // 1 MINS IDLE TIME
    private static SessionTimeoutManager mSessionTimeoutManager;
    private CountDownTimer timer;
    private WeakReference<BaseActivity> mBaseActivityWeakReference;

    private SessionTimeoutManager() {
        timer = new CustomCountDownTimer(START_TIME, INTERVAL);
        timer = timer.start();
    }

    public static SessionTimeoutManager getInstance() {
        synchronized (SessionTimeoutManager.class) {
            if (mSessionTimeoutManager == null) {
                mSessionTimeoutManager = new SessionTimeoutManager();

            }
        }
        return mSessionTimeoutManager;
    }


    public void resetTimer() {
        if (timer != null) {
            timer.cancel();
            timer = timer.start();
        }
    }

    public void setBaseActivityWeakReference(BaseActivity baseActivityWeakReference) {
        mBaseActivityWeakReference = new WeakReference<>(baseActivityWeakReference);
    }

    private void signOutUser(BaseActivity activity) {
        Utils.logoutUser(activity);
    }

    public class CustomCountDownTimer extends CountDownTimer {
        CustomCountDownTimer(long startTime, long interval) {
            super(startTime, interval);
        }

        @Override
        public void onFinish() {
            logoutUser();
        }

        @Override
        public void onTick(long millisUntilFinished) {
        }

        private void logoutUser() {
            Keystore.getInstance(BaseApplication.getInstance()).putBoolean(Constants.SESSION_EXPIRED, true);
            if (null != mBaseActivityWeakReference) {
                BaseActivity baseActivity = mBaseActivityWeakReference.get();
                if (null != baseActivity) {
                    if (!BaseApplication.getInstance().isApplicationInBackground()) {
                        signOutUser(baseActivity);
                    }
                }
            }
        }
    }

As you can see, the above class has been declared as a Singleton, where the actual logic of how the session is maintained is kept. The class has been created on singleton pattern in order to share the same instance of the class across multiple activities in the project for Session Handling.

You can download the complete project from here.

Please Note: The method shown in the sample-project is one of the ways to handle Session in Android. You can always reach out to me for any queries or for a discussion on any other better approach.