
Official documentation for Amplitude Experiment's Client-side Android SDK implementation.

## Install

Add to the dependencies in your Android project's `build.gradle` file.

```groovy
dependencies {
  implementation 'com.amplitude:experiment-android-client:<VERSION>'
}
```

{% callout type="tip" heading="Quick start" %}
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).

{% tabs tabs="Amplitude, Third party" %}
{% tab name="Amplitude" %}

1. [Initialize the experiment client](#initialize)
2. [Fetch variants](#fetch)
3. [Access a flag's variant](#variant)

```kotlin
class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        // (1) Initialize the experiment client
        val client = Experiment.initializeWithAmplitudeAnalytics(
            this, "DEPLOYMENT_KEY", ExperimentConfig()
        )

        // (2) Fetch variants
        try {
            // NOTE: The future returned resolves after a network call. Do not
            //       wait for this future on the main application thread in
            //       production applications to avoid ANR if the user has a poor
            //       network connection.
            client.fetch().get()
        } catch (e: Exception) {
            e.printStackTrace()
        }

        // (3) Lookup a flag's variant
        val variant = client.variant("<FLAG_KEY>")
        if (variant.value == "on") {
            // Flag is on
        } else {
            // Flag is off
        }
    }
}
```

{% /tab %}
{% tab name="Third party" %}

1. [Initialize the experiment client](#initialize)
2. [Fetch variants for a user](#fetch)
3. [Access a flag's variant](#variant)

```kotlin
class MyApplication : Application() {

   override fun onCreate() {
       super.onCreate()

       // (1) Initialize the experiment client
       val client = Experiment.initialize(this, "DEPLOYMENT_KEY", ExperimentConfig.builder()
           .exposureTrackingProvider(object : ExposureTrackingProvider {
               override fun track(exposure: Exposure) {
                   // TODO: Implement exposure tracking
                   // Analytics.with(context).track(
                   //     "\$exposure",
                   //     Properties()
                   //         .putValue("flag_key", exposure.flagKey)
                   //         .putValue("variant", exposure.variant)
                   //         .putValue("experiment_key", exposure.experimentKey));
                   // )
               }
           }).build()
       )

       // (2) Fetch variants for a user
       val user = ExperimentUser.builder()
           .userId("user@company.com")
           .deviceId("abcdefg")
           .userProperty("premium", true)
           .build()
       try {
            // NOTE: The future returned resolves after a network call. Do not
            //       wait for this future on the main application thread in
            //       production applications to avoid ANR if the user has a poor
            //       network connection.
           client.fetch(user).get()
       } catch (e: Exception) {
           e.printStackTrace()
       }

       // (3) Lookup a flag's variant
       val variant = client.variant("<FLAG_KEY>")
       if (variant.value == "on") {
           // Flag is on
       } else {
           // Flag is off
       }
   }
}
```

{% /tab %}
{% /tabs %}
{% /callout %}

## Initialize

Initialize the SDK client in your application on startup. The [deployment key](/docs/feature-experiment/data-model#deployments) argument you pass to the `apiKey` parameter must live within the same project that you are sending analytics events to.

{% code-group %}
```kotlin Amplitude
fun initializeWithAmplitudeAnalytics(
    application: Application, apiKey: String, config: ExperimentConfig
)
```

```kotlin Third party
fun initialize(application: Application, apiKey: String, config: ExperimentConfig)
```
{% /code-group %}

- **`application`**
  - **Requirement**: required
  - **Description**: The Android `Application` context. Used to persist variants across sessions.
- **`apiKey`**
  - **Requirement**: required
  - **Description**: The [deployment key](/docs/feature-experiment/data-model#deployments) which authorizes fetch requests and determines which flags the SDK evaluates for the user.
- **`config`**
  - **Requirement**: optional
  - **Description**: The client [configuration](#configuration) used to customize SDK client behavior.

The initializer returns a singleton instance, so subsequent initializations for the same instance name return the initial instance. To create multiple instances, use the `instanceName` [configuration](#configuration).

{% code-group %}
```kotlin Amplitude
val experiment = Experiment.initializeWithAmplitudeAnalytics(
    context,
    "DEPLOYMENT_KEY", 
    ExperimentConfig().apply {
        // must match the name you used for your Amplitude Analytics instance
        instanceName = "myCustomInstance"
    }
)
```

```kotlin Third party
val experiment = Experiment.initialize(
    context,
    "DEPLOYMENT_KEY",
    ExperimentConfig().apply {
        // must match the name you used for your Amplitude Analytics instance
        instanceName = "myCustomInstance"
    }
)
```
{% /code-group %}

### Configuration

SDK client configuration occurs during initialization.

| Name                                      | Description                                                                                                                                                                                                                                                                                | Default Value                    |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
| `debug`                                   | **Deprecated.** When `true`, sets `logLevel` to `Debug`. Use `logLevel` instead.                                                                                                                                                                                                           | `false`                          |
| `logLevel`                                | The minimum log level to output. The SDK ignores messages below this level. Options: `LogLevel.DISABLE`, `LogLevel.ERROR`, `LogLevel.WARN`, `LogLevel.INFO`, `LogLevel.DEBUG`, `LogLevel.VERBOSE`. Go to [Custom logging](#custom-logging).                                                | `LogLevel.ERROR`                 |
| `loggerProvider`                          | Custom logger implementation. Must implement the `LoggerProvider` interface. Go to [Custom logging](#custom-logging).                                                                                                                                                                      | `AndroidLoggerProvider()`        |
| `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 helps bootstrap 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()`](#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](#fetch) remote evaluation variants on [start](#start). If false, never fetch on start.                                                                                                                                                                     | `true`                           |
| `pollOnStart`                             | Poll for local evaluation flag configuration updates every minute on [start](#start).                                                                                                                                                                                                      | `true`                           |
| `automaticFetchOnAmplitudeIdentityChange` | Only matters if you use the `initializeWithAmplitudeAnalytics` initialization function to 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 its 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`                      |

{% callout type="note" heading="EU Data Center" %}
If you use Amplitude's EU data center, configure the `serverZone` option on initialization to `ServerZone.EU`.
{% /callout %}

### Integrations

If you use either Amplitude or Segment Analytics SDKs to track events into Amplitude, set up an integration on initialization. Integrations automatically implement [provider](#providers) interfaces to enable a more streamlined developer experience by making it easier to **manage user identity** and **track exposures events**.

{% accordion title="Amplitude integration" %}
The Amplitude Experiment SDK is set up to integrate with the Amplitude Analytics SDK.
{% code-group %}
```java Java
Amplitude.getInstance().init("API_KEY");
ExperimentClient experiment = Experiment.initializeWithAmplitudeAnalytics( 
    context, "DEPLOYMENT_KEY", new ExperimentConfig());
```

```kotlin Kotlin
Amplitude.getInstance().init("API_KEY")
val experiment = Experiment.initializeWithAmplitudeAnalytics( 
    context,
    "DEPLOYMENT_KEY",
    ExperimentConfig(),
)
```
{% /code-group %}
If you use a custom instance name for analytics, set the same value in the `instanceName` configuration option in the Experiment SDK.

Using the integration initializer configures implementations of the [user provider](#user-provider) and [exposure tracking provider](#exposure-tracking-provider) interfaces to pull user data from the Amplitude Analytics SDK and track exposure events.

**Supported Versions**

| Analytics Kotlin SDK Version | Analytics Legacy SDK Version | Experiment SDK Version |
| ---------------------------- | ---------------------------- | ---------------------- |
| `1.0.0+`                     | `2.36.0+`                    | `1.5.1+`               |

{% /accordion %}

{% accordion title="Segment integration" %}
Configure Experiment's integration with Segment Analytics manually. Then configure the Experiment SDK on initialization with an instance of the exposure tracking provider. Make sure this happens _after_ the analytics SDK loads and initializes.

{% code-group %}
```java Java
class SegmentExposureTrackingProvider implements ExposureTrackingProvider {
    private Analytics analytics;
    public SegmentExposureTrackingProvider(Analytics analytics) {
        this.analytics = analytics;
    }
    @Override
    public void track(Exposure exposure) {
        analytics.track(
                "$exposure",
                new Properties()
                    .putValue("flag_key", exposure.flagKey)
                    .putValue("variant", exposure.variant)
                    .putValue("experiment_key", exposure.experimentKey));
    }
}
```

```kotlin Kotlin
class SegmentExposureTrackingProvider(
    private val analytics: Analytics
): ExposureTrackingProvider {

    override fun track(exposure: Exposure) {
        analytics.track(
            "\$exposure",
            Properties()
                .putValue("flag_key", exposure.flagKey)
                .putValue("variant", exposure.variant)
                .putValue("experiment_key", exposure.experimentKey)
        )
    }
}
```
{% /code-group %}
Configure the Experiment SDK on initialization with an instance of the exposure tracking provider.

{% code-group %}
```java Java
Analytics analytics = // Initialize segment analytics
ExperimentConfig config = ExperimentConfig.builder()
    .exposureTrackingProvider(new SegmentExposureTrackingProvider(analytics))
    .build();
ExperimentClient experiment = Experiment.initialize(context, "DEPLOYMENT_KEY", config);
```

```kotlin Kotlin
val analytics = // Initialize segment analytics
val config = ExperimentConfig.builder()
    .exposureTrackingProvider(SegmentExposureTrackingProvider(analytics))
    .build()
val experiment = Experiment.initialize(context, "DEPLOYMENT_KEY", config)
```
{% /code-group %}
When [fetching variants](#fetch), pass the segment anonymous ID and user ID for the device ID and user ID, respectively.

{% code-group %}
```java Java
String userId = analytics.getAnalyticsContext().traits().userId();
String deviceId = analytics.getAnalyticsContext().traits().anonymousId();
try {
    ExperimentUser user = ExperimentUser.builder()
        .userId(userId)
        .deviceId(deviceId)
        .build();
    experiment.fetch(user).get();
} catch (Exception e) {
    e.printStackTrace();
}
```

```kotlin Kotlin
val userId = analytics.analyticsContext.traits().userId()
val deviceId = analytics.analyticsContext.traits().anonymousId()
try {
    val user = ExperimentUser.builder()
        .userId(userId)
        .deviceId(deviceId)
        .build()
    experiment.fetch(user).get()
} catch (e: Exception) {
    e.printStackTrace()
}
```
{% /code-group %}
{% /accordion %}

## Fetch

Fetches variants for a [user](/docs/feature-experiment/data-model#users) and stores the results in the client for fast access. The function [remote evaluates](/docs/feature-experiment/remote-evaluation) the user for flags associated with the deployment used to initialize the SDK client.

```kotlin
fun fetch(user: ExperimentUser? = null, options: FetchOptions? = null): Future<ExperimentClient>
```

| Parameter | Requirement | Description                                                                                                                                                                                                                                                                                                                                  |
| --------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `user`    | optional    | Explicit [user](/docs/feature-experiment/data-model#users) information to pass with the request to fetch. The SDK merges this user information with user information provided from [integrations](#integrations) through the [user provider](#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, wait for the fetch request to return a result before rendering the user experience to avoid the interface "flickering".

{% code-group %}
```java Java
try {
    ExperimentUser user = ExperimentUser.builder()
        .userId("user@company.com")
        .userProperty("premium", true)
        .build();
    experiment.fetch(user).get();
} catch (Exception e) {
    e.printStackTrace();
}
```

```kotlin Kotlin
try {
    val user = ExperimentUser.builder()
        .userId("user@company.com")
        .userProperty("premium", true)
        .build()
    experiment.fetch(user).get()
} catch (e: Exception) {
    e.printStackTrace()
}
```
{% /code-group %}

If you're using an [integration](#integrations) or a custom [user provider](#user-provider) then you can fetch without inputting the user.

{% code-group %}
```java Java
experiment.fetch(null);
```

```kotlin Kotlin
experiment.fetch()
```
{% /code-group %}

{% callout type="tip" heading="Fetch when user identity changes" %}
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 affect flag or experiment targeting rules.

For **user properties**, Amplitude recommends passing new user properties explicitly to `fetch()` instead of relying on user enrichment before [remote evaluation](/docs/feature-experiment/remote-evaluation). Remote user-property sync through a separate system has no timing guarantees for `fetch()`, which can create a race condition.
{% /callout %}

If `fetch()` times out (default 10 seconds) or fails for any reason, the SDK client returns and retries in the background with back-off. You may configure the timeout or disable retries in the [configuration options](#configuration) during SDK client initialization.

{% accordion title="Account-level bucketing and analysis (v1.9.0+)" %}
If your organization has purchased the [Accounts add-on](/docs/analytics/account-level-reporting) you may perform bucketing and analysis on groups rather than users. Reach out to your representative to gain access to this beta feature.

Include groups in the user sent with the fetch request (recommended), or identify them with the user using a group identify call from the [Group Identify API](/docs/apis/analytics/group-identify) or using [`setGroup()` from an analytics SDK](/docs/sdks/analytics/browser/browser-sdk-2#user-groups).

{% code-group %}
```java Java
try {
    ExperimentUser user = ExperimentUser.builder()
        .userId("user@company.com")
        .userProperty("premium", true)
        .group("org name", ["Amplitude"])
        .build();
    experiment.fetch(user).get();
} catch (Exception e) {
    e.printStackTrace();
}
```

```kotlin Kotlin
try {
    val user = ExperimentUser.builder()
        .userId("user@company.com")
        .userProperty("premium", true)
        .group("org name", ["Amplitude"])
        .build()
    experiment.fetch(user).get()
} catch (e: Exception) {
    e.printStackTrace()
}
```
{% /code-group %}
To pass freeform group properties, refer to this example:

{% code-group %}
```java Java
try {
    ExperimentUser user = ExperimentUser.builder()
        .userId("user@company.com")
        .userProperty("premium", true)
        .groupProperty("org name", ["Amplitude"])
        .build();
    experiment.fetch(user).get();
} catch (Exception e) {
    e.printStackTrace();
}
```

```kotlin Kotlin
try {
    val user = ExperimentUser.builder()
        .userId("user@company.com")
        .userProperty("premium", true)
        .groupProperty("org name", ["Amplitude"])
        .build()
    experiment.fetch(user).get()
} catch (e: Exception) {
    e.printStackTrace()
}
```
{% /code-group %}
{% /accordion %}

## Start

{% callout type="info" heading="Fetch vs start" %}
Use `start` if you're using client-side [local evaluation](/docs/feature-experiment/local-evaluation). If you're only using [remote evaluation](/docs/feature-experiment/remote-evaluation), call [fetch](#fetch) instead of `start`.
{% /callout %}

Start the Experiment SDK to get flag configurations from the server and fetch remote evaluation variants for the user. The SDK is ready when the returned future resolves.

```kotlin
fun start(user: ExperimentUser? = null): Future<ExperimentClient>
```

| Parameter | Requirement | Description                                                                                                                                                                                                                                                                                                                                                                                    |
| --------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
| `user`    | optional    | Explicit [user](/docs/feature-experiment/data-model#users) information to pass with the request to fetch variants. The SDK merges this user information with user information provided from [integrations](#integrations) through the [user provider](#user-provider), preferring properties passed explicitly to `fetch()` over provided properties. Also sets the user in the SDK for reuse. | `null` |

Call `start()` when your application is initializing, after user information is available to fetch variants. The 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.

- If your application never relies on remote evaluation, set `fetchOnStart` to `false` to avoid increased startup latency caused by remote evaluation.
- If your application relies on remote evaluation, but not right at startup, you may set `fetchOnStart` to `false` and call `fetch()` and await the future separately.

{% code-group %}
```kotlin Amplitude
try {
    experiment.start().get();
} catch (e: Exception) {
    e.printStackTrace();
}
```

```kotlin Third-party
ExperimentUser user = ExperimentUser.builder()
    .userId("user@company.com")
    .userProperty("premium", true)
    .build();
try {
    experiment.start(user).get();
} catch (e: Exception) {
    e.printStackTrace();
}
```
{% /code-group %}

## Variant

Access a [variant](/docs/feature-experiment/data-model#variants) for a [flag or experiment](/docs/feature-experiment/data-model#flags-and-experiments) from the SDK client's local store.

{% callout type="info" heading="Automatic exposure tracking" %}
When you use an [integration](#integrations) or set a custom [exposure tracking provider](#exposure-tracking-provider), `variant()` tracks an exposure event through the tracking provider. To disable this functionality, [configure](#configuration) `automaticExposureTracking` to be `false`, and track exposures manually using [`exposure()`](#exposure).
{% /callout %}

```kotlin
fun variant(key: String, fallback: Variant? = null): Variant
```

| Parameter  | Requirement | Description                                                                                                                                 |
| ---------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `key`      | required    | The **flag key** to identify the [flag or experiment](/docs/feature-experiment/data-model#flags-and-experiments) to access the variant for. |
| `fallback` | optional    | The value to return if the SDK found no variant for the given `flagKey`.                                                                    |

When determining which variant a user has been bucketed into, compare the variant `value` to a well-known string.

{% code-group %}
```java Java
Variant variant = client.variant("<FLAG_KEY>");
if (variant.is("on")) {
    // Flag is on
} else {
    // Flag is off
}
```

```kotlin Kotlin
Variant variant = client.variant("<FLAG_KEY>");
if (variant.value == "on") {
    // Flag is on
} else {
    // Flag is off
}
```
{% /code-group %}

{% callout type="note" heading="Access the variant's payload" %}
A variant may also include a dynamic [payload](/docs/feature-experiment/data-model#variants) 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 must cast the payload to the expected type. Cast JSON object and array types as `org.json.JSONObject` and `org.json.JSONArray` respectively.

For example, if the payload is `{"key":"value"}`:

{% code-group %}
```java Java
Variant variant = experiment.variant("<FLAG_KEY>");
if (variant.is("on") && variant.payload != null) {
    try {
        String value = ((JSONObject) variant.payload).getString("key");
    } catch (Exception e) {
        e.printStackTrace();
    }
}
```

```kotlin Kotlin
val variant = experiment.variant("<FLAG_KEY>")
if (variant.value == "on") {
    try {
        val value = (variant.payload as JSONObject).getString("key")
    } catch (e: Exception) {
        e.printStackTrace()
    }
}
```
{% /code-group %}
{% /callout %}

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.

{% code-group %}
```java Java
Variant variant = experiment.variant("<FLAG_KEY>", new Variant("control"));
if (variant.is("control")) {
    // Control
} else if (variant.is("treatment")) {
    // Treatment
}
```

```kotlin Kotlin
val variant = experiment.variant("<FLAG_KEY>", Variant("control"))
if (variant.value == "control") {
    // Control
} else if (variant.value == "treatment") {
    // Treatment
}
```
{% /code-group %}

## All

Access all [variants](/docs/feature-experiment/data-model#variants) stored by the SDK client.

```kotlin
fun all(): Map<String, Variant>
```

{% code-group %}
```java Java
experiment.all();
```

```kotlin Kotlin
experiment.all()
```
{% /code-group %}

## Clear

Clear all [variants](/docs/feature-experiment/data-model#variants) in the cache and storage.

```kotlin
fun clear()
```

You can call `clear` after user logout to clear the variants in cache and storage.

{% code-group %}
```java Java
experiment.clear();
```

```kotlin Kotlin
experiment.clear()
```
{% /code-group %}

## Exposure

Manually track an [exposure event](/docs/feature-experiment/under-the-hood/event-tracking#exposure-events) for the current variant of the given flag key through configured [integration](#integrations) or custom [exposure tracking provider](#exposure-tracking-provider). Generally used in conjunction with setting the `automaticExposureTracking` [configuration](#configuration) optional to `false`.

```kotlin
fun exposure(key: String)
```

| Parameter | Requirement | Description                                                                                                                                                                                                                        |
| --------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `key`     | required    | The **flag key** to identify the [flag or experiment](/docs/feature-experiment/data-model#flags-and-experiments) variant to track an [exposure event](/docs/feature-experiment/under-the-hood/event-tracking#exposure-events) for. |

{% code-group %}
```java Java
Variant variant = experiment.variant("<FLAG_KEY>");

// Do other things...

experiment.exposure("<FLAG_KEY>");
if (variant.is("control")) {
    // Control
} else if (variant.is("treatment")) {
    // Treatment
}
```

```kotlin Kotlin
val variant = experiment.variant("<FLAG_KEY>")

// Do other things...

experiment.exposure("<FLAG_KEY>")
if (variant.value == "control") {
    // Control
} else if (variant.value == "treatment") {
    // Treatment
}
```
{% /code-group %}

## Providers

{% callout type="tip" heading="Integrations" %}
If you use Amplitude or Segment analytics SDKs along side the Experiment Client SDK, Amplitude recommends you use an [integration](#integrations) instead of implementing custom providers.
{% /callout %}

Provider implementations enable a more streamlined developer experience by making it easier to manage user identity and track exposures events.

### User provider

The SDK client uses the user provider to access the most up-to-date user information only when needed (for example, when the SDK calls [`fetch()`](#fetch)). The user provider is optional, but helps if you have a user information store already set up in your application. With a user provider, you don't need to manage two separate user info stores in parallel. Separate stores can create divergent user state if the application updates the user store and experiment isn't (or vice versa).

```kotlin title="ExperimentUserProvider"
interface ExperimentUserProvider {
    fun getUser(): ExperimentUser
}
```

To use your custom user provider, set the `userProvider` [configuration](#configuration) option with an instance of your custom implementation on SDK initialization.

{% code-group %}
```java Java
ExperimentConfig config = ExperimentConfig.builder()
    .userProvider(new CustomUserProvider())
    .build();
ExperimentClient experiment = Experiment.initialize(
    context, "<DEPLOYMENT_KEY>", config);
```

```kotlin Kotlin
val config = ExperimentConfig.builder()
    .userProvider(CustomUserProvider())
    .build()
val experiment = Experiment.initialize(context, "<DEPLOYMENT_KEY>", config)
```
{% /code-group %}

### Exposure tracking provider

Amplitude highly recommends implementing an exposure tracking provider. [Exposure tracking](/docs/feature-experiment/under-the-hood/event-tracking#exposure-events) increases the accuracy and reliability of experiment results and improves visibility into which flags and experiments a user is exposed to.

```kotlin
interface ExposureTrackingProvider {
    fun track(exposure: Exposure)
}
```

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](#initialize) the SDK client lives within, and for the same user that the SDK [fetched](#fetch) variants for.

To use your custom user provider, set the `exposureTrackingProvider` [configuration](#configuration) option with an instance of your custom implementation on SDK initialization.

{% code-group %}
```java Java
ExperimentConfig config = ExperimentConfig.builder()
    .exposureTrackingProvider(new CustomExposureTrackingProvider())
    .build();
ExperimentClient experiment = Experiment.initialize(
    context, "<DEPLOYMENT_KEY>", config);
```

```kotlin Kotlin
val config = ExperimentConfig.builder()
    .exposureTrackingProvider(CustomExposureTrackingProvider())
    .build()
val experiment = Experiment.initialize(context, "<DEPLOYMENT_KEY>", config)
```
{% /code-group %}

## Bootstrapping

You may want to bootstrap the experiment client with an initial set of flags or variants when you get variants from an external source (for example, not from calling `fetch()` on the SDK client). Use cases include [local evaluation](/docs/feature-experiment/local-evaluation) or integration testing on specific variants.

### Bootstrapping variants

To bootstrap the client with a predefined set of variants, set the flags and variants in the `initialVariants` [configuration](#configuration) object, then set the `source` to `Source.InitialVariants` so that the SDK client prefers the bootstrapped variants over any fetched & stored variants from before for the same flags.

```swift
let config = ExperimentConfigBuilder()
    .initialVariants(["<FLAG_KEY>": Variant("<VARIANT>")])
    .source(Source.InitialVariants)
    .build()
let experiment = Experiment.initialize(apiKey: "<DEPLOYMENT_KEY>", config: config)
```

{% code-group %}
```java Java
ExperimentConfig config = ExperimentConfig.builder()
    .initialVariants(Map.of("<FLAG_KEY>", new Variant("<VARIANT>")))
    .source(Source.INITIAL_VARIANTS)
    .build();
ExperimentClient experiment = Experiment.initialize(
    context, "<DEPLOYMENT_KEY>", config);
```

```kotlin Kotlin
val config = ExperimentConfig.builder()
    .initialVariants(mapOf("<FLAG_KEY>" to Variant("<VARIANT>")))
    .source(Source.INITIAL_VARIANTS)
    .build()
val experiment = Experiment.initialize(context, "<DEPLOYMENT_KEY>", config)
```
{% /code-group %}

### Bootstrapping flag configurations

You may choose to bootstrap the SDK with an initial set of local evaluation flag configurations using the `initialFlags` configuration. Experiment evaluates these when you call [variant](#variant), unless you load an updated flag config or variant with [start](#start) or [fetch](#fetch).

To download initial flags, use the [evaluation flags API](/docs/apis/experiment/experiment-evaluation-api#flags-api)

```swift
let config = ExperimentConfigBuilder()
    .initialFlags("<FLAGS_JSON>")
    .build()
let experiment = Experiment.initialize(apiKey: "<DEPLOYMENT_KEY>", config: config)
```

{% code-group %}
```java Java
ExperimentConfig config = ExperimentConfig.builder()
    .initialFlags("<FLAGS_JSON>")
    .build();
ExperimentClient experiment = Experiment.initialize(
    context, "<DEPLOYMENT_KEY>", config);
```

```kotlin Kotlin
val config = ExperimentConfig.builder()
    .initialFlags("<FLAGS_JSON>")
    .build()
val experiment = Experiment.initialize(context, "<DEPLOYMENT_KEY>", config)
```
{% /code-group %}

## Custom logging

Control log verbosity with the `logLevel` configuration, or implement the `LoggerProvider` interface to integrate your own logger.

### Log levels

- `LogLevel.DISABLE` - No logs.
- `LogLevel.ERROR` - Errors only (default).
- `LogLevel.WARN` - Errors and warnings.
- `LogLevel.INFO` - Errors, warnings, and info.
- `LogLevel.DEBUG` - Errors, warnings, info, and debug.
- `LogLevel.VERBOSE` - All messages including verbose details.

{% code-group %}
```kotlin Kotlin
// Set log level to debug
val experiment = Experiment.initialize(
    context,
    "<DEPLOYMENT_KEY>",
    ExperimentConfig.builder()
        .logLevel(LogLevel.DEBUG)
        .build()
)


```

```java Java
// Set log level to debug
ExperimentClient experiment = Experiment.initialize(
    context,
    "<DEPLOYMENT_KEY>",
    ExperimentConfig.builder()
        .logLevel(LogLevel.DEBUG)
        .build()
);

```
{% /code-group %}

### Custom logger

Implement the `LoggerProvider` interface to use your own logging solution.

{% code-group %}
```kotlin Kotlin
// Implement the LoggerProvider interface
class CustomLoggerProvider : LoggerProvider {
    override fun verbose(msg: String) {
        // Send verbose logs to your logging service
        myLoggingService.verbose(msg)
    }

    override fun debug(msg: String) {
        myLoggingService.debug(msg)
    }

    override fun info(msg: String) {
        myLoggingService.info(msg)
    }

    override fun warn(msg: String) {
        myLoggingService.warn(msg)
    }

    override fun error(msg: String) {
        myLoggingService.error(msg)
    }
}

// Initialize with custom logger
val experiment = Experiment.initialize(
    context,
    "<DEPLOYMENT_KEY>",
    ExperimentConfig.builder()
        .loggerProvider(CustomLoggerProvider())
        .logLevel(LogLevel.WARN)
        .build()
)
```

```java Java
// Implement the LoggerProvider interface
class CustomLoggerProvider implements LoggerProvider {
    @Override
    public void verbose(String msg) {
        // Send verbose logs to your logging service
        myLoggingService.verbose(msg);
    }

    @Override
    public void debug(String msg) {
        myLoggingService.debug(msg);
    }

    @Override
    public void info(String msg) {
        myLoggingService.info(msg);
    }

    @Override
    public void warn(String msg) {
        myLoggingService.warn(msg);
    }

    @Override
    public void error(String msg) {
        myLoggingService.error(msg);
    }
}

// Initialize with custom logger
ExperimentClient experiment = Experiment.initialize(
    context,
    "<DEPLOYMENT_KEY>",
    ExperimentConfig.builder()
        .loggerProvider(new CustomLoggerProvider())
        .logLevel(LogLevel.WARN)
        .build()
);
```
{% /code-group %}

### Debug flag (deprecated)

The `debug` configuration flag is deprecated. Use `logLevel` instead.

{% code-group %}
```kotlin Kotlin
// Deprecated: Sets logLevel to Debug
val experiment = Experiment.initialize(
    context,
    "<DEPLOYMENT_KEY>",
    ExperimentConfig.builder()
        .debug(true)
        .build()
)

// Preferred: Use logLevel instead
val experiment = Experiment.initialize(
    context,
    "<DEPLOYMENT_KEY>",
    ExperimentConfig.builder()
        .logLevel(LogLevel.DEBUG)
        .build()
)
```

```java Java
// Deprecated: Sets logLevel to Debug
ExperimentClient experiment = Experiment.initialize(
    context,
    "<DEPLOYMENT_KEY>",
    ExperimentConfig.builder()
        .debug(true)
        .build()
);

// Preferred: Use logLevel instead
ExperimentClient experiment = Experiment.initialize(
    context,
    "<DEPLOYMENT_KEY>",
    ExperimentConfig.builder()
        .logLevel(LogLevel.DEBUG)
        .build()
);
```
{% /code-group %}
