Skip to main content
Supported on: Android SDK 1.0.33+
The experiments module assigns users to variants using a deterministic hash. No server call is needed — assignment is instant, works offline, and the same user always gets the same variant (as long as the user is in the experiment — see Who Is in an Experiment).

Two ways to run an experiment

  • Servable surface experiments — A/B test a whole paywall, onboarding flow, in-app message, or survey with no branching code. You design the variants in the Console and the SDK serves the right one automatically. Best for testing the content or design of a managed surface.
  • Code-level experiments — read the assigned variant yourself with getVariant or getExperimentConfig and branch in your own code. Best for feature flags, custom UI, or logic that isn’t a managed surface.

Servable Surface Experiments

Supported on: Android SDK 1.0.37+
Run an A/B test on a live paywall, onboarding flow, in-app message, or survey without changing your app code. In the Console, create an experiment on the surface: the control variant points at the live entity you already present, and the treatment variant carries an alternate configuration. When your app presents that surface the way it normally does, the SDK does the rest:
The SDK deterministically buckets the user and:
  • Treatment cohort → renders the treatment’s configuration in place of the live entity.
  • Control cohort (and users on older SDK versions) → renders the live entity unchanged.
Exposure is tracked automatically the first time the surface is presented to a user in the experiment, so conversions attribute to the experiment with no extra calls. A user outside the experiment’s targeting rules or traffic allocation sees the live entity and is not exposed (SDK 1.0.54+). The treatment configuration is not added to the live entity, so a user who isn’t bucketed into the treatment — or who is on an SDK version that predates this feature — never sees it. This works for every managed surface: paywalls, onboarding flows, in-app messages, and surveys. You only reach for the code-level API below when you’re experimenting on something that isn’t a managed surface.

Get Variant

experimentId is the experiment’s ID, not its name — copy it from the experiment’s page or the ID column of the experiments list in the Console; "paywall-test" above is a placeholder. Returns the variant ID set in the Console or null if the experiment is not in the config the SDK holds, is not running (paused, completed or archived), does not target Android, or the user is outside its targeting rules or traffic allocation (SDK 1.0.54+; see Who Is in an Experiment). The country rule reads the device region (Locale.getDefault(), then the rest of LocaleList), the minimum-version rule the app’s versionName, and “new users only” PackageInfo.firstInstallTime. The convenience method AppDNA.getExperimentVariant("paywall-test") is equivalent.
Always handle the null / else case. Experiments can be archived or stopped at any time from the Console.

Check a Specific Variant

Returns true only if the user is currently bucketed into the named variant. Useful as a guard before rendering variant-specific UI.

Get a Per-Variant Config Value

When an experiment carries a config payload, read individual keys with:
Returns Any? (string, number, boolean, list, or map depending on what the Console stored).

Exposure Tracking

The SDK records an experiment_exposure event automatically the first time it assigns a variant for a given experiment — on getVariant(), isInVariant(), getExperimentConfig() or a servable surface. Exposures are kept in memory, so after an app restart or AppDNA.reset() the next assignment is recorded again; a new session alone does not. Exposures are tracked as part of the standard analytics pipeline and surface in the Console experiment results. To inspect the active exposures (useful for debugging or attaching them to your own analytics events):
Returns List<ExposureEntry> where each entry has experimentId: String and variant: String.

Module Access

Module Methods

The top-level AppDNA object also exposes AppDNA.getExperimentVariant(experimentId), AppDNA.isInVariant(experimentId, variantId), and AppDNA.getExperimentConfig(experimentId, key) as convenience methods that delegate to this module.

Experiment Lifecycle

A status change reaches the device with its next config fetch; until then the SDK keeps the definition it has. See Experiment Lifecycle.

Full Example

Experiments are created in the Console under Experiments. The SDK uses MurmurHash3 for deterministic assignment — the same user ID always gets the same variant across sessions, platforms, and even offline.

Next Steps

  • Use Feature Flags for typed boolean rollouts
  • Combine experiments with Remote Config for variant-scoped values
  • Present Paywalls using PaywallContext to attribute conversions to the experiment