Browse docs

Explore by section, then jump directly into a page.

Kotlin SDK

Integrate the FlashAnalytics Kotlin SDK in native Android apps.

Use the Kotlin SDK for native Android apps when you want app lifecycle, screen tracking, deep links, notification lifecycle, native crash capture, caught error reporting, and experiment assignment in a native integration.

Install

dependencies {
  implementation("app.flashanalytics:flashanalytics-kotlin:1.1.0")
}

Application setup

class MyApp : Application() {
  override fun onCreate() {
    super.onCreate()

    FlashAnalyticsAndroid.init(
      application = this,
      options = FlashAnalyticsOptions(
        appId = "YOUR_APP_ID",
        endpoint = "https://api.flashanalytics.app",
        captureAppLifecycle = true,
        captureScreenViews = true,
        captureDeepLinks = true,
        capturePushLifecycle = true,
        captureNativeCrashes = true,
        captureVariants = CaptureVariantsOptions(
          onAssignmentsChanged = { assignments ->
            // called whenever the local cache changes
          }
        ),
        defaultNotificationTtlMs = 300_000L,
      )
    )
  }
}

Quick start

val analytics = FlashAnalyticsAndroid.getInstance()

analytics.track("app_opened")

CoroutineScope(Dispatchers.Main).launch {
  val variant = analytics.assignExperiment("checkout-cta")
  val assignments = analytics.autoAssignExperiments()
}

analytics.assignExperimentAsync("checkout-cta") { result ->
  // handle result
}

Experiments

Enable captureVariants to keep a local cache of assignments in sync automatically. On each new session all experiments are fetched; after identify() only profile-mode assignments are refreshed; on session expiry only session-mode entries are removed.

// All cached assignments — no API call
val all = analytics.getAllExperiments()

// Single experiment — checks cache first, falls back to API if not found (suspend)
val assignment = analytics.getExperimentById("checkout-cta")
println(assignment?.variantName)

// Callback wrapper for non-coroutine code
analytics.getExperimentByIdAsync("checkout-cta") { result ->
  println(result?.variantName)
}

// Manual assignment (always calls the API)
CoroutineScope(Dispatchers.Main).launch {
  val variant = analytics.assignExperiment("checkout-cta")
  val assignments = analytics.autoAssignExperiments()
}

analytics.assignExperimentAsync("checkout-cta") { result ->
  // handle result
}

Auto capture

  • App lifecycle via captureAppLifecycle
  • Screen views via captureScreenViews
  • Deep links via captureDeepLinks
  • Native crashes via captureNativeCrashes or captureErrors
  • Experiment auto-assignment via captureVariants
  • Push lifecycle helpers via capturePushLifecycle

Manual error tracking

try {
  riskyOperation()
} catch (e: Exception) {
  FlashAnalyticsAndroid.getInstance().trackError(
    throwable = e,
    eventName = "payment_failed",
    properties = mapOf("orderId" to orderId),
  )
}

Notification lifecycle

The SDK supports delivered, opened, dismissed, action-clicked, and expired events. Android still needs receiver registration, notification intents, and payload forwarding from the host app.

val decision = analytics.handleIncomingNotificationLifecycle(
  values = message.data + mapOf(
    "notificationId" to notificationId,
    "messageId" to messageId,
    "provider" to "firebase",
  ),
  source = "android_local_notification",
)

if (decision.shouldDisplayNotification) {
  // build and show your notification
}
analytics.trackNotificationEvent(
  event = FlashNotificationEvent.OPENED,
  intent = intent,
  source = "android_intent",
  appState = "terminated",
  coldStart = true,
)

Session access

val session = analytics.getSession()
println(session?.id)
println(session?.estimatedExpiresAt)
println(session?.estimatedTtlMs)