Initialization
ConvertSDK.builder(), onReady(), direct-data mode, event subscription
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(...)anddata(...)set → the SDK logs a WARN and prefersdata(the pre-fetched config wins). - Neither set → every call becomes a no-op (
runExperiencereturnsnull,trackConversionis a silent skip) and a WARN is logged. The builder never throws.
onReady { ... } — waiting for config
onReady { ... } — waiting for configConfig 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
- Configuration Options — every builder option and its default
- Return Types & Models — what
runExperience/runFeaturereturn - Code Examples — end-to-end snippets
Updated 28 days ago