Este producto no es compatible con el sitio Datadog seleccionado. ().

Descripción general

Esta página describe cómo instrumentar su aplicación Android o Android TV con el SDK de Feature Flags de Datadog. Las Feature Flags de Datadog proporcionan una forma unificada de controlar de forma remota la disponibilidad de funciones en su aplicación, experimentar de forma segura y ofrecer nuevas experiencias con confianza.

El SDK de Feature Flags de Datadog para Android está construido sobre OpenFeature, un estándar abierto para la gestión de Feature Flags. Esta guía explica cómo instalar el SDK, configurar el proveedor de Datadog y evaluar Feature Flags en su aplicación.

Para la mayoría de las aplicaciones, la API de OpenFeature es el enfoque recomendado. Si necesita múltiples contextos de evaluación independientes en la misma aplicación, consulte Integración directa de FlagsClient.

Primeros pasos

Aquí tiene un ejemplo mínimo para que los Feature Flags funcionen en su aplicación Android:

// 1. Add dependencies (see Installation section)

// 2. Initialize the Datadog Android SDK (in Application.onCreate)
val configuration = Configuration.Builder(
    clientToken = "<CLIENT_TOKEN>",
    env = "<ENV_NAME>",
    variant = "<APP_VARIANT_NAME>"
)
    .useSite(DatadogSite.)
    .build()
Datadog.initialize(this, configuration, TrackingConsent.GRANTED)

// 3. Enable Feature Flags with a bounded initialization timeout
Flags.enable(
    FlagsConfiguration.Builder()
        .initializationTimeout(2_000L)
        .build()
)

// 4. Create and set up the OpenFeature provider
val provider = FlagsClient.Builder().build().asOpenFeatureProvider()
OpenFeatureAPI.setProviderAndWait(provider)

// 5. Set evaluation context (who is the user)
OpenFeatureAPI.setEvaluationContext(
    ImmutableContext(
        targetingKey = "user-123",
        attributes = mapOf("tier" to Value.String("premium"))
    )
)

// 6. Evaluate flags anywhere in your app
val client = OpenFeatureAPI.getClient()
val isEnabled = client.getBooleanValue("my-feature", false)

El resto de esta guía explica cada paso en detalle.

Instalación

Agregue el SDK de Feature Flags de Datadog y el proveedor de OpenFeature como dependencias de Gradle en el archivo build.gradle de su módulo de aplicación:

build.gradle

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

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

Inicializar el SDK

Inicialice Datadog lo antes posible en el ciclo de vida de su aplicación, normalmente en el método onCreate() de su clase Application. Esto ayuda a garantizar que todas las evaluaciones de Feature Flags y la telemetría se capturen correctamente. Para crear un token de cliente, consulte Client tokens.

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

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

Habilitar marcadores

Después de inicializar Datadog, habilite Flags para adjuntarlo a la instancia actual del SDK de Datadog para Android y prepararse para la creación del proveedor y la evaluación de Feature Flags:

import com.datadog.android.flags.Flags
Flags.enable()

También puede pasar un objeto de configuración; consulte Configuración avanzada.

Cree y configure el proveedor

Cree un FlagsClient y conviértalo en un proveedor de OpenFeature utilizando la extensión asOpenFeatureProvider(). Haga esto una vez durante el inicio de la aplicación:

import com.datadog.android.flags.FlagsClient
import com.datadog.android.flags.openfeature.asOpenFeatureProvider
import dev.openfeature.kotlin.sdk.OpenFeatureAPI

// Create and configure the provider
val provider = FlagsClient.Builder().build().asOpenFeatureProvider()

// Set it as the OpenFeature provider
OpenFeatureAPI.setProviderAndWait(provider)
El proveedor de OpenFeature envuelve un Datadog FlagsClient internamente. Este es un detalle de implementación: una vez configurado, usted interactúa exclusivamente a través de la API estándar de OpenFeature.
El SDK de OpenFeature para Kotlin utiliza un único proveedor global y un contexto de evaluación. Si necesita múltiples contextos de evaluación independientes en la misma aplicación (por ejemplo, para diferentes usuarios en una aplicación multiusuario), consulte Integración directa de FlagsClient.

Establezca el contexto de evaluación

Defina a quién o a qué se aplica la evaluación de Feature Flags mediante un ImmutableContext. El contexto de evaluación incluye información del usuario o de la sesión utilizada para determinar qué variaciones de los marcadores deben devolverse. Establezca esto antes de evaluar los Feature Flags para ayudar a garantizar una segmentación adecuada.

Datadog Feature Flags requiere que los atributos del contexto de evaluación sean valores primitivos planos: cadenas, números y booleanos. No pase objetos o arreglos anidados; no son compatibles y pueden causar que los datos de exposición se descarten.
import dev.openfeature.kotlin.sdk.ImmutableContext
import dev.openfeature.kotlin.sdk.Value

OpenFeatureAPI.setEvaluationContext(
    ImmutableContext(
        targetingKey = "user-123",
        attributes = mapOf(
            "email" to Value.String("user@example.com"),
            "tier" to Value.String("premium")
        )
    )
)
Los atributos de OpenFeature deben usar planos Value primitivos como Value.String(), Value.Integer(), Value.Double(), o Value.Boolean(). La clave de segmentación debe ser consistente para el mismo usuario para ayudar a garantizar una evaluación de Feature Flags consistente entre sesiones. Para usuarios anónimos, utilice un UUID persistente almacenado, por ejemplo, en SharedPreferences.

Evalúe marcadores

Después de configurar su proveedor y contexto de evaluación, puede leer los valores de los Feature Flags en toda su aplicación. La evaluación de marcadores es local e instantánea: el SDK utiliza datos almacenados en caché localmente, por lo que no se producen solicitudes de red al evaluar marcadores. Esto hace que las evaluaciones sean seguras para realizarse en el hilo principal.

Cada Feature Flag se identifica mediante una clave (una cadena única) y se puede evaluar con un método tipado que devuelve un valor del tipo esperado. Si el marcador no existe o no se puede evaluar, el SDK devuelve el valor predeterminado proporcionado.

Primero, obtenga un cliente de OpenFeature:

import dev.openfeature.kotlin.sdk.OpenFeatureAPI

val client = OpenFeatureAPI.getClient()

Marcadores booleanos

Los Feature Flags booleanos representan condiciones de encendido/apagado o verdadero/falso:

val isNewCheckoutEnabled = client.getBooleanValue(
    key = "checkout.new",
    defaultValue = false
)

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

Marcadores de cadena

Los Feature Flags de cadena seleccionan entre múltiples variantes o cadenas de configuración:

val theme = client.getStringValue(
    key = "ui.theme",
    defaultValue = "light"
)

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

Marcadores de tipo entero y doble

Los Feature Flags numéricos son apropiados cuando una funcionalidad depende de un parámetro numérico como un límite, porcentaje o multiplicador:

val maxItems = client.getIntegerValue(
    key = "cart.items.max",
    defaultValue = 20
)

val priceMultiplier = client.getDoubleValue(
    key = "pricing.multiplier",
    defaultValue = 1.0
)

Feature Flags estructurados

Los Feature Flags estructurados son útiles para escenarios de configuración remota donde se deben proporcionar múltiples propiedades juntas como datos tipo JSON:

import dev.openfeature.kotlin.sdk.Value

val config = client.getObjectValue(
    key = "ui.config",
    defaultValue = Value.Structure(mapOf(
        "color" to Value.String("#00A3FF"),
        "fontSize" to Value.Integer(14)
    ))
)

// Access nested values
val color = config.asStructure()?.get("color")?.asString()
val fontSize = config.asStructure()?.get("fontSize")?.asInteger()

Detalles de evaluación de marcadores

Cuando necesite más que el valor de un Feature Flag, puede obtener metadatos de evaluación detallados, incluyendo el valor evaluado, el nombre de la variante, el motivo y cualquier código de error:

val details = client.getStringDetails(
    key = "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)     // Reason for this value (for example: "TARGETING_MATCH" or "DEFAULT")
print(details.errorCode)  // Error code, if any

Existen métodos de detalle similares para otros tipos: getBooleanDetails(), getIntegerDetails(), getDoubleDetails() y getObjectDetails().

Los detalles del marcador le ayudan a depurar el comportamiento de evaluación y a entender por qué un usuario recibió un valor determinado.

Observe eventos del proveedor

La observación de eventos del proveedor está disponible en dd-sdk-android-flags-openfeature 3.6.0 y versiones posteriores. Utilice la misma versión para dd-sdk-android-flags.

Utilice OpenFeatureAPI.observe() para reaccionar a los cambios de estado del proveedor. El proveedor OpenFeature de Datadog emite ProviderReady, ProviderStale y ProviderError según el estado del FlagsClient subyacente.

import dev.openfeature.kotlin.sdk.OpenFeatureAPI
import dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.launch

val stateJob = lifecycleScope.launch {
    OpenFeatureAPI.observe<OpenFeatureProviderEvents>()
        .catch {
            // Handle Flow collection errors.
        }
        .collect { event ->
            when (event) {
                is OpenFeatureProviderEvents.ProviderReady -> {
                    // The provider is ready to evaluate flags.
                }
                is OpenFeatureProviderEvents.ProviderStale -> {
                    // Cached assignments are available, but they may be out of date.
                }
                is OpenFeatureProviderEvents.ProviderError -> {
                    // The provider cannot evaluate flags.
                }
                is OpenFeatureProviderEvents.ProviderConfigurationChanged -> {
                    // The provider configuration changed.
                }
                else -> {
                    // Handle other OpenFeature provider events as needed.
                }
            }
        }
}

Cancele el trabajo de recolección cuando el componente de observación se detenga. Por ejemplo, recolecte desde lifecycleScope en un Activity o Fragment de Android, o desde viewModelScope en un ViewModel.

Configuración avanzada

Configuración global

La API Flags.enable() acepta una configuración opcional con las opciones enumeradas a continuación. Estos ajustes se aplican globalmente a todos los proveedores:

val config = FlagsConfiguration.Builder()
    .initializationTimeout(2_000L)
    // configure additional options here
    .build()

Flags.enable(config)
initializationTimeout(timeoutMs)
Tiempo máximo, en milisegundos, para esperar a que el primer contexto de evaluación esté listo. El tiempo de espera cubre la carga de datos en caché, la obtención de asignaciones, la lectura y decodificación de la respuesta, el almacenamiento de asignaciones y la publicación del estado de listo. No cambia el tiempo de espera del cliente HTTP. La operación de asignación continúa después del tiempo de espera y puede mover al cliente a Ready cuando se completa.

El tiempo de espera se aplica solo a la primera llamada setEvaluationContext. Esa llamada consume el tiempo de espera incluso si la operación falla o nunca comienza. Las llamadas posteriores no tienen temporizador de inicialización. El valor predeterminado es 5_000 milisegundos. Establézcalo en cero o en un valor negativo para desactivar el tiempo de espera. Cuando el tiempo de espera expira, el cliente se vuelve Stale si hay asignaciones almacenadas en caché disponibles. De lo contrario, se vuelve Error.

initializationTimeout está disponible en dd-sdk-android-flags 3.14.0 y versiones posteriores.
trackExposures()
Cuando true (predeterminado), el SDK registra automáticamente un evento de exposición cuando se evalúa un marcador. Estos eventos contienen metadatos sobre qué marcador se accedió, qué variante se entregó y bajo qué contexto. Se envían a Datadog para que el usuario pueda analizar posteriormente la adopción de funciones. Si solo necesita una evaluación local sin telemetría, puede desactivarla con : trackExposures(false).
rumIntegrationEnabled()
Cuando true (predeterminado), las evaluaciones de marcadores se rastrean en RUM, lo que permite correlacionarlos con las sesiones de usuario. Esto permite análisis como “¿Los usuarios en la variante B experimentan más errores?”. Si su aplicación no utiliza RUM, este marcador no tiene efecto y puede dejarse de forma segura en su valor predeterminado. Use rumIntegrationEnabled(false) para desactivar la integración de RUM.
gracefulModeEnabled()
Controla cómo el SDK maneja el uso incorrecto de la API, por ejemplo, crear un cliente antes de llamar a Flags.enable(), crear un cliente duplicado con el mismo nombre o recuperar un cliente que aún no se ha creado.

El comportamiento exacto del Graceful Mode depende de su configuración de compilación:

  • Compilaciones de lanzamiento: El SDK siempre aplica Graceful Mode: cualquier uso indebido solo se registra internamente si Datadog.setVerbosity() está configurado.
  • Compilaciones de depuración con gracefulModeEnabled = true (predeterminado): El SDK siempre registra advertencias en la consola.
  • Compilaciones de depuración con gracefulModeEnabled = false: El SDK genera IllegalStateException por el uso incorrecto de la API, lo que aplica un enfoque de falla rápida que ayuda a detectar errores de configuración a tiempo.

Puede ajustar gracefulModeEnabled() según su fase de desarrollo o control de calidad.

Configuración por proveedor

Puede configurar proveedores individuales con endpoints personalizados antes de crearlos:

val provider = FlagsClient.Builder()
    .useCustomFlagEndpoint("https://your-proxy.example.com/flags")
    .useCustomExposureEndpoint("https://your-proxy.example.com/exposure")
    .useCustomEvaluationEndpoint("https://your-proxy.example.com/evaluations")
    .build()
    .asOpenFeatureProvider()

OpenFeatureAPI.setProviderAndWait(provider)

Integración de Direct FlagsClient (avanzado)

Para la mayoría de las aplicaciones, la API de OpenFeature descrita anteriormente es el enfoque recomendado. Sin embargo, puede usar el FlagsClient de Datadog directamente si tiene requisitos específicos que la abstracción de OpenFeature no admite.

Utilice FlagsClient directamente solo si usted:

  • Requiere múltiples contextos de evaluación independientes en la misma aplicación (por ejemplo, diferentes contextos para diferentes usuarios en una aplicación multiusuario)
  • Desea trabajar con tipos nativos de Kotlin directamente (JSONObject en lugar de Value.Structure)
  • Necesita control detallado sobre el ciclo de vida del cliente y la configuración por instancia

Instalación (FlagsClient)

Si solo necesita la API directa, puede omitir la dependencia de OpenFeature:

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>"
}

Cree y recupere un cliente (FlagsClient)

Cree un cliente una vez, normalmente durante el inicio de la aplicación:

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

Recupere el mismo cliente en cualquier parte de su aplicación:

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

También puede crear y recuperar múltiples clientes proporcionando el parámetro name:

FlagsClient.Builder("checkout").build()
val flagsClient = FlagsClient.get("checkout")
Si ya existe un cliente con el nombre proporcionado, se reutiliza la instancia existente.

Establezca el contexto de evaluación (FlagsClient)

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

Este método obtiene las asignaciones de los Feature Flags del servidor de forma asíncrona, en segundo plano. La operación no es bloqueante y es segura para subprocesos. Las actualizaciones de los Feature Flags están disponibles para evaluaciones posteriores después de que se complete la operación en segundo plano.

Observar cambios en el estado del cliente directo

Observación directa del estado del cliente con flagsClient.state está disponible en dd-sdk-android-flags 3.4.0 y versiones posteriores.

Utilice flagsClient.state para verificar el estado actual del cliente directo o registrar un oyente para los cambios de estado:

import com.datadog.android.flags.FlagsStateListener
import com.datadog.android.flags.model.FlagsClientState

val listener = object : FlagsStateListener {
    override fun onStateChanged(newState: FlagsClientState) {
        when (newState) {
            FlagsClientState.NotReady -> {
                // The client has not loaded assignments yet.
            }
            FlagsClientState.Reconciling -> {
                // The client is fetching assignments for a context change.
            }
            FlagsClientState.Ready -> {
                // Assignments are loaded and available for evaluation.
            }
            FlagsClientState.Stale -> {
                // Cached assignments are available, but the latest fetch failed.
            }
            is FlagsClientState.Error -> {
                // No assignments are available for evaluation.
            }
        }
    }
}

flagsClient.state.addListener(listener)

val currentState = flagsClient.state.getCurrentState()

El oyente recibe el estado actual cuando se registra, y luego recibe los cambios de estado futuros. Mantenga la devolución de llamada rápida y envíe el trabajo de larga duración a otro subproceso. Llame a flagsClient.state.removeListener(listener) cuando el componente de observación se detenga.

Evalúe los Feature Flags (FlagsClient)

val isNewCheckoutEnabled = flagsClient.resolveBooleanValue(
    flagKey = "checkout.new",
    defaultValue = false
)
val theme = flagsClient.resolveStringValue(
    flagKey = "ui.theme",
    defaultValue = "light"
)
val maxItems = flagsClient.resolveIntValue(
    flagKey = "cart.items.max",
    defaultValue = 20
)

val priceMultiplier = flagsClient.resolveDoubleValue(
    flagKey = "pricing.multiplier",
    defaultValue = 1.0
)
import org.json.JSONObject

val config = flagsClient.resolveStructureValue(
    flagKey = "ui.config",
    defaultValue = JSONObject().apply {
        put("color", "#00A3FF")
        put("fontSize", 14)
    }
)
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
print(details.errorCode)  // The error that occurred during evaluation, if any

Comparación de API

Esta tabla destaca las diferencias clave entre las API de OpenFeature y FlagsClient para ayudarle a elegir la integración que se ajuste a sus requisitos.

CaracterísticaOpenFeatureFlagsClient
Estándar de APIOpenFeature (neutral respecto al proveedor)Específico de Datadog
Contexto de evaluaciónGlobal/estáticoPor instancia de cliente
Feature Flags estructuradosValue.StructureJSONObject
Seguridad de tiposTipos de ValueOpenFeatureTipos nativos de Kotlin
Dependencia del proveedorBaja (neutral respecto al proveedor)Más alta (específica de Datadog)
Gestión de estadoObservación basada en flujoRegistro manual de oyentes

Pruebas

Puede realizar pruebas en un entorno de prueba de Datadog dedicado con el proveedor real de Datadog, o cambiarlo por un FeatureProvider en memoria para controlar directamente los valores de los marcadores en el código de prueba. Esta sección muestra el enfoque en memoria, que mantiene las pruebas herméticas y sin conexión. El SDK de Kotlin de OpenFeature upstream no incluye un InMemoryProvider, por lo que las pruebas utilizan un pequeño FeatureProvider personalizado. El siguiente ejemplo reemplaza el proveedor de OpenFeatureAPI: si su código de producción utiliza el wrapper FlagsClient de Datadog directamente, su prueba debe realizar aserciones a través del mismo cliente OpenFeatureAPI que utiliza el wrapper, no FlagsClient.

Agregue kotlinx-coroutines-test a su configuración de prueba (el initialize del SDK es una función suspend):

build.gradle

dependencies {
    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1'
}
import dev.openfeature.kotlin.sdk.*
import dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import kotlin.test.assertTrue

class FakeProvider(private val flags: Map<String, Any>) : FeatureProvider {
    override val hooks = emptyList<Hook<*>>()
    override val metadata = object : ProviderMetadata { override val name = "fake" }
    private val events = MutableSharedFlow<OpenFeatureProviderEvents>(replay = 1)

    override suspend fun initialize(initialContext: EvaluationContext?) {
        // No-op. The SDK emits ProviderReady after initialize returns.
    }
    override fun shutdown() {}
    override suspend fun onContextSet(old: EvaluationContext?, new: EvaluationContext) {}

    override fun getBooleanEvaluation(key: String, defaultValue: Boolean, context: EvaluationContext?) =
        ProviderEvaluation(value = (flags[key] as? Boolean) ?: defaultValue)
    override fun getStringEvaluation(key: String, defaultValue: String, context: EvaluationContext?) =
        ProviderEvaluation(value = (flags[key] as? String) ?: defaultValue)
    override fun getIntegerEvaluation(key: String, defaultValue: Int, context: EvaluationContext?) =
        ProviderEvaluation(value = (flags[key] as? Int) ?: defaultValue)
    override fun getDoubleEvaluation(key: String, defaultValue: Double, context: EvaluationContext?) =
        ProviderEvaluation(value = (flags[key] as? Double) ?: defaultValue)
    override fun getObjectEvaluation(key: String, defaultValue: Value, context: EvaluationContext?) =
        ProviderEvaluation(value = (flags[key] as? Value) ?: defaultValue)

    override fun observe(): Flow<OpenFeatureProviderEvents> = events
}

class CheckoutFlagsTest {
    private lateinit var client: Client

    @Before
    fun setUp() = runTest {
        OpenFeatureAPI.setProviderAndWait(
            FakeProvider(mapOf("new-checkout-flow" to true))
        )
        client = OpenFeatureAPI.getClient()
    }

    @Test
    fun newCheckoutEnabled() {
        assertTrue(client.getBooleanValue("new-checkout-flow", false))
    }
}

OpenFeatureAPI es un singleton de todo el proceso, así que reinícielo entre clases de prueba si las pruebas comparten una JVM. Envuelva setProviderAndWait en runTest { ... }: no se puede llamar desde un método @Before simple porque es suspend.

Lecturas adicionales