Skip to content

Initialization

Ahmed Abbas edited this page Aug 7, 2026 · 3 revisions

Initialization

ConvertSDK has no public constructor. Every instance is built through ConvertSDK.builder(context).build(). Create exactly one instance per process — ideally from Application.onCreate() — because the SDK owns a long-lived coroutine scope, a durable on-disk event queue, a WorkManager registration, and network observers. Multiple instances waste resources and can produce duplicate tracking events.

The Builder

import com.convert.sdk.android.ConvertSDK
import com.convert.sdk.core.model.LogLevel

val sdk = ConvertSDK.builder(applicationContext)
    .sdkKey("YOUR_SDK_KEY")
    .logLevel(LogLevel.INFO) // optional
    .build()

ConvertSDK.builder(context) immediately reduces the passed context to its application context, so the SDK never retains an Activity or Fragment. You can safely pass an Activity — only the application context is kept.

Every builder setter is optional except sdkKey(...) or data(...). See Configuration Options for the full list.

Direct-data mode (pre-fetched config)

If you already have a config blob — shipped with your app bundle, fetched by a separate layer, or a test fixture — skip the network fetch with data(...):

import com.convert.sdk.core.model.generated.ConfigResponseData

val config: ConfigResponseData = loadBakedConfig() // your own loader
val sdk = ConvertSDK.builder(applicationContext)
    .data(config)
    .build()

Resolution rules when the builder finishes:

  • data(...) set → the SDK uses it and skips the initial network fetch.
  • Both sdkKey(...) and data(...) set → the SDK logs a WARN and prefers data (the pre-fetched config wins).
  • Neither set → every call becomes a no-op (runExperience returns null, trackConversion is a silent skip) and a WARN is logged. The builder never throws.

onReady { ... } — waiting for config

Config fetch is asynchronous. Until the first config loads, runExperience returns null and trackConversion silently skips. Gate your first interaction with onReady:

sdk.onReady {
    val ctx = sdk.createContext()
    val variation = ctx.runExperience("homepage-redesign")
    applyVariation(variation)
}

A callback registered after the SDK is already ready still runs — it is dispatched on the next coroutine tick, so there is no timing race. Callbacks dispatch on Dispatchers.Default; marshal back to the main thread inside the callback before touching UI.

onReady returns the SDK instance, so it chains fluently.

Ready fires once — later refreshes are a different event

onReady (and SystemEvents.READY) fires exactly once per SDK instance, on the first config seed — whichever source supplies it: direct-data mode, the initial network fetch, or the local config cache on an offline cold start. Every subsequent successful config load — each tick of the foreground refresh loop — fires SystemEvents.CONFIG_UPDATED instead, carrying a timestamp.

So put one-time bootstrap work in onReady, and anything that must re-run when the config changes (re-reading a feature flag, re-rendering a variation) behind CONFIG_UPDATED:

import com.convert.sdk.android.EventCallback
import com.convert.sdk.core.event.SystemEvents

sdk.onReady { /* runs once */ }

sdk.on(SystemEvents.CONFIG_UPDATED, EventCallback { data ->
    // A fresh config just landed — re-evaluate anything you cached.
})

Subscribing to other events

Beyond readiness, the SDK emits named events. Subscribe with on(event, callback), which returns a SubscriptionToken:

import com.convert.sdk.android.EventCallback
import com.convert.sdk.core.event.SystemEvents

val token = sdk.on(SystemEvents.BUCKETING, EventCallback { data ->
    // data carries the bucketing payload, e.g. experienceKey / variationKey / visitorId
})

// Later — unsubscribe with the token (preferred):
sdk.off(SystemEvents.BUCKETING, token)

Both the token form and the callback-identity form of off(...) work. Prefer the token form for new code. The SystemEvents constants are listed in Return Types & Models and explained in the shared Event System doc.

Creating a visitor context

A ConvertContext binds one visitor to the SDK. There are three ways to create one:

// 1) Auto-visitor — UUID v4 persisted in SharedPreferences, stable across launches.
val ctx = sdk.createContext()

// 2) Explicit visitor — you own the id (e.g. your login id).
val ctx = sdk.createContext("visitor-42")

// 3) Explicit visitor + initial attributes.
val ctx = sdk.createContext(
    visitorId = "visitor-42",
    attributes = mapOf("plan" to "premium", "accountAgeDays" to 120),
)

The explicit-id overloads neither read nor write the auto-UUID, so you can hold an anonymous context and a signed-in context in the same session. See the shared Visitor Context doc for the conceptual model, and Code Examples for full flows.

Next steps

Clone this wiki locally