Official documentation for Amplitude Experiment's Client-side Android SDK implementation.
Add to the dependencies in your Android project's build.gradle
file.
1dependencies {2 implementation 'com.amplitude:experiment-android-client:<VERSION>'3}
The right way to initialize the Experiment SDK depends on whether you use an Amplitude SDK for analytics or a third party (for example, Segment).
1class MyApplication : Application() { 2 3 override fun onCreate() { 4 super.onCreate() 5 6 // (1) Initialize the experiment client 7 val client = Experiment.initializeWithAmplitudeAnalytics( 8 this, "DEPLOYMENT_KEY", ExperimentConfig() 9 )10 11 // (2) Fetch variants12 try {13 // NOTE: The future returned resolves after a network call. Do not14 // wait for this future on the main application thread in15 // production applications to avoid ANR if the user has a poor16 // network connection.17 client.fetch().get()18 } catch (e: Exception) {19 e.printStackTrace()20 }21 22 // (3) Lookup a flag's variant23 val variant = client.variant("<FLAG_KEY>")24 if (variant.value == "on") {25 // Flag is on26 } else {27 // Flag is off28 }29 }30}
1class MyApplication : Application() { 2 3 override fun onCreate() { 4 super.onCreate() 5 6 // (1) Initialize the experiment client 7 val client = Experiment.initialize(this, "DEPLOYMENT_KEY", ExperimentConfig.builder() 8 .exposureTrackingProvider(object : ExposureTrackingProvider { 9 override fun track(exposure: Exposure) {10 // TODO: Implement exposure tracking11 // Analytics.with(context).track(12 // "\$exposure",13 // Properties()14 // .putValue("flag_key", exposure.flagKey)15 // .putValue("variant", exposure.variant)16 // .putValue("experiment_key", exposure.experimentKey));17 // )18 }19 }).build()20 )21 22 // (2) Fetch variants for a user23 val user = ExperimentUser.builder()24 .userId("user@company.com")25 .deviceId("abcdefg")26 .userProperty("premium", true)27 .build()28 try {29 // NOTE: The future returned resolves after a network call. Do not30 // wait for this future on the main application thread in31 // production applications to avoid ANR if the user has a poor32 // network connection.33 client.fetch(user).get()34 } catch (e: Exception) {35 e.printStackTrace()36 }37 38 // (3) Lookup a flag's variant39 val variant = client.variant("<FLAG_KEY>")40 if (variant.value == "on") {41 // Flag is on42 } else {43 // Flag is off44 }45 }46}
The SDK client should be initialized in your application on startup. The deployment key argument passed into the apiKey
parameter must live within the same project that you are sending analytics events to.
1fun initializeWithAmplitudeAnalytics(2 application: Application, apiKey: String, config: ExperimentConfig3)
1fun initialize(application: Application, apiKey: String, config: ExperimentConfig)
Parameter |
Requirement | Description |
---|---|---|
application |
required | The Android Application context. Used to persist variants across sessions. |
apiKey |
required | The deployment key which authorizes fetch requests and determines which flags should be evaluated for the user. |
config |
optional | The client configuration used to customize SDK client behavior. |
The initializer returns a singleton instance, so subsequent initializations for the same instance name will always return the initial instance. To create multiple instances, use the instanceName
configuration.
1val experiment = Experiment.initializeWithAmplitudeAnalytics(2 context, "DEPLOYMENT_KEY", ExperimentConfig()3)
1val experiment = Experiment.initialize(context, "DEPLOYMENT_KEY", ExperimentConfig())
SDK client configuration occurs during initialization.
Name |
Description | Default Value |
---|---|---|
debug |
Enable additional debug logging within the SDK. Should be set to false in production builds. | false |
fallbackVariant |
The default variant to fall back if a variant for the provided key doesn't exist. | {} |
initialVariants |
An initial set of variants to access. This field is valuable for bootstrapping the client SDK with values rendered by the server using server-side rendering (SSR). | {} |
source |
The primary source of variants. Set the value to Source.INITIAL_VARIANTS and configure initialVariants to bootstrap the SDK for SSR or testing purposes. |
Source.LOCAL_STORAGE |
serverZone |
Select the Amplitude data center to get flags and variants from | ServerZone.US |
serverUrl |
The host to fetch remote evaluation variants from. For hitting the EU data center, use serverZone . |
https://api.lab.amplitude.com |
flagsServerUrl |
The host to fetch local evaluation flags from. For hitting the EU data center, use serverZone . |
https://flag.lab.amplitude.com |
fetchTimeoutMillis |
The timeout for fetching variants in milliseconds. | 10000 |
retryFetchOnFailure |
Whether to retry variant fetches in the background if the request doesn't succeed. | true |
automaticExposureTracking |
If true, calling variant() tracks an exposure event through the configured exposureTrackingProvider . If no exposure tracking provider is set, this configuration option does nothing. |
true |
fetchOnStart |
If true or null, always fetch remote evaluation variants on start. If false, never fetch on start. | true |
pollOnStart |
Poll for local evaluation flag configuration updates once per minute on start. | true |
automaticFetchOnAmplitudeIdentityChange |
Only matters if you use the initializeWithAmplitudeAnalytics initialization function to seamlessly integrate with the Amplitude Analytics SDK. If true any change to the user ID, device ID or user properties from analytics triggers the experiment SDK to fetch variants and update it's cache. |
false |
userProvider |
An interface used to provide the user object to fetch() when called. |
null |
exposureTrackingProvider |
Implement and configure this interface to track exposure events through the experiment SDK, either automatically or explicitly. | null |
instanceName |
Custom instance name for experiment SDK instance. The value of this field is case-sensitive. | null |
initialFlags |
A JSON string representing an initial set of flag configurations to use for local evaluation. | undefined |
If you use Amplitude's EU data center, configure the serverZone
option on initialization to ServerZone.EU
.
If you use either Amplitude or Segment Analytics SDKs to track events into Amplitude, set up an integration on initialization. Integrations automatically implement provider interfaces to enable a more streamlined developer experience by making it easier to manage user identity and track exposures events.
Using the integration initializer will automatically configure implementations of the user provider and exposure tracking provider interfaces to pull user data from the Amplitude Analytics SDK and track exposure events. Supported Versions
Amplitude integration
instanceName
configuration option in the Experiment SDK.
Analytics SDK Version
Experiment SDK Version
2.36.0+
1.5.1+
Segment integration
SegmentExposureTrackingProvider.java
Fetches variants for a user and store the results in the client for fast access. This function remote evaluates the user for flags associated with the deployment used to initialize the SDK client.
1fun fetch(user: ExperimentUser? = null, options: FetchOptions? = null): Future<ExperimentClient>
Parameter | Requirement | Description |
---|---|---|
user |
optional | Explicit user information to pass with the request to evaluate. This user information is merged with user information provided from integrations via the user provider, preferring properties passed explicitly to fetch() over provided properties. |
options |
optional | Explicit flag keys to fetch. |
Amplitude Experiment recommends calling fetch()
during application start up so that the user gets the most up-to-date variants for the application session. Furthermore, you'll need to wait for the fetch request to return a result before rendering the user experience to avoid the interface "flickering".
1try {2 ExperimentUser user = ExperimentUser.builder()3 .userId("user@company.com")4 .userProperty("premium", true)5 .build();6 experiment.fetch(user).get();7} catch (Exception e) {8 e.printStackTrace();9}
1try {2 val user = ExperimentUser.builder()3 .userId("user@company.com")4 .userProperty("premium", true)5 .build()6 experiment.fetch(user).get()7} catch (e: Exception) {8 e.printStackTrace()9}
If you're using an integration or a custom user provider then you can fetch without inputting the user.
1experiment.fetch(null);
1experiment.fetch()
If you want the most up-to-date variants for the user, it's recommended that you call fetch()
whenever the user state changes in a meaningful way. For example, if the user logs in and receives a user ID, or has a user property set which may effect flag or experiment targeting rules.
In the case of user properties, Amplitude recommends passing new user properties explicitly to fetch()
instead of relying on user enrichment prior to remote evaluation. This is because user properties that are synced remotely through a separate system have no timing guarantees with respect to fetch()
-- for example, a race.
If fetch()
times out (default 10 seconds) or fails for any reason, the SDK client will return and retry in the background with back-off. You may configure the timeout or disable retries in the configuration options when the SDK client is initialized.
Use start
if you're using client-side local evaluation. If you're only using remote evaluation, call fetch instead of start
.
Start the Experiment SDK to get flag configurations from the server and fetch remote evaluation variants for the user. The SDK is ready once the returned future resolves.
1fun start(user: ExperimentUser? = null): Future<ExperimentClient>
Parameter | Requirement | Description |
---|---|---|
user |
optional | Explicit user information to pass with the request to fetch variants. This user information is merged with user information provided from integrations through the user provider, preferring properties passed explicitly to fetch() over provided properties. Also sets the user in the SDK for reuse. |
Call start()
when your application is initializing, after user information is available to use to evaluate or fetch variants. The returned future resolves after loading local evaluation flag configurations and fetching remote evaluation variants.
Configure the behavior of start()
by setting fetchOnStart
in the SDK configuration on initialization to improve performance based on the needs of your application.
fetchOnStart
to false
to avoid increased startup latency caused by remote evaluation.fetchOnStart
to false
and call fetch()
and await the future separately.
1try {2 experiment.start().get();3} catch (e: Exception) {4 e.printStackTrace();5}
1ExperimentUser user = ExperimentUser.builder()2 .userId("user@company.com")3 .userProperty("premium", true)4 .build();5try {6 experiment.start(user).get();7} catch (e: Exception) {8 e.printStackTrace();9}
Access a variant for a flag or experiment from the SDK client's local store.
When an integration is used or a custom exposure tracking provider is set, variant()
will automatically track an exposure event through the tracking provider. To disable this functionality, configure automaticExposureTracking
to be false
, and track exposures manually using exposure()
.
1fun variant(key: String, fallback: Variant? = null): Variant
Parameter | Requirement | Description |
---|---|---|
key |
required | The flag key to identify the flag or experiment to access the variant for. |
fallback |
optional | The value to return if no variant was found for the given flagKey . |
When determining which variant a user has been bucketed into, you'll want to compare the variant value
to a well-known string.
1Variant variant = client.variant("<FLAG_KEY>");2if (variant.is("on")) {3 // Flag is on4} else {5 // Flag is off6}
1Variant variant = client.variant("<FLAG_KEY>");2if (variant.value == "on") {3 // Flag is on4} else {5 // Flag is off6}
A variant may also be configured with a dynamic payload of arbitrary data. Access the payload
field from the variant object after checking the variant's value
.
The payload
on Android is of type Object
(Any?
) meaning you will need to cast the payload to the expected type. JSON object and array types need to be cast as org.json.JSONObject
and org.json.JSONArray
respectively.
For example, if the payload is {"key":"value"}
:
1Variant variant = experiment.variant("<FLAG_KEY>");2if (variant.is("on") && variant.payload != null) {3 try {4 String value = ((JSONObject) variant.payload).getString("key");5 } catch (Exception e) {6 e.printStackTrace();7 }8}
1val variant = experiment.variant("<FLAG_KEY>")2if (variant.value == "on") {3 try {4 val value = (variant.payload as JSONObject).getString("key")5 } catch (e: Exception) {6 e.printStackTrace()7 }8}
A null
variant value
means that the user hasn't been bucketed into a variant. You may use the built in fallback parameter to provide a variant to return if the store doesn't contain a variant for the given flag key.
1Variant variant = experiment.variant("<FLAG_KEY>", new Variant("control"));2if (variant.is("control")) {3 // Control4} else if (variant.is("treatment")) {5 // Treatment6}
1val variant = experiment.variant("<FLAG_KEY>", Variant("control"))2if (variant.value == "control") {3 // Control4} else if (variant.value == "treatment") {5 // Treatment6}
Access all variants stored by the SDK client.
1fun all(): Map<String, Variant>
1experiment.all();
1experiment.all()
Clear all variants in the cache and storage.
1fun clear()
You can call clear
after user logout to clear the variants in cache and storage.
1experiment.clear();
1experiment.clear()
Manually track an exposure event for the current variant of the given flag key through configured integration or custom exposure tracking provider. Generally used in conjunction with setting the automaticExposureTracking
configuration optional to false
.
1fun exposure(key: String)
Parameter | Requirement | Description |
---|---|---|
key |
required | The flag key to identify the flag or experiment variant to track an exposure event for. |
1Variant variant = experiment.variant("<FLAG_KEY>"); 2 3// Do other things... 4 5experiment.exposure("<FLAG_KEY>"); 6if (variant.is("control")) { 7 // Control 8} else if (variant.is("treatment")) { 9 // Treatment10}
1val variant = experiment.variant("<FLAG_KEY>") 2 3// Do other things... 4 5experiment.exposure("<FLAG_KEY>") 6if (variant.value == "control") { 7 // Control 8} else if (variant.value == "treatment") { 9 // Treatment10}
If you use Amplitude or Segment analytics SDKs along side the Experiment Client SDK, Amplitude recommends you use an integration instead of implementing custom providers.
Provider implementations enable a more streamlined developer experience by making it easier to manage user identity and track exposures events.
The user provider is used by the SDK client to access the most up-to-date user information only when it's needed (for example, when fetch()
is called). This provider is optional, but helps if you have a user information store already set up in your application. This way, you don't need to manage two separate user info stores in parallel, which may result in a divergent user state if the application user store is updated and experiment isn't (or vice versa).
1interface ExperimentUserProvider {2 fun getUser(): ExperimentUser3}
To use your custom user provider, set the userProvider
configuration option with an instance of your custom implementation on SDK initialization.
1ExperimentConfig config = ExperimentConfig.builder()2 .userProvider(new CustomUserProvider())3 .build();4ExperimentClient experiment = Experiment.initialize(5 context, "<DEPLOYMENT_KEY>", config);
1val config = ExperimentConfig.builder()2 .userProvider(CustomUserProvider())3 .build()4val experiment = Experiment.initialize(context, "<DEPLOYMENT_KEY>", config)
Implementing an exposure tracking provider is highly recommended. Exposure tracking increases the accuracy and reliability of experiment results and improves visibility into which flags and experiments a user is exposed to.
1interface ExposureTrackingProvider {2 fun track(exposure: Exposure)3}
The implementation of track()
should track an event of type $exposure
(a.k.a name) with two event properties, flag_key
and variant
, corresponding to the two fields on the Exposure
object argument. Finally, the event tracked must eventually end up in Amplitude Analytics for the same project that the [deployment] used to initialize the SDK client lives within, and for the same user that variants were fetched for.
To use your custom user provider, set the exposureTrackingProvider
configuration option with an instance of your custom implementation on SDK initialization.
1ExperimentConfig config = ExperimentConfig.builder()2 .exposureTrackingProvider(new CustomExposureTrackingProvider())3 .build();4ExperimentClient experiment = Experiment.initialize(5 context, "<DEPLOYMENT_KEY>", config);
1val config = ExperimentConfig.builder()2 .exposureTrackingProvider(CustomExposureTrackingProvider())3 .build()4val experiment = Experiment.initialize(context, "<DEPLOYMENT_KEY>", config)
Thanks for your feedback!
June 4th, 2024
Need help? Contact Support
Visit Amplitude.com
Have a look at the Amplitude Blog
Learn more at Amplitude Academy
© 2024 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.