Android SDK
This guide explains how to integrate an MTRIBES Space into your Android application.
You'll learn how to synchronize changes from your Space into your codebase using our CLI's code generation, and how to use this code to control the UX of your end users.
Sample apps
Check out our sample app covering Android to fast-track your integration.
#
Requirements- An Android project ready to integrate with MTRIBES
- An MTRIBES Space with at least one Collection and one Experience
- The MTRIBES CLI installed
- Android code generated for your Space
Our SDK is designed to use in modern Android applications with support for backward compatibility,
up to devices running API 21(Android 5.0)
.
Kotlin
is treated as our first class language in the SDK with 100% Java interoperability in
mind.
Generated code is basically an Android library module which contains Kotlin code. But don't worry if your app is written in Java, as we always try to keep the experience and compatibility intact.
We use RxJava2 for our asynchronous functionalities, which is the only supported method at present. We're working on providing more options in the future, for example: coroutines.
We only support AndroidX
as support libraries are now deprecated.
#
Code integrationWith your Space code generated, you're ready to integrate MTRIBES into your Android application.
In the code examples which follow, we'll build out an example Space containing:
- A
homepage
Collection, which contains:- A
banner
Experience - A
body
Section, which supports:Hero
Experience typesCarousel
Experience types
- A
On successful completion, an Android library module will be generated. This newly generated module will
be automatically added to your project's settings.gradle
.
info
If run outside of your root directory, you'll need to add your generated module manually to
your project settings.gradle
#
Module integrationTo see the module in your Android Studio project navigator,
- Run
Sync Project with Gradle Files
. - Add the module dependency in your application
build.gradle
file just as the CLI prompts.
dependencies { implementation project(":<module_name>")}
#
Module insightsThe generated Android module uses Kotlin with 100% Java interoperability in mind. We've made sure the integration experience for both languages are seamless.
Note
Manually updating the version of the core library is not recommended. We suggest you always use the CLI to synchronize with the latest versions. The library cannot be used independently as we treat the CLI generated module and the library as a single entity.
#
InitializationWith your Space configured and code generated, you can now import and use Experience instances to control elements of your UX.
The entry point to the MTRIBES platform is the Mtribes
singleton class included within the
generated module. With that comes the initialization functionality, which should be the first
function you call in your application code.
The recommended way of invoking this is via the entry point of your app workflow, typically in the
Application.onCreate()
method, just before super.onCreate()
.
#
Client initializationThe MTRIBES Client
is a top-level object for configuring your MTRIBES SDK.
A Client
instance is generated along with your Space integration code and can be accessed via Mtribes.client
Before using the SDK, first initialize the MTRIBES Client
with the API key of the MTRIBES Environment
you wish to target.
You can find Environment API keys under your Space settings page.
- Kotlin
- Java
class MainApplication : Application() { override fun onCreate() { Mtribes.init(this, "YOUR_MTRIBES_API_KEY_GOES_HERE") super.onCreate() }}
public class MainApplication extends Application { @Override public void onCreate() { Mtribes.init(this, "YOUR_MTRIBES_API_KEY_GOES_HERE"); super.onCreate(); }}
#
Client configurationThe MTRIBES Client
will expose some optional settings. We've given these sensible defaults, but you can override them to further harmonize MTRIBES with your application.
Example: Disable session locking to enable live Section and Experience change events during a user session.
- Kotlin
- Java
Mtribes.client.sessionLock = false
Mtribes.getClient().setSessionLock(false);
It's best to configure the Client before starting the session, but configuration can be adjusted at any time.
See the full list of client configuration options.
#
Session startBefore accessing an Experience, it's important to start a session for the active user, so we can serve them personalized states.
We represent an MTRIBES session via the session
object, which you can access via Mtribes.session
#
Anonymous userStart a session for an anonymous user.
- Kotlin
- Java
Mtribes.session.start()
Mtribes.getSession().start();
This should be done:
- on app launch if the user is anonymous
- when a user logs out
It's important to wait for the completion of start()
before
accessing Experiences and Sections.
#
Logged in userStarting a session for a logged in user is similar to an anonymous one, except you must provide their unique identifier.
- Kotlin
- Java
Mtribes.session.start(StartOptions(user.id))
Mtribes.getSession().start(new StartOptions(user.id));
This should be done:
- on app launch if the user is already logged in
- when a user logs in
Again, wait for the completion of start()
before
accessing Experiences and Sections.
Note
- User Id passed to SDK has to be consistent across all the devices/platforms.
- User Id passed should be attached to a same user for at least 30days.
Personal Information
A user ID should be opaque and not personally identifiable. For example, you should avoid email addresses, social security numbers or similarly personal information.
If you don't have an opaque ID for a user, you can use a secure hashing algorithm
such as SHA256 to encode it before
passing it to session.start
.
#
Contextual propertiesWhen starting a session, you can also provide fields with values specific to the user. These open up powerful Tribe targeting options in the MTRIBES platform.
Example 1: Start a session for an anonymous user with a contextual property of
campaign
.
- Kotlin
- Java
Mtribes.session.start( StartOptions( fields = mutableMapOf("campaign" to campaignId) ))
Map<String, Object> fields = new HashMap<>();fields.put("campaign", campaignId);
Mtribes.getSession().start(new StartOptions(null, fields));
Example 2: Start a session for a logged in user with a contextual property of
subscription
.
- Kotlin
- Java
Mtribes.session.start( StartOptions( userId = user.id, fields = mutableMapOf("subscription" to user.subscription) ))
Map<String, Object> fields = new HashMap<>();fields.put("subscription", user.subscription);
Mtribes.getSession().start(new StartOptions(user.id, fields);
We currently support the following contextual property types.
Boolean
:true
orfalse
Number
: e.g.842
,0.332
String
: e.g."gold"
Date
: ISO-8601 UTC string encoded timestamp e.g."2020-12-02T02:45:02.076Z"
This must include the date and time.
Property Limits
A maximum of 50 contextual properties can be active at one time. You can remove unused properties to make room for new ones in the contextual property settings page.
To avoid exceeding these limits or sending PII by mistake, we recommend selecting specific contextual properties to include when starting a session.
#
Experience accessWith the session primed, you can now check whether Experiences modeled in the Space are enabled for a user, and if so, what custom data properties have been defined for them.
- Kotlin
- Java
// example of a render function in your app to present a Bannerfun renderBanner() { // access the banner Experience from the homepage Collection val banner = Mtribes.collections.homepage.banner // only render the banner Experience if it's enabled if (banner.enabled) { // access customized data for the banner val imageURL = banner.data.imageURL drawBanner(imageURL) }}
// example of a render function in your app to present a Bannerprivate void renderBanner() { // access the banner Experience from the homepage Collection BannerExperience banner = Mtribes.getCollections().homepage.banner;
// only render the banner Experience if it's enabled if (banner.isEnabled()) { // access customized data for the banner String imageURL = banner.getData().imageURL; drawBanner(imageURL); }}
#
Section iterationSections contain a list of child Experiences defined at runtime by a Scheduler in the MTRIBES platform. You can iterate over these child Experiences and render each in turn, depending on its type.
Each Section defines its Supported
Experience types enum
. You can use these as switch cases/when expressions and render appropriately.
- Kotlin
- Java
// example of a render function in your app to render// a dynamic list of UI elements in the body areafun renderBody() { // access the body Section from the homepage Collection val body = Mtribes.collections.homepage.body // render each Experience of the Section in order body.children.forEach { exp -> // if an Experience is disabled we'll skip it if (exp.enabled) { when (exp::class) { HomepageSection.Supported.HERO.type -> { drawHero(exp.data) } HomepageSection.Supported.CAROUSEL.type -> { drawCarousel(exp.data) } } } }}
// example of a render function in your app to render// a dynamic list of UI elements in the body areaprivate void renderBody() { // access the body Section from the homepage Collection Section body = Mtribes.getCollections().homepage.body;
// render each Experience of the Section in order for (int i = 0; i < body.getChildren().size(); i++) { Experience<?> exp = body.getChildren().get(i); // if an Experience is disabled we'll skip it if(exp.isEnabled()) { if (exp instanceof HeroExperience) { drawHero(((HeroExperience) exp).getData()); } else if (exp instanceof CarouselExperience) { drawCarousel(((CarouselExperience) exp).getData()); } } }}
#
Change eventsThe state of an Experience or Section may change for a user during their session. You can monitor these changes and reflect them in your code.
Live updates
If you want published change events to fire during a user's session, then you'll
need to set Client.sessionLock to false
. By default, session
locking is enabled to avoid published changes negatively impacting UX.
- Kotlin
- Java
// access the members of the homepage Collectionval banner = Mtribes.collections.homepage.bannerval body = Mtribes.collections.homepage.body
// when the banner Experience changes, re-render itbanner.changed().subscribe { renderBanner() }
// when the body Section changes, re-render itbody.changed().subscribe { renderBody() }
// access the members of the homepage CollectionBannerExperience banner = Mtribes.getCollections().homepage.banner;Section body = Mtribes.getCollections().homepage.body;
// when the banner Experience changes, re-render itbanner.changed().subscribe(changeEvent -> renderBanner(changeEvent));
// when the body Section changes, re-render itbody.changed().subscribe(changeEvent -> renderBody(changeEvent));
#
Behavior trackingTo help you gather user analytical behavioral patterns associated with an
Experience, we expose a track
function.
Analytic events support a Category
and Action
to help with event
classification. These properties are encoded as a string in the format
<category>/<action>
. If only <action>
is provided, a default Category of
user
is assumed.
Analytic events also support two different optional parameters: metadata and payload.
Metadata allows up to 3 key/value pairs of additional information to be associated with the event. Metadata can be within event based tribe filters. Each key should be unique.
The payload parameter is optional and allows you to include any custom string data you wish to track along with the event. This string can be no larger than 1024 bytes.
Here are a few examples from our banner Experience above.
- Kotlin
- Java
// access the banner Experience from the homepage Collectionval banner = Mtribes.collections.homepage.banner
// track an ad viewed event// category 'ad', action 'viewed'banner.track("ad/viewed")
// track a sign in event// category 'user' (default), action 'signed_in'banner.track("signed_in")
// optional metadata can be provided via key/value pairs// limited to 3 pairs with unique keys only// Value is a string array. If you only have one value,// supply an array with one element.banner.track("item/clicked", EventMetadata( PairList("source", listOf("promotion", "event")), PairList("target_id", listOf("42070")), PairList("state", listOf("active")))
// [Deprecated] using `Pair` class// to specify a metadata key/value pair.banner.track("item/clicked", EventMetadata(Pair("source", "promotion"))
// an optional event payload of type string can be provided as a third parameter// limited to 1024 bytesbanner.track( "item/clicked", null, // this is metadata which is optional "My arbitrary payload")
// access the banner Experience from the homepage CollectionBannerExperience banner = Mtribes.collections.homepage.banner;
// track an ad viewed event// category 'ad', action 'viewed'banner.track("ad/viewed");
// track a sign in event// category 'user' (default), action 'signed_in'banner.track("signed_in");
// optional metadata can be provided via key/value pairs// limited to 3 pairs with unique keys only// Value is a string array. If you only have one value,// supply an array with one element.Mtribes.getSession().track("item/clicked", new EventMetadata( new PairList("source", Arrays.asList("promotion", "event")), new PairList("target_id", Collections.singletonList("42070")), new PairList("state", Collections.singletonList("active"))));
// [Deprecated] using `Pair` class// to specify a metadata key/value pair.Mtribes.getSession().track("item/clicked", new EventMetadata(new Pair<>("source", "promotion"));
// an optional event payload of type string can be provided as a third parameter// limited to 1024 bytesbanner.track( "item/clicked", null, // this is metadata which is optional "My arbitrary payload");
#
Global Event trackingTo track events globally, across your application,
which aren't necessarily associated with an Experience, we expose session.track
.
- Kotlin
- Java
// track an ad viewed eventMtribes.session.track("ad/viewed")
// track a sign in event// category 'user' (default), action 'signed_in'Mtribes.session.track("signed_in")
// optional metadata can be provided via key/value pairs// limited to 3 pairs with unique keys only// Value is a string array. If you only have one value,// supply an array with one element.Mtribes.session.track("item/clicked", EventMetadata( PairList("source", listOf("promotion", "event")), PairList("target_id", listOf("42070")), PairList("state", listOf("active")))
// [Deprecated] using `Pair` class// to specify a metadata key/value pair.Mtribes.session.track("item/clicked", EventMetadata(Pair("source", "promotion"))
// an optional event payload of type string can be provided as a third parameter// limited to 1024 bytesMtribes.session.track( "item/clicked", null, // this is metadata which is optional "My arbitrary payload");
// track an ad viewed event// category 'ad', action 'viewed'Mtribes.getSession().track("ad/viewed");
// track a sign in event// category 'user' (default), action 'signed_in'Mtribes.getSession().track("signed_in");
// optional metadata can be provided via key/value pairs// limited to 3 pairs with unique keys only// Value is a string array. If you only have one value,// supply an array with one element.Mtribes.getSession().track("item/clicked", new EventMetadata( new PairList("source", Arrays.asList("promotion", "event")), new PairList("target_id", Collections.singletonList("42070")), new PairList("state", Collections.singletonList("active"))));
// [Deprecated] using `Pair` class// to specify a metadata key/value pair.Mtribes.getSession().track("item/clicked", new EventMetadata(new Pair<>("source", "promotion"));
// an optional event payload of type string can be provided as a third parameter// limited to 1024 bytesMtribes.session.track( "item/clicked", null, // this is metadata which is optional "My arbitrary payload");
#
Trusted identityTrusted Identity helps ensure the authenticity of users identified into your MTRIBES Space. View our comprehensive Trusted Identity guide to understand what this is and how to enable it.
Once you have a hashed user signature returned from your server, you should pass this as an option when starting a session for a logged in user.
- Kotlin
- Java
Mtribes.session.start( StartOptions( userId = user.id, signed = signature ))
Mtribes.getSession().start(new StartOptions(user.id, null, signature));
#
TroubleshootingYou can enable internal SDK logs to get a deeper understanding of some workflows.
It's common practice to use a custom logger or a third party library like Timber for logging in an Android app code base.
To make the logging experience seamless and consistent, you could use your own logger but conform to the MtLogger
contract.
Following is an example where you can use Timber with Debug build configuration in your app.
- Kotlin
- Java
if (BuildConfig.DEBUG) { Mtribes.client.logger = object : MtLogger { override fun error(tag: String, message: String, throwable: Throwable?) { Timber.e(throwable, message) }
override fun info(tag: String, message: String) { Timber.i(message) }
override fun warn(tag: String, message: String) { Timber.w(message) } }}
if (BuildConfig.DEBUG) { Mtribes.getClient().setLogger(new MtLogger() { @Override public void info(@NotNull String tag, @NotNull String message) { Timber.i(message); }
@Override public void warn(@NotNull String tag, @NotNull String message) { Timber.w(message); }
@Override public void error(@NotNull String tag, @NotNull String message, @Nullable Throwable throwable) { Timber.e(throwable, message); } })}
#
Configuration optionsSee below a list of all available client configuration options.
#
sessionLockpublic final var sessionLock: Boolean
Defaults to true
.
Determines whether the session lock cache is enabled or not.
When true, the default backing store of the cache will be memory
.
This means app refreshing will cause the cache to be purged, and updated Experience and Section states to be made available.
When set to 'false', all session caching is disabled. Published updates from MTRIBES will be pushed in real-time to connected clients.
We’d recommend you only use this in development, or when dealing with scheduled updates that need to be real-time. In all other cases, this can negatively impact the user experience, as published changes can alter the UI a user is currently engaging with.
#
waitForMsecpublic final var waitForMsec: Int
Defaults to 1200
milliseconds (1.2 seconds).
When session.start()
is called, a network request is made to prime the session with Experience and Section states for the user.
This returns a Rx.Single which should be awaited until the session is ready to be accessed.
The priming request is designed to return quickly, however, poor network conditions may impact the response time.
To ensure that UX is not adversely impacted due to unexpected network delays,
you can set waitForMsec
to cap the number of milliseconds before the Single is complete and the application can begin accessing Experience and section states.
If the defined wait time has elapsed, then accessing the session's Experience and Section states will target code generated fallbacks. In the case that the same user was recently active, their session will target previously primed session states.
Priming will continue in the background if wait time elapses and populate the session state once loaded.
#
userTrackingpublic final var userTracking: Boolean
Defaults to true
.
Determines whether user behavioral tracking events may be sent to the MTRIBES platform.
Analytics events are needed to support meaningful insights and intelligent targeting decisions in MTRIBES.
Set this option to false
if the user did not give tracking consent.
#
includeTribespublic final var includeTribes: Boolean
Defaults to false
.
When true, Tribes for the current user will be evaluated when their session starts.
Any Tribes the current user belongs to will have their IDs exposed via session.tribeIds
#
logpublic final var logger: MtLogger
Sets a custom logger to consume and manage internal logs from the SDK.