Skip to content

JavaInterop

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

Java Interop

The Convert Android SDK is Kotlin-first but designed for clean use from Java. Static factories, default-argument overloads, and SAM-convertible callbacks mean Java callers need no Kotlin glue. This page shows the Java forms of the common calls; the Kotlin forms are in Code Examples.

Build the SDK

ConvertSDK.builder(context) is a @JvmStatic factory, callable directly from Java. Builder setters chain:

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

ConvertSDK sdk = ConvertSDK.builder(getApplicationContext())
    .sdkKey("YOUR_SDK_KEY")
    .logLevel(LogLevel.INFO)
    .build();

Wait for readiness

onReady takes a Runnable, so a Java lambda works verbatim:

sdk.onReady(() -> {
    ConvertContext ctx = sdk.createContext();
    Variation variation = ctx.runExperience("homepage-redesign");
    applyVariation(variation);
});

Run experiences and features

runExperience, runExperiences, and trackConversion carry @JvmOverloads, so the default-argument arities are generated for Java — you do not pass the optional parameters unless you need them:

// arity-1: enableTracking defaults to true
Variation v = ctx.runExperience("homepage-redesign");

// arity-2: suppress the tracking event
Variation quiet = ctx.runExperience("homepage-redesign", false);

// all experiences
java.util.List<Variation> all = ctx.runExperiences();

Feature feature = ctx.runFeature("checkout-v2");
if (feature != null && feature.getEnabled()) {
    enableCheckoutV2();
}

Read feature variables

The scalar accessors live on the FeatureExtensions static facade (@file:JvmName("FeatureExtensions")). Call them as static methods with the Feature as the first argument:

import com.convert.sdk.android.FeatureExtensions;

String color = FeatureExtensions.getString(feature, "ctaColor");
Integer limit = FeatureExtensions.getInt(feature, "maxItems");
Double price = FeatureExtensions.getDouble(feature, "price");
Boolean experimental = FeatureExtensions.getBoolean(feature, "experimental");

Each returns null when the key is absent or the value cannot be coerced. Alternatively, read feature.getVariables().get("key") and decode the JsonElement yourself.

Track a conversion

trackConversion has three generated arities — (key), (key, goalData), and (key, goalData, conversionSetting). The goal-key-only form is the common case:

ctx.trackConversion("signup-completed");

To attach goal data, build GoalData(GoalDataKey, JsonElement) values. Each value is a kotlinx.serialization.json.JsonElement; build primitives with the JsonPrimitive(...) factory functions from kotlinx.serialization.json, which Java reaches through the generated JsonElementKt facade:

import com.convert.sdk.core.model.GoalData;
import com.convert.sdk.core.model.GoalDataKey;
import kotlinx.serialization.json.JsonElement;
import kotlinx.serialization.json.JsonElementKt;
import java.util.List;

JsonElement amount = JsonElementKt.JsonPrimitive(49.99);
JsonElement txId = JsonElementKt.JsonPrimitive("tx-42");

List<GoalData> data = List.of(
    new GoalData(GoalDataKey.AMOUNT, amount),
    new GoalData(GoalDataKey.TRANSACTION_ID, txId)
);
ctx.trackConversion("purchase-completed", data);

If your codebase is mostly Java, a tiny Kotlin helper that returns a List<GoalData> is often the most readable way to assemble goal data.

Subscribe to events

EventCallback is a Kotlin fun interface, so Java lambdas SAM-convert to it automatically. on(...) returns a SubscriptionToken:

import com.convert.sdk.android.SubscriptionToken;
import com.convert.sdk.core.event.SystemEvents;

SubscriptionToken token = sdk.on(SystemEvents.BUCKETING, data -> handle(data));

// later
sdk.off(SystemEvents.BUCKETING, token);

Toggle tracking

sdk.setTrackingEnabled(false);
sdk.setTrackingEnabled(true);
boolean enabled = sdk.isTrackingEnabled();

The one call that needs Kotlin: setPreview

Every method above is directly Java-callable. ConvertContext.setPreview(...) is the exception: it is a Kotlin suspend function, so from Java it appears with a trailing Continuation parameter rather than the two-argument form Kotlin sees. Calling it from Java is not practical — wrap it in a tiny Kotlin bridge that your Java code can call with a completion callback:

// PreviewBridge.kt — Kotlin, called from Java.
package com.example.convert

import com.convert.sdk.android.ConvertContext
import com.convert.sdk.core.preview.PreviewParam
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

object PreviewBridge {
    /**
     * Parses a raw `convert_preview` value, applies it to [context], and
     * invokes [onApplied] once the preview target has resolved. Does
     * nothing when [raw] is malformed.
     */
    @JvmStatic
    fun apply(
        scope: CoroutineScope,
        context: ConvertContext,
        raw: String?,
        onApplied: Runnable,
    ) {
        val parsed = raw?.let { PreviewParam.parse(it) } ?: return
        scope.launch {
            context.setPreview(parsed.first, parsed.second)
            onApplied.run()
        }
    }
}
// Java caller
String raw = getIntent().getData() == null
    ? null
    : getIntent().getData().getQueryParameter("convert_preview");

PreviewBridge.apply(scope, previewCtx, raw, () -> render(previewCtx.runExperience("homepage-redesign")));

PreviewParam.parse itself is @JvmStatic and Java-friendly — it returns a kotlin.Pair<String, String>, read via getFirst() / getSecond(), or null on a malformed value. See Code Examples.

Quick reference

Kotlin Java
ConvertSDK.builder(ctx) ConvertSDK.builder(ctx) (@JvmStatic)
sdk.onReady { } sdk.onReady(() -> { }) (Runnable)
ctx.runExperience(key) ctx.runExperience(key) (@JvmOverloads)
feature.enabled feature.getEnabled()
feature.getString("k") FeatureExtensions.getString(feature, "k")
sdk.on(event) { } sdk.on(event, data -> { }) (SAM)
PreviewParam.parse(raw) PreviewParam.parse(raw)kotlin.Pair (@JvmStatic)
ctx.setPreview(e, v) suspend — wrap in a Kotlin bridge (above)

Related pages

Clone this wiki locally