Перейти к документации

Android SDK

Kixo Android SDK поддерживает Kotlin 2.0+ и Java, требует minSdk 24 (Android 7.0) и собран под compileSdk 35. За свой targetSdk по-прежнему отвечает само приложение. Один вызов Kixo.configure в вашем Application.onCreate автоматически отслеживает экраны, нажатия, сессии, сбои и события жизненного цикла. Для отслеживания push-уведомлений нужен мост FCM, описанный ниже. Автоматического отслеживания сетевых запросов в текущем Android-релизе нет. SDK также поддерживает повторы сессий, идентификацию пользователей и цели.

Быстрый старт

Нужно изменить три файла: добавить Maven-репозиторий, добавить зависимость и вставить две строки в свой подкласс Application.

Требования к сборке: compileSdk 35, minSdk 24, Kotlin 2.0+ или Java, а также bytecode 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")
        }
    }
}

Примечание

На этом интеграция аналитики закончена. Стандартные автотрекеры включены по умолчанию; для push-уведомлений по-прежнему нужен мост FCM ниже. Переопределяйте отдельные флаги через KixoConfiguration.Builder(...) только при необходимости.

Добавьте в приложение

Maven-репозиторий Kixo размещён на GitHub Pages. Добавьте его рядом с google() и mavenCentral() в 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")
        }
    }
}

Затем объявите зависимость в модуле приложения:

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

Совет

android.permission.INTERNET и android.permission.ACCESS_NETWORK_STATE уже включены в манифест SDK. Разрешениями на уведомления по-прежнему управляет ваше приложение; они объявляются только при подключении push-функций.

Многомодульные проекты

Конфигурация Gradle implementationне транзитивна: если объявить implementation("io.kixo:kixo-android-sdk:0.1.20") в библиотечном модуле, например в :core_domain, зависимость Kixo НЕ станет доступна в :app или любом другом модуле-потребителе. Рабочих варианта два — выберите один.

Схема A — каждый модуль, который вызывает Kixo, объявляет зависимость сам (рекомендуется). Так classpath каждого модуля остаётся минимальным, а каскадных пересборок удаётся избежать. Используйте version catalog (libs.kixo.sdk), чтобы менять версию в одном месте.

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
}

Схема B — реэкспорт через api(...). Объявление короче, но в публичный ABI библиотечного модуля попадают типы Kixo — любое обновление версии приведёт к пересборке всех downstream-модулей. Используйте этот вариант только если библиотека сама использует типы Kixo в своих публичных сигнатурах, например возвращает 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
}

Предупреждение

Если при компиляции модуля появляется Unresolved reference: Kixo, значит, в этом модуле нет собственной зависимости на SDK — добавьте строку implementation выше или используйте вариант B.

Инициализация

Настраивайте Kixo в своём подклассе Application: onCreate выполняется раньше любой activity, поэтому просмотры экранов, нажатия и события жизненного цикла собираются с первого кадра. Зарегистрируйте Application в манифесте через 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",
        )
    }
}

Если нужна тонкая настройка — флаги автосбора, частота flush, сэмплирование replay, собственный хост API — создайте KixoConfiguration явно:

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)

Примечание

Идемпотентно. Повторный вызов configure в рамках того же процесса ничего не делает и пишет WARN в лог — SDK сохраняет первую конфигурацию. События, которые ваш auth singleton ставит в очередь до того, как сработает до configure, буферизуются (до 50 штук) и отправляются после инициализации SDK, поэтому Kixo.identify(...) можно вызывать из глобального объекта до завершения Application.onCreate.

Отслеживание событий

Основная часть инструментирования строится на трёх примитивах: track для событий, markGoal для сигналов конверсии и addBreadcrumb для контекста вне событий.

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",
)

Совет

У целей есть уровни важности. Отмеченные цели попадают в воронки активации Kixo и в ежедневный cron поиска изменений: если объём цели падает на 70% неделя к неделе, в дашборде она помечается бейджем Требует проверки. Используйте markGoal для нескольких действительно важных моментов, а track — для всего остального.

Стандартные события

Удобная обёртка над Kixo.track для событий, которые Kixo распознаёт по имени: это точные строковые ключи, с которыми работает детектор стандартных событий на бэкенде. Даёт проверку структуры свойств на этапе компиляции и единый источник истины для имён.

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)

Идентификация пользователей

Привяжите последующие события к постоянному user id и набору атрибутов. Склейка анонимного и известного пользователя происходит в Kixo: события, собранные до identify, задним числом будут отнесены к тому же пользователю.

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()

⚠️ Ловушка со знаком доллара в Kotlin. Стандартные ключи идентификации начинаются с $ ($email, $name, $first_name), а в строковом литерале Kotlin знак доллара нужно экранировать как "\$email". Если написать "$email", Kotlin подставит значение переменной email, и оно незаметно сохранится как атрибут произвольный, а колонки Audience email / name так и останутся пустыми. Проще всего использовать типизированную перегрузку (SDK 0.1.13+): там ошибиться невозможно — Kixo.setUserProperty(StandardProperty.EMAIL, email).

Пометить пользователя для сегментации

Используйте setUserProperty со значением булево значение, чтобы добавить пользователю простой тег «да/нет». Тег сохраняется между запусками и используется в сегментах, email-кампаниях и запросах в чате — кроме вызова 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",
))

Свойства сохраняются через SharedPreferences между запусками и автоматически добавляются ко всем исходящим событиям. В чате можно написать, например, "отправить приветственное письмо пользователям, у которых subscribe = true", — Kixo сам соберёт сегмент и подготовит черновик шаблона. Очищаются при Kixo.reset().

Каталог стандартных свойств

Зарезервированные ключи свойств используют префикс $, чтобы не пересекаться с вашими пользовательскими traits. В каталоге Kixo — 37 ключей: 3 универсальных набора (identity, geo, lifecycle) и 5 отраслевых наборов для B2B (subscription, e-commerce, media, marketplace, loyalty). Задавайте только те, что подходят вашему продукту, — в дашборде будут показаны только заполненные наборы.

Идентификация

Используется всегда. Задаёт колонки в заголовке профиля.

КлючТипОписание
$emailстрокаОсновной email; часто используется как ключ для объединения профилей.
$phoneстрокаНомер телефона в формате E.164.
$nameстрокаПолное отображаемое имя.
$first_nameстрокаИмя.
$last_nameстрокаФамилия.
$avatar_urlстрокаПолный URL аватара пользователя.

Геоданные

Географический контекст.

КлючТипОписание
$countryстрокаКод страны по ISO 3166.
$cityстрокаНазвание города.
$regionстрокаШтат или провинция.
$timezoneстрокаЧасовой пояс IANA, например America/Los_Angeles.
$languageстрокаТег IETF, например en или ru-RU.
$localeстрокаПолный идентификатор локали.

Жизненный цикл

Когда мы видели этого пользователя.

КлючТипОписание
$createdISO8601Время регистрации или создания аккаунта.
$last_seenISO8601Время последнего взаимодействия.

Подписка

Используйте, если в вашем продукте есть тарифы.

КлючТипОписание
$planстрокаSlug тарифа — free, pro, enterprise.
$subscription_statusстрокаactive / trial / cancelled / past_due.
$trial_endsISO8601Когда заканчивается текущий пробный период.
$mrrчислоЕжемесячная регулярная выручка в валюте аккаунта.
$subscription_startedISO8601Когда началась текущая подписка.

Электронная коммерция

Используйте, если продаёте товары.

КлючТипОписание
$lifetime_ordersчислоКоличество завершённых заказов.
$lifetime_revenueчислоОбщие расходы.
$aovчислоСредний чек.
$last_purchaseISO8601Последняя успешная покупка.
$first_purchaseISO8601Первая успешная покупка.
$cart_abandoned_countчислоОбщее число брошенных корзин.

Медиа

Используйте, если публикуете контент.

КлючТипОписание
$content_tierстрокаfree / premium / paid.
$subscribed_categoriesCSV-строка или массивКатегории, на которые подписан пользователь.
$watch_time_totalчислоОбщее время просмотра в секундах.
$last_playedISO8601Время последнего начала воспроизведения.

Маркетплейс

Используйте, если ваш продукт — двусторонняя платформа.

КлючТипОписание
$seller_tierстрокаSlug уровня на стороне продавца.
$buyer_tierстрокаSlug уровня на стороне покупателя.
$listings_countчислоАктивные объявления пользователя.
$reviews_countчислоОтзывы, полученные пользователем.
$verifiedбулево значениеСтатус KYC.

Лояльность

Используйте для программ вовлечения и вознаграждений.

КлючТипОписание
$loyalty_pointsчислоТекущий доступный баланс баллов.
$vip_levelстрокаSlug уровня VIP.
$referral_countчислоУспешные рефералы, засчитанные этому пользователю.

Совет

Не нашли подходящий вариант? Используйте обычные ключи для пользовательских traits. Они появятся в панели Custom Traits в дашборде и не будут засорять колонки профиля. Пять отраслевых наборов выше — это практичные заготовки для самых распространённых моделей B2B; терминология конкретного продукта, например shipping_plan, остаётся без префикса.

Super-properties

Пары ключ-значение на уровне сессии, которые автоматически добавляются ко всем исходящим событиям. В отличие от traits в identify, которые описывают личность пользователя, super-properties описывают контекст сессии: активный вариант A/B-теста, flavor сборки, включённые feature flag. Сохраняются между запусками и очищаются при reset(). Если ключи совпадают, приоритет всегда у properties в событии, отправленном через track.

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()

Push-уведомления

Есть два пути интеграции. Выберите A, если вы используете FCM и хотите как можно быстрее получить рабочую настройку. Выберите B, если у вас уже есть собственный FirebaseMessagingService, который нельзя перестроить, или если нужен явный контроль над тем, какие доставки FCM видит Kixo.

Вариант A — унаследоваться от KixoFirebaseMessagingService (автоотслеживание)

Унаследуйтесь от KixoFirebaseMessagingService и вызовите super.onMessageReceived(...) в своём override — Kixo автоматически отправит push_received для видимого уведомления или push_silent для data-only сообщения. Базовый класс также обрабатывает регистрацию onNewToken, если вы её не переопределяете. Регистрация AndroidManifest.xml не отличается от обычного сервиса FCM.

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

Примечание

Kixo компилирует этот необязательный класс с Firebase Messaging, но не подтягивает Firebase в приложение транзитивно. В SDK Firebase объявлен как compileOnly; если приложение выбирает этот вариант, зависимость от firebase-messaging у него уже должна быть — как и у любого FCM receiver.

Вариант B — вызывать API вручную из собственного FCM service

Зарегистрируйте FCM token в Kixo через FirebaseMessagingService.onNewToken, а затем явно логируйте каждую доставку. Этот вариант подходит, если Kixo должен видеть только часть доставок FCM. Сейчас на Android поддерживается доставка только через 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 не даёт универсального lifecycle-хука для открытий уведомлений, их закрытия и нажатий на кнопки действий. Передавайте эти сигналы из notification intent или receiver, которые создаёт ваше приложение:

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

Запись сессий

Повторяйте реальную визуальную картину того, что видел пользователь. При каждом захвате SDK кодирует сжатый кадр экрана (изображение JPEG) вместе со структурным снимком иерархии представлений и загружает оба артефакта — благодаря этому проигрыватель в дашборде может показывать попиксельно точное воспроизведение рядом с временной шкалой взаимодействий. Настройте replay для проекта в Панель управления → Настройки → Повтор сеанса. SDK автоматически получает и обновляет эту проектную политику, включая маскирование, режимы захвата и разрешение на загрузку по сотовой сети.

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)

Если загрузка по сотовой сети отключена, поставленные в очередь записи replay ждут подходящей сети.

Совет

Маскирование до отправки. Kixo записывает пиксели, поэтому маскирование выполняется до до того, как какие-либо данные покинут устройство. Поля пароля и e-mail определяются автоматически и скрываются; текст в структурных снимках проходит через фильтр PII; а любое представление, помеченное setKixoMask(true), превращается в непрозрачный прямоугольник в кадре до до кодирования в JPEG — его пиксели никогда не покидают устройство. Экраны Jetpack Compose по умолчанию маскируются целиком; вызовите setKixoMask(false) на внешнем ComposeView, чтобы включить экран, который вы уже проверили. В дашборде оператор видит повтор сессии рядом с временной шкалой событий.

Сбор данных

SDK собирает данные, включённые в проекте, а также события и свойства, которые отправляет ваше приложение.

Отладка

Kixo.diagnostics() возвращает снимок состояния SDK только для чтения — удобно для скрытого экрана отладки или smoke-теста. Помогает понять, почему события не уходят, без отладчика.

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

Чтобы принудительно выполнить flush из тестового окружения, вызовите его явно — метод может блокироваться на сетевом обмене до timeoutMs:

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

Маршруты Activity / Fragment сразу создают события screen_view и структурированные записи screen_visit с метаданными о времени на экране и переходах. Для Jetpack Compose Navigation вызывайте Kixo.screen из LaunchedEffect, привязанного к маршруту, — тогда SDK увидит одно событие на каждый destination, независимо от числа рекомпозиций.

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()
        }
    }
}

AI-агенты для разработки

Публичный API SDK небольшой и хорошо приспособлен для автодополнения: все методы доступны на синглтоне Kixo, каждый пример Kotlin в этом руководстве начинается с import io.kixo.sdk.Kixo, а в нашем README есть блок "AI agent quick reference", который такие инструменты, как Claude Code, Cursor и Codex, могут сразу вставить в свой контекст. Если ваш агент зашёл в тупик, начните с этого канонического примера:

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

Примечание

Все разделы выше написаны с расчётом на этот сценарий: импорты всегда указаны явно, типы всегда названы, а синглтон SDK нигде не скрыт за псевдонимом. Передайте эту страницу своему агенту — дальше он доведёт интеграцию сам.