I'd like to check-in and checkout a user with the server every time that the app is opened/closed, whether it is launched or resumed from the task drawer. Is there is a way to do this while avoiding having to call a function in each Activity?
Thank you!
EDIT
In this answer, matdev brought to my attention the more modern approach to listening to app lifecycle events via ProcessLifeCycleOwner
. See https://developer.android.com/topic/libraries/architecture/lifecycle
As such, to better organize the desired session management functionality, the following structure should be used. Register the SessionTracker
in onCreate
of the MyApplication
. Functionality related to tracking user sessions is then sequestered to the SessionTracker
class.
First add to your build.gradle
dependencies {
implementation "android.arch.lifecycle:extensions:1.1.1"
}
Then, implement the following:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(SessionTracker.getInstance());
}
}
public class SessionTracker implements LifecycleObserver {
private static SessionTracker sSessionTracker;
private static final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000; // Time allowed for transitions
private Timer mStopDelayTimer;
private TimerTask mActivityTransitionTimerTask;
private boolean mWasInBackground = true;
private AppSession mAppSession;
public static SessionTracker getInstance() {
if (sSessionTracker == null) {
sSessionTracker = new SessionTracker();
}
return sSessionTracker;
}
private SessionTracker() {
// no-op
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private void onLifecycleStop() {
submitAppSession(appSession);
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private void onLifecycleStart() {
mAppSession = new AppSession();
}
private void submitAppSession(AppSession appSession) {
// TODO submit app session here
}
}
public class AppSession {
/* TODO */
}
PREVIOUS ANSWER
The answer provided by d60402 here and the suggestion by Hanno Binder to register activity callbacks using Application.registerActivityLifecycleCallbacks() led me to this solution.
I extended Application and registered callbacks to Activity methods onPause and onStart as shown below. In these methods a timer is started/stopped (one activity being exited where onPause is called, a new one being entered where onStart is called). The flag "wasInBackground" is toggled when the app determined to be in the background/foreground (true/false). If the app was in the background when the onStart callback is run, "appEntered" is called. If the time passed between onPause and onStart callbacks is greater than a specified time (giving enough time for activity transitions) "appExited" is called where the app session is considered to be finished.
public class MyApplication extends Application {
public static final String LOG_TAG = "MyApp";
public boolean wasInBackground = true;
private AppSession appSession;
private Timer mActivityTransitionTimer;
private TimerTask mActivityTransitionTimerTask;
private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000; // Time allowed for transitions
Application.ActivityLifecycleCallbacks activityCallbacks = new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityResumed(Activity activity) {
if (wasInBackground) {
//Do app-wide came-here-from-background code
appEntered();
}
stopActivityTransitionTimer();
}
@Override
public void onActivityPaused(Activity activity) {
startActivityTransitionTimer();
}
...
};
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(activityCallbacks);
}
public void startActivityTransitionTimer() {
this.mActivityTransitionTimer = new Timer();
this.mActivityTransitionTimerTask = new TimerTask() {
public void run() {
// Task is run when app is exited
wasInBackground = true;
appExited();
}
};
this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
MAX_ACTIVITY_TRANSITION_TIME_MS);
}
public void stopActivityTransitionTimer() {
if (this.mActivityTransitionTimerTask != null) {
this.mActivityTransitionTimerTask.cancel();
}
if (this.mActivityTransitionTimer != null) {
this.mActivityTransitionTimer.cancel();
}
this.wasInBackground = false;
}
private void appEntered() {
Log.i(LOG_TAG, "APP ENTERED");
appSession = new AppSession();
}
private void appExited() {
Log.i(LOG_TAG, "APP EXITED");
appSession.finishAppSession();
// Submit AppSession to server
submitAppSession(appSession);
long sessionLength = (appSession.getT_close() - appSession.getT_open())/1000L;
Log.i(LOG_TAG, "Session Length: " + sessionLength);
}