SessionTracker is a general purpose framework to provide a foundation for session management in your app. Your app provides (a) session tracking storage implementation and (b) session tracking state machine configuration, while SessionTracker provides callbacks to create/update/release session resources.
At the project level build.gradle
, add a maven repo pointing to https://jitpack.io
, e.g.:
allprojects {
repositories {
google()
maven { url "https://jitpack.io" } // this is it
jcenter()
}
}
At a module level build.gradle
, add the following dependency:
implementation 'com.github.vitkhudenko:session_tracker:2.0.1'
Session is a flexible entity - it could be a user session with user signing in/out or a bluetooth device session with all its possible states.
In SessionTracker framework, sessions are represented by session tracking records - instances of
SessionRecord
. It is an immutable data structure of 2 fields - session ID and
session tracking state.
Note, there are two (partially intersecting) types of session state:
- The session state that is tracked by SessionTracker, which is always an instance of enum by the contract.
- The session state that is specific to your app, which can be as diverse as your app's business logic requires
and which can not be represented by the
SessionRecord
.
Please don't mess one with another. Actual implementation of session, including its persistence, is up to your app and is out of SessionTracker framework responsibility. It is correct to say that session tracking state (the one tracked by SessionTracker) is a subset of a full session state in your app.
SessionTracker framework supports session tracking auto-restoration on application process restarts. Your
app must provide an implementation of ISessionTrackerStorage
, which is used by SessionTracker to make CRUD
operations on session tracking records.
SessionTracker maintains a state machine per session. Your app must define a set of possible events and states per session. Using events and states, your app should provide state machine transitions, which are used to configure session state machine. For example, your app may define the following session tracking events and states:
enum class State {
INACTIVE, ACTIVE
}
enum class Event {
LOGIN, LOGOUT
}
then a sample transitions config (an implementation of ISessionStateTransitionsSupplier
) could be as following:
val sessionStateTransitionsSupplier = object : ISessionStateTransitionsSupplier<Event, State> {
override fun getStateTransitions(sessionId: SessionId) = listOf(
Transition(
event = Event.LOGIN,
statePath = listOf(State.INACTIVE, State.ACTIVE)
),
Transition(
event = Event.LOGOUT,
statePath = listOf(State.ACTIVE, State.INACTIVE)
)
)
}
Such config would mean there are two possible session tracking states (ACTIVE
/INACTIVE
) and two possible
session tracking events: LOGIN
(to move session from INACTIVE
to ACTIVE
state) and LOGOUT
(to move
session from ACTIVE
to INACTIVE
state).
In order to make SessionTracker ready to function it should be initialized first. The most appropriate place for
initialize(sessionTrackerListener: Listener<Event, State>)
call is android.app.Application.onCreate()
.
Suppose your user hits "Login" button, your app authenticates user and creates a session. In order to make use of SessionTracker the session should be "attached" to SessionTracker:
sessionTracker.trackSession(sessionId, State.ACTIVE)
Now SessionTracker is tracking the session until your app calls untrackSession(sessionId)
. Next time
your app starts (and SessionTracker is initialized), the session tracking will be automatically restored
by SessionTracker with the same ACTIVE
state.
As long as session is tracked, its session tracking state changes are propagated to your app via SessionTracker.Listener
.
Suppose eventually your user hits "Log Out" button, then your app is responsible to communicate this event
to SessionTracker by asking to consume LOGOUT
event for the session:
sessionTracker.consumeEvent(sessionId, Event.LOGOUT)
Now SessionTracker updates session tracking state to INACTIVE
, persists session record with the new state and
propagates this state change via SessionTracker.Listener
. Note, the session is still tracked by SessionTracker,
so next time your app starts, the session tracking will be automatically restored by SessionTracker with
the same INACTIVE
state.
SessionTracker.Listener
has useful for your app callbacks that allow to manage session resources appropriately:
-
onSessionTrackerInitialized(sessionTracker: SessionTracker<Event, State>, sessionRecords: List<SessionRecord<State>>)
SessionTracker has added sessions to the list of tracked sessions. This happens as a result of calling
SessionTracker.initialize(sessionTrackerListener: Listener<Event, State>)
. This callback is the right place to create any resources for the sessions (a DB connection, a DI scope, etc.). -
onSessionTrackingStarted(sessionTracker: SessionTracker<Event, State>, sessionRecord: SessionRecord<State>)
SessionTracker has added session to the list of tracked sessions. This happens as a result of calling
SessionTracker.trackSession(sessionId, state)
. This callback is the right place to create any resources for the session (a DB connection, a DI scope, etc.). -
onSessionStateChanged(sessionTracker: SessionTracker<Event, State>, sessionRecord: SessionRecord<State>, oldState: State)
Session tracking state has changed. This happens as a result of calling
SessionTracker.consumeEvent(sessionId, event)
. This callback is the right place to create or release any resources for the session (a DB connection, a DI scope, etc.). -
onSessionTrackingStopped(sessionTracker: SessionTracker<Event, State>, sessionRecord: SessionRecord<State>)
SessionTracker has removed session from the list of tracked sessions. This happens as a result of calling
SessionTracker.untrackSession(sessionId)
. This may also happen as a result of callingSessionTracker.consumeEvent
if session appears in one of theautoUntrackStates
(aSessionTracker
constructor parameter). This callback is the right place to release any resources for the session (a DB connection, a DI scope, etc.). -
onAllSessionsTrackingStopped(sessionTracker: SessionTracker<Event, State>, sessionRecords: List<SessionRecord<State>>)
SessionTracker has removed all sessions from the list of tracked sessions. This happens as a result of calling
SessionTracker.untrackAllSessions()
. This callback is the right place to release any resources for the sessions (a DB connection, a DI scope, etc.).
DI framework | Sample app module |
---|---|
Koin | sample_app_koin |
Dagger | sample_app_dagger |
SessionTracker is thread-safe. Public methods are declared as synchronized
. Thread-safe compound actions are
possible by using synchronized statement on SessionTracker
instance:
synchronized(sessionTracker) {
sessionTracker.consumeEvent(..) // step 1 of the compound action
sessionTracker.consumeEvent(..) // step 2 of the compound action
}
SessionTracker is a synchronous tool, meaning it neither creates threads nor uses thread-pools or handlers.
Typical simple SessionTracker usage implies being a singleton in your app.
MIT License
Copyright (c) 2019 Vitaliy Khudenko
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.