Android SDK
De Kixo Android SDK ondersteunt Kotlin 2.0+ en Java, vereist minSdk 24 (Android 7.0) en is gebouwd tegen compileSdk 35. Je hostapp blijft zelf verantwoordelijk voor zijn eigen targetSdk. Met één aanroep van Kixo.configure in je Application.onCreate registreer je automatisch schermen, taps, sessies, crashes en lifecycle-events. Voor pushtracking heb je de FCM-bridge hieronder nodig. Automatische tracking van netwerkverzoeken zit niet in de huidige Android-release. De SDK ondersteunt ook session replay, identity en goals.
Snel starten
Drie bestanden. Voeg de Maven-repository toe, voeg de dependency toe en zet daarna twee regels in je subklasse van Application.
Buildvereisten: compileSdk 35, minSdk 24, Kotlin 2.0+ of Java, en Java 17-bytecode.
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven {
url = uri("https://raw.githubusercontent.com/kixoio/kixo-android-sdk/main/repo")
}
}
}Opmerking
Dat is de volledige analyticsintegratie. De standaard auto-trackers staan al aan; voor push heb je nog wel de FCM-bridge hieronder nodig. Overschrijf losse flags alleen met KixoConfiguration.Builder(...) als dat nodig is.
Toevoegen aan je app
De Kixo Maven-repository wordt gehost op GitHub Pages. Voeg die in settings.gradle.kts toe naast google() en mavenCentral():
// 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")
}
}
}Declareer daarna de dependency in je appmodule:
// app/build.gradle.kts
dependencies {
implementation("io.kixo:kixo-android-sdk:0.1.20")
}Tip
android.permission.INTERNET en android.permission.ACCESS_NETWORK_STATE zijn opgenomen in het SDK-manifest. Rechten voor meldingen blijven van je app en declareer je pas als je pushfunctionaliteit inschakelt.
Projecten met meerdere modules
De implementation-configuratie van Gradle is niet transitief: als je implementation("io.kixo:kixo-android-sdk:0.1.20") in een librarymodule declareert (bijvoorbeeld :core_domain), wordt Kixo NIET zichtbaar voor :app of andere afnemers. Er zijn twee werkende patronen — kies er één.
Patroon A — elke module die Kixo aanroept, declareert het zelf (aanbevolen). Houdt het classpath van elke module beperkt en voorkomt een ketting van rebuilds. Gebruik een version catalog (libs.kixo.sdk), zodat je de versie maar op één plek hoeft te wijzigen.
// :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
}Patroon B — opnieuw exporteren via api(...). Eén declaratie, maar de publieke ABI van de librarymodule bevat nu ook Kixo-typen — elke versie-update triggert dan een rebuild van alle afhankelijke modules. Gebruik dit alleen als de library Kixo-typen hergebruikt in eigen publieke signatures, bijvoorbeeld door KixoDiagnostics uit een functie terug te geven.
// :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
}Waarschuwing
Zie je Unresolved reference: Kixo tijdens het compileren in een module, dan mist die module zijn eigen dependency op de SDK. Voeg de implementation-regel hierboven toe of gebruik patroon B.
Initialiseren
Configureer Kixo in je subklasse van Application — onCreate draait vóór elke activity, zodat elke schermweergave, tap en lifecycle-event vanaf het eerste frame wordt vastgelegd. Registreer de Application in je manifest met android:name=".MyApp".
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",
)
}
}Voor fijnmazige instellingen — zoals auto-track-flags, flush-interval, replay-sampling en een aangepaste API-host — maak je expliciet een KixoConfiguration:
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)Opmerking
Idempotent. Een tweede aanroep van configure vanuit hetzelfde proces is een als WARN gelogde no-op: de SDK houdt de eerste configuratie aan. Events die je auth-singleton in de wachtrij zet voordat voordat op configure uitkomt, worden gebufferd (tot 50) en alsnog verstuurd zodra de SDK is aangesloten. Daardoor kun je Kixo.identify(...) vanuit een global aanroepen voordat Application.onCreate klaar is.
Events registreren
Drie bouwstenen dragen het grootste deel van je instrumentatie: track voor events, markGoal voor conversiesignalen en addBreadcrumb voor context buiten events om.
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",
)Tip
Doelen krijgen een beoordeling. Gemarkeerde doelen voeden de activatiefunnels van Kixo en de dagelijkse cronjob voor wijzigingsdetectie. Daalt het volume van een doel week-op-week met 70%, dan verschijnt het in je dashboard met een Moet worden beoordeeld-badge. Gebruik markGoal voor de paar momenten die er echt toe doen; track voor al het overige.
Standaardevents
Een dunne laag boven op Kixo.track voor events die Kixo op naam herkent — letterlijke string-keys waarop de standaardeventdetector in de backend matcht. Inclusief validatie van de property-structuur tijdens compilatie en één centrale bron voor naamgeving.
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)Gebruikers identificeren
Koppel volgende events aan een stabiele gebruikers-ID en een set traits. De koppeling van anoniem naar bekend gebeurt in Kixo: events die vóór identify zijn vastgelegd, worden achteraf alsnog aan dezelfde gebruiker toegeschreven.
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()⚠️ Kotlin-valkuil met het dollarteken. Standaard identity-keys hebben het voorvoegsel $ ($email, $name, $first_name) — en in een Kotlin-literal moet je het dollarteken escapen als "\$email". Schrijf je "$email", dan interpoleert de string je email-variabele. Die waarde komt dan ongemerkt terecht als een aangepast-trait en vult de Audience-kolommen voor e-mail of naam nooit. De eenvoudigste oplossing is de typed overload (SDK 0.1.13+); die kun je niet verkeerd gebruiken: Kixo.setUserProperty(StandardProperty.EMAIL, email).
Geef een gebruiker een tag voor segmentatie
Gebruik setUserProperty met een boolean-waarde om een eenvoudige ja/nee-tag aan de gebruiker te hangen. Die tag blijft over appstarts heen bestaan en wordt gebruikt voor segmenten, e-mailcampagnes en chatquery's — zonder extra setup naast de SDK-aanroep.
// 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",
))Properties worden via SharedPreferences over appstarts heen bewaard en automatisch aan elk uitgaand event toegevoegd. Zeg in chat bijvoorbeeld "stuur een welkomstmail naar gebruikers waarvoor subscribe true is" — Kixo maakt dan het segment en een eerste versie van de template voor je. Gewist bij Kixo.reset().
Catalogus met standaardeigenschappen
Gereserveerde property-sleutels krijgen het voorvoegsel $, zodat ze gescheiden blijven van je eigen traits. De catalogus van Kixo bevat 37 sleutels in 3 universele packs (identity, geo, lifecycle) en 5 B2B-specifieke packs (subscription, e-commerce, media, marketplace, loyalty). Stel alleen in wat voor jouw product relevant is — het dashboard past zich aan en toont alleen de packs die je gebruikt.
Identiteit
Altijd relevant. Bepaalt de kolommen in de profielkop.
| Sleutel | Type | Beschrijving |
|---|---|---|
$email | tekenreeks | Primair e-mailadres, vaak de samenvoegsleutel voor identiteitskoppeling. |
$phone | tekenreeks | E.164-telefoonnummer. |
$name | tekenreeks | Volledige weergavenaam. |
$first_name | tekenreeks | Voornaam. |
$last_name | tekenreeks | Achternaam. |
$avatar_url | tekenreeks | Volledige URL van de avatarafbeelding van de gebruiker. |
Geo
Geografische context.
| Sleutel | Type | Beschrijving |
|---|---|---|
$country | tekenreeks | ISO 3166-landcode. |
$city | tekenreeks | Plaatsnaam. |
$region | tekenreeks | Staat of provincie. |
$timezone | tekenreeks | IANA-zone zoals America/Los_Angeles. |
$language | tekenreeks | IETF-tag zoals en of ru-RU. |
$locale | tekenreeks | Volledige locale-id. |
Levenscyclus
Wanneer hebben we deze gebruiker gezien?
| Sleutel | Type | Beschrijving |
|---|---|---|
$created | ISO8601 | Moment van registratie of accountaanmaak. |
$last_seen | ISO8601 | Tijdstip van de laatste interactie. |
Abonnement
Stel dit in als je product abonnementen heeft.
| Sleutel | Type | Beschrijving |
|---|---|---|
$plan | tekenreeks | Niveauslug — free, pro, enterprise. |
$subscription_status | tekenreeks | active / trial / cancelled / past_due. |
$trial_ends | ISO8601 | Wanneer de huidige proefperiode afloopt. |
$mrr | getal | Maandelijks terugkerende omzet in de accountvaluta. |
$subscription_started | ISO8601 | Wanneer het huidige abonnement is gestart. |
E-commerce
Stel dit in als je producten verkoopt.
| Sleutel | Type | Beschrijving |
|---|---|---|
$lifetime_orders | getal | Aantal voltooide bestellingen. |
$lifetime_revenue | getal | Totale bestedingen. |
$aov | getal | Gemiddelde bestelwaarde. |
$last_purchase | ISO8601 | Meest recente succesvolle aankoop. |
$first_purchase | ISO8601 | Eerste succesvolle aankoop. |
$cart_abandoned_count | getal | Totaal aantal achtergelaten winkelwagens. |
Media
Stel dit in als je content publiceert.
| Sleutel | Type | Beschrijving |
|---|---|---|
$content_tier | tekenreeks | free / premium / paid. |
$subscribed_categories | CSV-string of array | Categorieën die de gebruiker volgt. |
$watch_time_total | getal | Totale kijktijd in seconden. |
$last_played | ISO8601 | Meest recente start van afspelen. |
Marktplaats
Stel dit in als je een tweezijdig platform hebt.
| Sleutel | Type | Beschrijving |
|---|---|---|
$seller_tier | tekenreeks | Slug van het verkopersniveau. |
$buyer_tier | tekenreeks | Tier-slug aan de koperskant. |
$listings_count | getal | Actieve aanbiedingen van de gebruiker. |
$reviews_count | getal | Reviews die deze gebruiker heeft ontvangen. |
$verified | boolean | KYC-status. |
Loyaliteit
Stel dit in voor engagement- en beloningsprogramma’s.
| Sleutel | Type | Beschrijving |
|---|---|---|
$loyalty_points | getal | Huidig saldo aan inwisselbare punten. |
$vip_level | tekenreeks | Slug van het VIP-niveau. |
$referral_count | getal | Succesvolle doorverwijzingen die aan deze gebruiker zijn toegeschreven. |
Tip
Staat je patroon er niet tussen? Gebruik dan losse sleutels voor custom traits. Die verschijnen in het dashboardpaneel Custom Traits zonder de profielkolommen te vervuilen. De 5 verticale pakketten hierboven zijn gerichte aannames voor de meest voorkomende B2B-vormen — klantspecifieke termen (zoals shipping_plan) blijven zonder voorvoegsel.
Super-properties
Sleutel-waardeparen per sessie die automatisch aan elk uitgaand event worden toegevoegd. Anders dan identify-traits, die de identiteit beschrijven, leggen super-properties de sessiecontext vast — zoals de actieve A/B-variant, build flavor en ingeschakelde feature flags. Ze blijven over appstarts heen bewaard en worden gewist bij reset(). Bij naamconflicten krijgen properties per event op track altijd voorrang.
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()Pushmeldingen
Twee integratieroutes. Kies A als je FCM gebruikt en de kortste werkende setup wilt; kies B als je al een eigen FirebaseMessagingService hebt die je niet kunt herstructureren, of als je precies wilt bepalen welke FCM-afleveringen Kixo ziet.
Optie A — breid KixoFirebaseMessagingService uit (automatische tracking)
Breid KixoFirebaseMessagingService uit en roep super.onMessageReceived(...) aan vanuit je override — Kixo verstuurt dan automatisch push_received (zichtbare payload) of push_silent (alleen data). De basisklasse regelt ook de registratie van onNewToken, zolang je die niet overschrijft. De registratie van AndroidManifest.xml blijft hetzelfde als bij een gewone FCM-service.
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
}
}Opmerking
Kixo compileert deze optionele klasse tegen Firebase Messaging, maar voegt Firebase niet transitief toe aan je app. De SDK declareert Firebase als compileOnly; een app die deze optie gebruikt, moet dus al afhankelijk zijn van firebase-messaging, zoals elke FCM-receiver.
Optie B — roep de handmatige API aan vanuit je eigen FCM-service
Registreer je FCM-token bij Kixo via FirebaseMessagingService.onNewToken en log daarna elke aflevering expliciet. Gebruik deze route als je wilt dat Kixo alleen een deel van de FCM-afleveringen ziet. Op Android wordt aflevering momenteel alleen via FCM ondersteund.
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 biedt geen universele lifecycle-hook voor het openen, wegvegen of afhandelen van meldingen via actieknoppen. Geef die signalen daarom door vanuit de notification intents of receivers die je app zelf aanmaakt:
import io.kixo.sdk.Kixo
Kixo.logPushOpened(payload = pushPayload) // open
Kixo.logPushOpened(payload = pushPayload, actionId = "reply") // action-button tap
Kixo.logPushDismissed(payload = pushPayload) // swipe-awaySessie-replay
Speel een echte, visuele reconstructie af van wat de gebruiker zag. Bij elke vastlegging codeert de SDK een gecomprimeerd schermframe (een JPEG-afbeelding) samen met een structurele momentopname van de view-hiërarchie, en uploadt beide — zodat de speler in het dashboard de opname pixelnauwkeurig kan weergeven naast de interactietijdlijn. Configureer replay voor het project in Dashboard → Instellingen → Sessiereplay. De SDK leest en ververst dat projectbeleid automatisch, inclusief maskering, vastlegmodi en toestemming voor upload via mobiele data.
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)Als upload via mobiele data is uitgeschakeld, blijft replay in de wachtrij staan tot er een toegestaan netwerk beschikbaar is.
Tip
Maskeren vóór upload. Kixo legt pixels vast, dus maskering gebeurt voordat er iets het toestel verlaat. Wachtwoord- en e-mailvelden worden automatisch herkend en afgeschermd, tekst in structurele momentopnamen gaat door een PII-filter, en elke view die je markeert met setKixoMask(true) wordt in het frame gerasterd tot een ondoorzichtige rechthoek voordat de JPEG wordt gecodeerd — die pixels verlaten het toestel nooit. Schermen in Jetpack Compose worden standaard volledig gemaskeerd (roep setKixoMask(false) aan op de buitenste ComposeView om een scherm op te nemen dat je hebt beoordeeld). In het dashboard kunnen operators de replayspeler en de eventtijdlijn naast elkaar doorlopen.
Gegevensverzameling
De SDK legt de gegevens vast die in je project zijn ingeschakeld, plus de events en properties die je app verstuurt.
Debugging
Kixo.diagnostics() geeft een alleen-lezen momentopname van de status van de SDK terug — handig in een verborgen debugscherm of smoke test. Beantwoordt "waarom komen mijn events niet door?" zonder debugger.
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 stateForceer een flush vanuit je testharnas — blokkeert maximaal timeoutMs op een netwerk-roundtrip:
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
Routes van Activity en Fragment leveren direct screen_viewevents op, plus gestructureerde screen_visit-records met metadata over verblijfsduur en flow. Gebruik je Jetpack Compose Navigation, verstuur dan Kixo.screen vanuit een LaunchedEffect die aan de route is gekoppeld — de SDK ziet dan precies één event per bestemming, ongeacht het aantal recompositions.
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()
}
}
}AI-codeassistenten
De publieke API van de SDK is klein en opgezet voor code completion: elke methode staat op de Kixo-singleton, elk Kotlin-voorbeeld in deze gids begint met import io.kixo.sdk.Kixo, en onze README bevat een blok "AI agent quick reference" dat tools als Claude Code, Cursor en Codex direct in hun context kunnen plakken. Loopt je agent vast, begin dan hier:
// 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."Opmerking
Elke sectie hierboven is geschreven met die werkwijze in gedachten: imports zijn altijd expliciet, typen worden altijd voluit genoemd en de SDK-singleton krijgt nooit een alias. Geef deze pagina aan je agent en laat die het uitvoeren.