Android and Android TV Feature Flags

This product is not supported for your selected Datadog site. ().
このページは日本語には対応しておりません。随時翻訳に取り組んでいます。
翻訳に関してご質問やご意見ございましたら、お気軽にご連絡ください
Join the Preview!

Feature Flags are in Preview. Complete the form to request access.

Request Access

Overview

This page describes how to instrument your Android or Android TV application with the Datadog Feature Flags SDK. Datadog feature flags provide a unified way to remotely control feature availability in your app, experiment safely, and deliver new experiences with confidence.

This guide explains how to install and enable the SDK, create and use a FlagsClient, and configure advanced options.

Installation

Declare dd-sdk-android-flags as a dependency in your project. Add the library as a Gradle dependency in your application module’s build.gradle file:

build.gradle

dependencies {
    implementation "com.datadoghq:dd-sdk-android-flags:<latest-version>"

    // Recommended: RUM integration drives analysis and enriches RUM session data
    implementation "com.datadoghq:dd-sdk-android-rum:<latest-version>"
}

Initialize the SDK

Initialize Datadog as early as possible in your app lifecycle—typically in your Application class’s onCreate() method. This ensures all feature flag evaluations and telemetry are captured correctly.

val configuration = Configuration.Builder(
    clientToken = "<CLIENT_TOKEN>",
    env = "<ENV_NAME>",
    variant = "<APP_VARIANT_NAME>"
).build()

Datadog.initialize(this, configuration, TrackingConsent.GRANTED)

Enable flags

After initializing Datadog, enable Flags to attach it to the current Datadog SDK instance and prepare for client creation and flags evaluation:

import com.datadog.android.flags.Flags

Flags.enable()

You can also pass a configuration object; see Advanced configuration.

Create and retrieve a client

Create a client once, typically during app startup:

FlagsClient.Builder().build() // Creates the default client

Retrieve the same client anywhere in your app:

val flagsClient = FlagsClient.get() // Retrieves the "default" client

You can also create and retrieve multiple clients by providing the name parameter:

FlagsClient.Builder("checkout").build()
val flagsClient = FlagsClient.get("checkout")
If a client with the given name already exists, the existing instance is reused.

Set the evaluation context

Define who or what the flag evaluation applies to using a FlagsEvaluationContext. The evaluation context includes user or session information used to determine which flag variations should be returned. Call this method before evaluating flags to ensure proper targeting.

flagsClient.setEvaluationContext(
    EvaluationContext(
        targetingKey = "user-123",
        attributes = mapOf(
            "email" to "user@example.com",
            "tier" to "premium"
        )
    )
)

This method fetches flag assignments from the server asynchronously in the background. The operation is non-blocking and thread-safe. Flag updates are available for subsequent evaluations once the background operation completes.

Evaluate flags

After creating the FlagsClient and setting its evaluation context, you can start reading flag values throughout your app. Flag evaluation is local and instantaneous—the SDK uses locally cached data, so no network requests occur when evaluating flags. This makes evaluations safe to perform on the main thread.

Each flag is identified by a key (a unique string) and can be evaluated with a typed method that returns a value of the expected type. If the flag doesn’t exist or cannot be evaluated, the SDK returns the provided default value.

Boolean flags

Use resolveBooleanValue(key, defaultValue) for flags that represent on/off or true/false conditions. For example:

val isNewCheckoutEnabled = flagsClient.resolveBooleanValue(
    flagKey = "checkout.new",
    defaultValue = false
)

if (isNewCheckoutEnabled) {
    showNewCheckoutFlow()
} else {
    showLegacyCheckout()
}

String flags

Use resolveStringValue(key, defaultValue) for flags that select between multiple variants or configuration strings. For example:

val theme = flagsClient.resolveStringValue(
    flagKey = "ui.theme",
    defaultValue = "light"
)

when (theme) {
    "light" -> setLightTheme()
    "dark" -> setDarkTheme()
    else -> setLightTheme()
}

Integer and double flags

For numeric flags, use resolveIntValue(key, defaultValue) or resolveDoubleValue(key, defaultValue). These are appropriate when a feature depends on a numeric parameter such as a limit, percentage, or multiplier:

val maxItems = flagsClient.resolveIntValue(
    flagKey = "cart.items.max",
    defaultValue = 20
)

val priceMultiplier = flagsClient.resolveDoubleValue(
    flagKey = "pricing.multiplier",
    defaultValue = 1.0
)

Object flags

For structured or JSON-like data, use resolveStructureValue(key, defaultValue). This method returns a JSONObject, which can represent complex nested data. Object flags are useful for remote configuration scenarios where multiple properties need to be provided together. For example:

import org.json.JSONObject

val config = flagsClient.resolveStructureValue(
    flagKey = "ui.config",
    defaultValue = JSONObject().apply {
        put("color", "#00A3FF")
        put("fontSize", 14)
    }
)

Flag evaluation details

When you need more than just the flag value, use the resolve(key, defaultValue): ResolutionDetails<T> method. These methods return both the evaluated value and metadata explaining the evaluation. For example:

val details = flagsClient.resolve(
    flagKey = "paywall.layout",
    defaultValue = "control"
)

print(details.value)      // Evaluated value (for example: "A", "B", or "control")
print(details.variant)    // Variant name, if applicable
print(details.reason)     // Description of why this value was chosen (for example: "TARGETING_MATCH" or "DEFAULT")
print(details.errorCode)  // The error that occurred during evaluation, if any

Flag details may help you debug evaluation behavior and understand why a user received a given value.

Advanced configuration

The Flags.enable() API accepts optional configuration with options listed below.

val config = FlagsConfiguration.Builder()
    // configure options here
    .build()

Flags.enable(config)
trackExposures()
When true (default), the SDK automatically records an exposure event when a flag is evaluated. These events contain metadata about which flag was accessed, which variant was served, and under what context. They are sent to Datadog so you can later analyze feature adoption. If you only need local evaluation without telemetry, you can disable it with: trackExposures(false).
rumIntegrationEnabled()
When true (default), flag evaluations are tracked in RUM, which enables correlating them with user sessions. This enables analytics such as “Do users in variant B experience more errors?”. If your app does not use RUM, this flag has no effect and can be safely left at its default value. Use rumIntegrationEnabled(false) to disable RUM integration.
gracefulModeEnabled()
Controls how the SDK handles incorrect use of the FlagsClient API—for example, creating a client before calling Flags.enable(), creating a duplicate client with the same name, or retrieving a client that hasn’t been created yet.

The exact behavior of Graceful Mode depends on your build configuration:

  • Release builds: The SDK always enforces Graceful Mode: any misuse is only logged internally if Datadog.setVerbosity() is configured.
  • Debug builds with gracefulModeEnabled = true (default): The SDK always logs warnings to the console.
  • Debug builds with gracefulModeEnabled = false: The SDK raises IllegalStateException for incorrect API usage, enforcing a fail-fast approach that helps detect configuration mistakes early.

You can adjust gracefulModeEnabled() depending on your development or QA phase.

useCustomFlagEndpoint()
Configures a custom server URL for retrieving flag assignments.
useCustomExposureEndpoint()
Configures a custom server URL for sending flags exposure data.

Further reading

お役に立つドキュメント、リンクや記事: