2

How can I calculate session length of a user in android so that I can push ads at the right time? Is there a library that can help me with this. I am using google analytics to get average session length but how do I get that of individual users?

Blaze Mathew
  • 185
  • 9
  • You can achieve this behaviour by setting a CountDownTimer on application launch. Just a thought of an work-around. – Reaz Murshed Mar 27 '16 at 07:01

3 Answers3

4

I've found the same problem and done some research on the subject. My conclussions are:

Defining what a user session is is a quite complex matter.

  • "Is the (third party) email app you open to write the feedback part of your application and your session?". The same applies to any share app or any player.

  • When an user switch back to another app to check whatever on other app and comes back, is that part of your app sessions. Notice I'm not talking about the previous case, I mean the user uses the menu/desktop/applist button to switch.

  • When the device enters sleep mode after a while due inactivity, should you session timer continue to count? When the users wake his device your app will be still be on foreground.

I've also found some Android specific issues:

  • Android do not have a native concept of application-session length. That is, how many time the user spent on the app on foreground.

  • An Android Application is actually a series of screen (Activities).

  • Android have an Application class, but it's not on dev hands how and when it's created/killed. It's a OS task. Your app could be on the background but your Application class could be still be on memory.

  • Some methods are not reliable to count on them to be called, like Activity.OnStop (see http://developer.android.com/intl/es/reference/android/app/Activity.html#onStop())

So my final solution was:

Add a timer on Application (in fact a simple long to keed a System.currentTimeMillis () Add an "open" Activities counter, initialized to 0 on Application.onCreate()

Add an onActivityResumed() method to Application. I should increment the counter and initialize the timer if the previous counter value was 0. Add to all Activities onResume() a call to Application.onActivityResumed()

Add an onActivityPaused() method to Application. It decrements the counter and fires the timeout tasks (defined below) Add a call to Application.onActivityPaused() on all Activities.onPause()

When the counter reaches 0 we can not take it for granted the session has finished, we could be just opening another Activity from our own app. I'm using here a TimeTask to check again, on let's say, 5 seconds later, and if the counter is still at 0 we can take for granted the session has finished. We should also cancel this timer (if any) on each onActivityResumed() call.

I encapsulated this behaviour on one class:

import java.util.Timer;
import java.util.TimerTask;

import android.util.Log;

public class UserSessionHelper {
    private static final String TAG = UserSessionHelper.class.getSimpleName();

    private static final boolean DEBUG = true;

    private static final int TIMEOUT_SECONDS = 5;

    public static interface OnUserSessionEnd {
        public void onSessionEnd(long sessionDurationinMillis);
    }

    protected OnUserSessionEnd onSessionEnd;

    protected long sessionStart = 0;
    protected int resumedActivities = 0;

    protected Timer timer = null;

    public UserSessionHelper(OnUserSessionEnd onSessionEnd) {
        this.onSessionEnd = onSessionEnd;
    }

    class EndSessionTask extends TimerTask {
        public void run() {
            if (DEBUG) {
                Log.d(TAG, "Session ended!");
            }

            timer.cancel();
            timer = null;

            long duration = System.currentTimeMillis() - sessionStart - TIMEOUT_SECONDS*1000;
            sessionStart = 0;

            onSessionEnd.onSessionEnd(duration);

        }
    }
    public void clearTimeout() {
        if (DEBUG) {
            Log.d(TAG, "Cancelling the session ending timer!");
        }

        if (timer != null) {
            timer.cancel();
            timer.purge();
            timer = null;
        }
    }

    public void setTimeout(int seconds) {
        timer = new Timer();
        timer.schedule(new EndSessionTask(), seconds * 1000);
    }

    public void onActivityResumed() {
        if (DEBUG) {
            Log.d(TAG, "Activity comes to foreground! " + resumedActivities);
        }       

        clearTimeout();

        if (sessionStart == 0) {
            sessionStart = System.currentTimeMillis();
        }
        resumedActivities++;

    }

    public void onActivityPaused() {
        if (DEBUG) {
            Log.d(TAG, "Activity goes to background! " + resumedActivities);
        }       

        resumedActivities--;

        if (resumedActivities == 0) {
            setTimeout(TIMEOUT_SECONDS);
        }
    }
}

And used it from my custom Applicaction class:

protected UserSessionHelper.OnUserSessionEnd mUserSessionEnded = new UserSessionHelper.OnUserSessionEnd() {

    @Override
    public void onSessionEnd(long sessionDurationinMillis) {
        if (DEBUG) {
            Log.d(TAG, "Session ended in " + (sessionDurationinMillis / 1000) + " seconds.");
        }
    }
};

public void onActivityResumed() {
    sessionHelper.onActivityResumed();
}

public void onActivityPaused() {
    sessionHelper.onActivityPaused();
}

protected UserSessionHelper sessionHelper = new UserSessionHelper(mUserSessionEnded);

This doesn't resolve the problem in the cases we call or switch to a third party app for a while and come back (this will appear as two sessions). Any clue on this one?

rgrocha
  • 1,461
  • 10
  • 19
0

Using Kotlin and Android Architecture Components:

//In application class
ProcessLifecycleOwner.get().getLifecycle().addObserver(appLifecycleObserver);

class AppLifecycleObserver @Inject constructor(val appConfig: ApplicationConfig) : LifecycleObserver {

  private var startTime: Long? = null

  @OnLifecycleEvent(Lifecycle.Event.ON_START)
  fun onEnterForeground() {
    startTime = System.currentTimeMillis()
  }

  @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
  private fun onEnterBackground() {
    if (startTime != null && appConfig.getBoolean(MPARTICLE_ENABLED)) {
      //Session duration
      val sessionLength = System.currentTimeMillis() - startTime!! 
    }
  }
}
Amjed Baig
  • 410
  • 5
  • 9
-1

You take two integer variable which will store the value of System.currentTimeMillis() at the starting of the session and at another desired point of time or you may check the difference between the times at regular intervals.