Ir a la documentación

SDK de Android

El SDK de Kixo para Android es compatible con Kotlin 2.0+ y Java, requiere minSdk 24 (Android 7.0) y está compilado contra compileSdk 35. Tu app anfitriona sigue siendo responsable de su propio targetSdk. Con una sola llamada a Kixo.configure en tu Application.onCreate, se registran automáticamente pantallas, toques, sesiones, fallos y eventos del ciclo de vida. El seguimiento de push requiere el puente de FCM que se describe más abajo. El seguimiento automático de solicitudes de red no forma parte de la versión actual para Android. El SDK también admite repetición de sesiones, identidad y objetivos.

Inicio rápido

Tres archivos. Añade el repositorio Maven, añade la dependencia y pega dos líneas en tu subclase de Application.

Requisitos de compilación: compileSdk 35, minSdk 24, Kotlin 2.0+ o Java, y bytecode de Java 17.

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://raw.githubusercontent.com/kixoio/kixo-android-sdk/main/repo")
        }
    }
}

Nota

Con eso queda hecha toda la integración de analítica. Los auto-trackers estándar vienen activados por defecto; el push sigue necesitando el puente de FCM de más abajo. Ajusta cada opción con KixoConfiguration.Builder(...) solo cuando haga falta.

Añádelo a tu app

El repositorio Maven de Kixo está alojado en GitHub Pages. Añádelo junto a google() y mavenCentral() en settings.gradle.kts:

kotlin
// settings.gradle.kts
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://raw.githubusercontent.com/kixoio/kixo-android-sdk/main/repo")
        }
    }
}

Después, declara la dependencia en el módulo de tu app:

kotlin
// app/build.gradle.kts
dependencies {
    implementation("io.kixo:kixo-android-sdk:0.1.20")
}

Consejo

android.permission.INTERNET y android.permission.ACCESS_NETWORK_STATE vienen incluidos en el manifiesto del SDK. Los permisos de notificaciones siguen siendo responsabilidad de tu app y se declaran cuando activas las funciones de push.

Proyectos con varios módulos

La configuración implementation de Gradle es no transitiva: declarar implementation("io.kixo:kixo-android-sdk:0.1.20") en un módulo de biblioteca (por ejemplo, :core_domain) NO hace que Kixo sea visible para :app ni para ningún otro consumidor. Hay dos patrones válidos; elige uno.

Patrón A — cada módulo que llama a Kixo lo declara por su cuenta (recomendado). mantiene al mínimo el classpath de cada módulo y evita recompilaciones en cascada. Usa un catálogo de versiones (libs.kixo.sdk) para cambiar la versión en un solo sitio.

kotlin
// :core_domain/build.gradle.kts
dependencies {
    implementation("io.kixo:kixo-android-sdk:0.1.20")   // local use only
}

// :app/build.gradle.kts
dependencies {
    implementation(project(":core_domain"))
    implementation("io.kixo:kixo-android-sdk:0.1.20")   // declared again — fine
}

Patrón B — reexpórtalo mediante api(...). Es una única declaración, pero el ABI público del módulo de biblioteca pasa a incluir tipos de Kixo: cualquier cambio de versión obliga a recompilar todos los módulos dependientes. Úsalo solo cuando la biblioteca reutilice tipos de Kixo en sus propias firmas públicas, por ejemplo, si una función devuelve KixoDiagnostics.

kotlin
// :core_domain/build.gradle.kts
dependencies {
    api("io.kixo:kixo-android-sdk:0.1.20")              // re-exposed
}

// :app/build.gradle.kts
dependencies {
    implementation(project(":core_domain"))            // gets Kixo for free
}

Advertencia

Si al compilar un módulo aparece Unresolved reference: Kixo, a ese módulo le falta su propia dependencia del SDK: añade la línea implementation de arriba o usa el patrón B.

Inicializa

Configura Kixo desde tu subclase de Application: onCreate se ejecuta antes que cualquier activity, así que todas las pantallas, toques y eventos del ciclo de vida se capturan desde el primer fotograma. Registra Application en tu manifest con android:name=".MyApp".

kotlin
import android.app.Application
import io.kixo.sdk.Kixo

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        Kixo.configure(
            context   = this,
            projectId = "kx_proj_YOUR_PROJECT_ID",
            apiKey    = "kx_key_YOUR_API_KEY",
        )
    }
}

Si necesitas ajustar parámetros concretos —marcadores de seguimiento automático, cadencia de envío, muestreo de replay o un host de API personalizado— crea KixoConfiguration explícitamente:

kotlin
import io.kixo.sdk.Kixo
import io.kixo.sdk.KixoConfiguration

val config = KixoConfiguration.Builder(
    projectId = "kx_proj_YOUR_PROJECT_ID",
    apiKey    = "kx_key_YOUR_API_KEY",
)
    .autoTrackScreens(true)
    .autoTrackTaps(true)
    .autoTrackNetwork(false)  // reserved; no-op in the current Android release
    .autoTrackCrashes(true)
    .autoTrackSessions(true)
    .autoTrackPush(true)
    .flushIntervalMillis(30_000)
    .flushAt(20)
    .maxBufferSize(200)
    .build(applicationContext)

Kixo.configure(this, config)

Nota

Idempotente. Una segunda llamada a configure desde el mismo proceso no hace nada y se registra como WARN: el SDK conserva la primera configuración. Los eventos que tu singleton de autenticación deje en cola antes de que se complete antes configure se guardan en un búfer (máximo 50) y se reenvían cuando el SDK ya está inicializado, así que puedes llamar a Kixo.identify(...) desde un punto global antes de que termine Application.onCreate.

Registrar eventos

Tres primitivas cubren la mayor parte de la instrumentación: track para eventos, markGoal para señales de conversión y addBreadcrumb para contexto no asociado a eventos.

kotlin
import io.kixo.sdk.Kixo

Kixo.track("video_played", mapOf(
    "video_id"    to "vid_42",
    "duration_ms" to 18_500,
    "autoplay"    to false,
))

// markGoal(name, value?, currency?, properties?) — pass extra context
// through the named 'properties' argument (a Map can't be the 2nd
// positional arg; that slot is the Double 'value').
Kixo.markGoal("activated", properties = mapOf(
    "step" to "onboarding_completed",
))

// Revenue goals use the typed value + currency parameters:
Kixo.markGoal("purchase_completed", value = 49.99, currency = "USD")

Kixo.addBreadcrumb(
    message  = "user toggled dark mode",
    category = "ui",
    level    = "info",
)

Consejo

Los objetivos se califican. Los objetivos marcados alimentan los embudos de activación de Kixo y la tarea diaria de detección de cambios: si el volumen de un objetivo cae un 70 % frente a la semana anterior, aparecerá en tu panel con la insignia Pendiente de revisión. Usa markGoal para los pocos momentos que de verdad importan; track para todo lo demás.

Eventos estándar

Azúcar sintáctico sobre Kixo.track para los eventos que Kixo reconoce por nombre: claves de texto literal que el detector de eventos estándar del backend compara tal cual. Validación en compilación de la forma de las propiedades y una única fuente de verdad para los nombres.

kotlin
import io.kixo.sdk.Kixo
import io.kixo.sdk.SubscriptionInterval

Kixo.trackPurchase(
    amount    = 49.99,
    currency  = "USD",
    productId = "pro_yearly",
)

Kixo.trackSubscriptionStart(
    plan     = "pro",
    amount   = 9.99,
    currency = "USD",
    interval = SubscriptionInterval.MONTH,
)

Kixo.trackSignup(method = "google")
Kixo.trackTrialStart(plan = "pro", days = 14)
Kixo.trackCancel(plan = "pro", reason = "too_expensive")
Kixo.trackUpgrade(fromPlan = "free", toPlan = "pro")
Kixo.trackActivation(event = "first_post_published")
Kixo.trackShare(channel = "twitter", contentId = "post_123")
Kixo.trackInvite(channel = "email", recipientCount = 5)

Identificar usuarios

Vincula los eventos posteriores a un identificador de usuario estable y a un conjunto de atributos. La unión entre anónimo e identificado se resuelve en Kixo: los eventos capturados antes de identify se atribuyen retroactivamente al mismo usuario.

kotlin
import io.kixo.sdk.Kixo

// Reserved standard property keys carry a $-prefix (Mixpanel
// convention) so they namespace away from your own custom traits
// and promote to the dashboard's profile columns. See
// io.kixo.sdk.StandardProperty for the typed catalogue, or the
// "Standard property catalog" section below for the full 37-key list.
Kixo.identify("user_123", mapOf(
    "$email" to "jane@example.com",     // identity
    "$name"  to "Jane Doe",              // identity
    "$plan"  to "pro",                   // subscription pack
    "$lifetime_orders" to 12,            // e-commerce pack
    "signup_source" to "twitter_ad",     // custom trait
))

// Logout: clear identity, super-properties, and the persisted queue.
Kixo.reset()

⚠️ Cuidado con el signo de dólar en Kotlin. Las claves estándar de identidad llevan el prefijo $ ($email, $name, $first_name) y, en un literal de Kotlin, debe escapar el signo de dólar como "\$email". Si escribes "$email", Kotlin interpolará tu variable email, así que el valor acabará silenciosamente como trait personalizado y nunca rellenará las columnas de correo o nombre en Audience. La forma más simple de evitarlo es usar la sobrecarga tipada (SDK 0.1.13+), que no da margen de error: Kixo.setUserProperty(StandardProperty.EMAIL, email).

Etiquetar a un usuario para segmentarlo

Usa setUserProperty con un valor booleano para añadir al usuario una etiqueta sencilla de sí o no. La etiqueta se conserva entre lanzamientos y sirve para segmentos, campañas de correo y consultas en el chat, sin más configuración que la llamada al SDK.

kotlin
// Tag a user as subscribed — segments + campaigns can target this
Kixo.setUserProperty("subscribe", true)

// VIP membership
Kixo.setUserProperty("vip", true)

// String + numeric values work too
Kixo.setUserProperty("plan_tier", "enterprise")
Kixo.setUserProperty("lifetime_orders", 42)

// Bulk-set
Kixo.setUserProperties(mapOf(
    "subscribe" to true,
    "plan_tier" to "enterprise",
))

Las propiedades se guardan con SharedPreferences entre lanzamientos y se adjuntan automáticamente a todos los eventos salientes. En el chat puedes pedir cosas como "envía un correo de bienvenida a los usuarios cuyo subscribe sea true"; Kixo crea el segmento y te deja el borrador de la plantilla. Se borran con Kixo.reset().

Catálogo estándar de propiedades

Las claves de propiedad reservadas llevan el prefijo $ para separarlas de tus atributos personalizados. El catálogo de Kixo incluye 37 claves repartidas en 3 bloques universales (identidad, geografía y ciclo de vida) y 5 bloques verticales B2B (suscripción, comercio electrónico, medios, marketplace y fidelización). Define solo las que encajen con tu producto: el panel se adapta y muestra únicamente los bloques que hayas rellenado.

Identidad

Siempre relevante. Define las columnas de la cabecera del perfil.

ClaveTipoDescripción
$emailcadenaCorreo electrónico principal; suele usarse como clave de unión para consolidar identidades.
$phonecadenaNúmero de teléfono en formato E.164.
$namecadenaNombre completo para mostrar.
$first_namecadenaNombre.
$last_namecadenaApellidos.
$avatar_urlcadenaURL completa de la imagen de avatar del usuario.

Geolocalización

Contexto geográfico.

ClaveTipoDescripción
$countrycadenaCódigo de país ISO 3166.
$citycadenaNombre de la ciudad.
$regioncadenaEstado o provincia.
$timezonecadenaZona IANA como America/Los_Angeles.
$languagecadenaEtiqueta IETF como en o ru-RU.
$localecadenaIdentificador de configuración regional completo.

Ciclo de vida

Cuándo lo vimos.

ClaveTipoDescripción
$createdISO8601Fecha y hora de registro o creación de la cuenta.
$last_seenISO8601Última interacción.

Suscripción

Úsalo si tu producto tiene planes.

ClaveTipoDescripción
$plancadenaSlug del nivel: free, pro, enterprise.
$subscription_statuscadenaactive / trial / cancelled / past_due.
$trial_endsISO8601Cuándo termina la prueba actual.
$mrrnúmeroIngresos recurrentes mensuales en la divisa de la cuenta.
$subscription_startedISO8601Cuándo empezó la suscripción actual.

Comercio electrónico

Úsalo si vendes productos.

ClaveTipoDescripción
$lifetime_ordersnúmeroNúmero de pedidos completados.
$lifetime_revenuenúmeroGasto total.
$aovnúmeroValor medio del pedido.
$last_purchaseISO8601Última compra completada correctamente.
$first_purchaseISO8601Primera compra completada con éxito.
$cart_abandoned_countnúmeroNúmero total de abandonos de carrito.

Medios

Úsalo si publicas contenido.

ClaveTipoDescripción
$content_tiercadenafree / premium / paid.
$subscribed_categoriesCadena CSV o listaCategorías que sigue el usuario.
$watch_time_totalnúmeroTiempo total de visualización en segundos.
$last_playedISO8601Último inicio de reproducción.

Marketplace

Úsalo si tu producto es una plataforma de dos caras.

ClaveTipoDescripción
$seller_tiercadenaSlug del nivel del vendedor.
$buyer_tiercadenaSlug del nivel del comprador.
$listings_countnúmeroAnuncios activos del usuario.
$reviews_countnúmeroReseñas recibidas por el usuario.
$verifiedbooleanoEstado de KYC.

Fidelización

Úsalo para programas de fidelización y recompensas.

ClaveTipoDescripción
$loyalty_pointsnúmeroSaldo actual de puntos canjeables.
$vip_levelcadenaSlug del nivel VIP.
$referral_countnúmeroReferencias correctas atribuidas a este usuario.

Consejo

¿No aparece tu caso? Usa claves sin prefijo para los atributos personalizados. Se muestran en el panel de atributos personalizados del dashboard sin llenar de ruido las columnas de perfil. Los 5 bloques verticales anteriores son propuestas para las estructuras B2B más habituales; la terminología específica de cada cliente (por ejemplo, shipping_plan) se deja sin prefijo.

Superpropiedades

Pares clave-valor por sesión que se adjuntan automáticamente a todos los eventos salientes. A diferencia de los atributos de identify (que describen la identidad), las superpropiedades describen el contexto de la sesión: variante A/B activa, flavor de compilación o feature flags activadas. Se conservan entre lanzamientos y se borran con reset(). Si hay conflicto, las properties del evento en track siempre tienen prioridad.

kotlin
import io.kixo.sdk.Kixo

Kixo.setSuperProperty("build_flavor", "beta")
Kixo.setSuperProperties(mapOf(
    "ab_variant"        to "B",
    "referrer_campaign" to "autumn-launch",
))

// Sugar for A/B tracking — stored as 'experiment_<id>'.
Kixo.setExperimentVariant("checkout_v2", "variant_a")

Kixo.unsetSuperProperty("build_flavor")
Kixo.clearSuperProperties()

Notificaciones push

Hay dos vías de integración. Elige la A si usas FCM y quieres la configuración funcional más corta; elige la B si ya tienes un FirebaseMessagingService personalizado que no puedes reorganizar o si quieres controlar explícitamente qué entregas de FCM ve Kixo.

Opción A — hereda de KixoFirebaseMessagingService (seguimiento automático)

Crea una subclase de KixoFirebaseMessagingService y llama a super.onMessageReceived(...) desde tu override: Kixo emite automáticamente push_received (payload visible) o push_silent (solo datos). La clase base también gestiona el registro de onNewToken si no lo sobrescribes. El registro de AndroidManifest.xml no cambia respecto al de un servicio FCM normal.

kotlin
import com.google.firebase.messaging.RemoteMessage
import io.kixo.sdk.KixoFirebaseMessagingService

class MyMessagingService : KixoFirebaseMessagingService() {
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        super.onMessageReceived(remoteMessage)  // Kixo auto-tracks push_received
        // … your own routing / notification display
    }
}

Nota

Kixo compila esta clase opcional contra Firebase Messaging, pero no añade Firebase a tu app de forma transitiva. El SDK declara Firebase como compileOnly; si eliges esta opción, tu app debe depender ya de firebase-messaging, como cualquier receiver de FCM.

Opción B — llama a la API manualmente desde tu propio servicio FCM

Registra tu token de FCM en Kixo mediante FirebaseMessagingService.onNewToken y, después, registra cada entrega de forma explícita. Usa esta vía si quieres que Kixo solo vea una parte de las entregas de FCM. Por ahora, la entrega en Android solo es compatible con FCM.

kotlin
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import io.kixo.sdk.Kixo
import io.kixo.sdk.PushProvider

class MyMessagingService : FirebaseMessagingService() {
    override fun onNewToken(token: String) {
        Kixo.setPushToken(token, PushProvider.FCM)
    }

    override fun onMessageReceived(message: RemoteMessage) {
        // Convert the FCM payload to a Map<String, Any?> and log it —
        // Kixo correlates this with the open / dismiss it sees later.
        Kixo.logPushReceived(message.data.toMap(), appState = "background")
    }
}

Android no expone un punto de enganche universal del ciclo de vida para aperturas, descartes ni botones de acción de las notificaciones. Reenvía esas señales desde los intents o receivers de notificaciones que cree tu app:

kotlin
import io.kixo.sdk.Kixo

Kixo.logPushOpened(payload = pushPayload)            // open
Kixo.logPushOpened(payload = pushPayload, actionId = "reply")  // action-button tap
Kixo.logPushDismissed(payload = pushPayload)         // swipe-away

Replay de sesiones

Reproduce una reconstrucción visual real de lo que vio la persona usuaria. En cada captura, el SDK codifica un fotograma comprimido de la pantalla (una imagen JPEG) junto con una instantánea estructural de la jerarquía de vistas y sube ambos, para que el reproductor del panel pueda mostrar una reproducción fiel al píxel junto a la cronología de interacción. Configura la repetición de sesiones del proyecto en Dashboard → Ajustes → Reproducción de sesiones. El SDK lee y actualiza automáticamente esa política del proyecto, incluido el enmascaramiento, los modos de captura y el permiso para subir por red móvil.

kotlin
import io.kixo.sdk.Kixo
import io.kixo.sdk.KixoConfiguration

val config = KixoConfiguration.Builder(
    projectId = "kx_proj_YOUR_PROJECT_ID",
    apiKey    = "kx_key_YOUR_API_KEY",
)
    .build(applicationContext)

Kixo.configure(this, config)

Si la subida por red móvil está desactivada, la repetición de sesiones en cola espera a una red permitida.

Consejo

Enmascara antes de subir. Kixo captura píxeles, así que el enmascaramiento se aplica antes de que nada salga del dispositivo. Los campos de contraseña y correo electrónico se detectan y se ocultan automáticamente; el texto de las instantáneas estructurales pasa por un filtro de PII; y cualquier vista marcada con setKixoMask(true) se rasteriza como un rectángulo opaco en el fotograma antes de codificar el JPEG: sus píxeles nunca salen del dispositivo. Las pantallas de Jetpack Compose se enmascaran por completo por defecto (llama a setKixoMask(false) en el ComposeView más externo para incluir una pantalla que ya hayas auditado). Los operadores revisan el reproductor de Replay junto con la cronología de eventos en el panel.

Recopilación de datos

El SDK captura los datos activados en tu proyecto, además de los eventos y las propiedades que envía tu aplicación.

Depuración

Kixo.diagnostics() devuelve una instantánea de solo lectura del estado del SDK, útil en una pantalla de depuración oculta o en una prueba de humo. Responde a «¿por qué no están llegando mis eventos?» sin necesidad de abrir el depurador.

kotlin
import io.kixo.sdk.Kixo

val diag = Kixo.diagnostics()
Log.d("Kixo", "queued=${diag.queue.bufferedEventCount}")
Log.d("Kixo", "paused=${diag.paused}")                  // collection paused state
Log.d("Kixo", "lifecycleState=${diag.lifecycleState}")  // SDK lifecycle state

Fuerza un envío inmediato desde tu entorno de pruebas: bloquea hasta timeoutMs mientras espera una ida y vuelta de red:

kotlin
import io.kixo.sdk.Kixo

// Async fire-and-forget — returns immediately.
Kixo.flush()

// Blocking variant for instrumentation tests. Never call on the main thread.
val landed: Boolean = Kixo.flushBlocking(timeoutMs = 5_000L)
assertTrue(landed)

Compose Navigation

Las rutas de Activity / Fragment generan al instante eventos screen_view y registros estructurados de screen_visit, con metadatos de permanencia y flujo, sin configuración adicional. En Jetpack Compose Navigation, lanza Kixo.screen desde un LaunchedEffect asociado a la ruta: así el SDK verá un evento por destino, independientemente del número de recomposiciones.

kotlin
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import io.kixo.sdk.Kixo

@Composable
fun AppNavHost(nav: NavHostController) {
    NavHost(navController = nav, startDestination = "home") {
        composable("home") {
            LaunchedEffect("home") { Kixo.screen("HomeScreen") }
            HomeScreen()
        }
        composable("settings") {
            LaunchedEffect("settings") { Kixo.screen("SettingsScreen") }
            SettingsScreen()
        }
    }
}

Agentes de programación con AI

La superficie pública del SDK es pequeña y está pensada para el autocompletado: todos los métodos cuelgan del singleton Kixo, todos los ejemplos de Kotlin de esta guía empiezan con import io.kixo.sdk.Kixo, y nuestro README incluye un bloque "AI agent quick reference" que herramientas como Claude Code, Cursor y Codex pueden pegar directamente en su contexto. Si tu agente se atasca, este es el punto de partida canónico:

kotlin
// Tell your AI coding agent:
// "Integrate the Kixo Android SDK using io.kixo:kixo-android-sdk
//  from https://raw.githubusercontent.com/kixoio/kixo-android-sdk/main/repo.
//  Call Kixo.configure(this, projectId, apiKey) in Application.onCreate.
//  Then use Kixo.track / Kixo.identify / Kixo.markGoal as needed."

Nota

Todas las secciones anteriores están pensadas para ese flujo: los imports siempre son explícitos, los tipos siempre se nombran y el singleton del SDK nunca usa alias. Pásale esta página a tu agente y deja que trabaje.