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 SQLite offline 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.
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 2 days ago