Android SDK

View as Markdown

The Unleash Android SDK lets you evaluate feature flags in Android applications. It connects to Unleash or Unleash Edge to fetch evaluated flags for a given Unleash context.

You can use this SDK with Unleash Enterprise or Unleash Open Source.

For an overview of how Unleash SDKs work, including offline behavior, feature compatibility across SDKs, and default refresh and metrics intervals, refer to the SDK overview.

Requirements

  • Android API level 21 or higher (API level 23 recommended)

Installation

You will require the SDK on your classpath, so go ahead and add it to your dependency management file.

Gradle

1implementation("io.getunleash:unleash-android:${unleash.sdk.version}")

Maven

1<dependency>
2 <groupId>io.getunleash</groupId>
3 <artifactId>unleash-android</artifactId>
4 <version>${unleash.sdk.version}</version>
5</dependency>

Proguard

You shouldn’t have to configure proguard for this library, but if you do, you can use the following configuration:

1-keep class io.getunleash.android.** { *; }

Permissions

Your app will need internet permission in order to reach Unleash and access network state to be able to react to network changes. So in your manifest file add

1<uses-permission android:name="android.permission.INTERNET" />
2<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Initialization

Configure your client instance (use a single instance to avoid file contention on cache directory).

1val unleash = DefaultUnleash(
2 androidContext = applicationContext, // likely a reference to your Android application context
3 unleashConfig = UnleashConfig.newBuilder(appName = "test-android-app")
4 .proxyUrl("https://eu.app.unleash-hosted.com/demo/api/frontend")
5 .clientKey("<client-side SDK API key>")
6 .pollingStrategy.interval(60000)
7 .metricsStrategy.interval(60000)
8 .build()
9)
10
11// Create an initial UnleashContext
12// The context can be changed at any time but we recommend you provide sensible defaults at the start
13// This will make sure that the initial state for the feature flags is correct
14val initialContext = UnleashContext.newBuilder()
15 .userId("However you resolve your userid")
16 .sessionId("However you resolve your session id")
17 .build()
18unleash.setContext(initialContext)

You can pass event listeners either when constructing DefaultUnleash or when calling start(). By default the SDK uses delayed initialization (delayedInitialization = true). When delayed initialization is enabled, any listeners you pass to the constructor are stored and only registered when you call start(). This avoids emitting events before the SDK has finished its startup sequence. If you prefer to register and activate listeners immediately, set delayedInitialization to false in the UnleashConfig and the SDK will register constructor-provided listeners at construction time and start immediately.

Start Unleash

From the moment you call unleash.start(), it will immediately poll for the latest feature flags and then continue polling in the background.

Because this call is asynchronous, it will not block the main thread so you may want to check if the initial state is ready before proceeding.

You have a few options:

  1. Wait until Unleash is ready. This will make sure that your app is in sync with the feature toggles.
  2. Provide an initial state. This could make sense in cases where the network is not available and you want to provide a default state.
  3. None of the above. Unleash comes with a default behavior for feature flags with unknown state.

Check if the SDK is ready

You can check if the initial state is ready by checking unleash.isReady(). Normally, you would add an exit condition to avoid an infinite loop.

1// Start the Unleash instance
2unleash.start()
3// Wait until Unleash is ready
4var maxWaits = 10 // 10 * 100ms = 1 seconds
5while (maxWaits > 0 && !unleash.isReady()) {
6 // Wait until Unleash is ready
7 Thread.sleep(100)
8 maxWaits --
9}

Alternatively, you can add an event listener to Unleash which is a more reactive approach. This way you can be notified when Unleash is ready:

1// Start the Unleash instance
2unleash.start(
3 eventListeners = listOf(object: UnleashReadyListener {
4 override fun onReady() {
5 // notify your app that Unleash is ready
6 }
7 })
8)

Bootstrap

If you need to have a known state for your UnleashClient, you can provide the initial state as a list of toggles. This is useful when you have a known initial state for your feature toggles.

1val toggles = listOf(
2 Toggle(name = "flag-1", enabled = true)
3)
4instance.start(bootstrap = toggles)

Alternatively, you can perform a query against the frontend API using your HTTP client of choice and save the output as a json file. Then you can tell Unleash to use this file to set up toggle states.

1val toggles = File("/tmp/proxyresponse.json")
2unleash.start(bootstrapFile = toggles)

Default behavior

You can just start Unleash and it will use the default behavior for feature flags with unknown state until it has fetched the latest state from Unleash.

In some situations this could be a valid option. For example, if you don’t want the additional complexity of the option above and you are fine with the default behavior.

1unleash.start()

Check flags

After starting the Unleash instance, you can start using the feature toggles.

1if (unleash.isEnabled("flag-1")) {
2 println("flag-1 enabled")
3} else {
4 println("flag-1 disabled")
5}
6
7unleash.getVariant("flag-with-variant").let { variant ->
8 if (variant.featureEnabled) {
9 println("variant enabled: ${variant.name}") // you can also access variant.payload
10 } else {
11 println("variant disabled")
12 }
13}

Events

To migrate from the old SDK, see the migration guide.

This SDK exposes small focused listener interfaces. Below are short copy-paste examples showing how to register them.

  • Register a heartbeat listener (fires on successful fetch, 304 and errors):
1val heartbeatListener = object : UnleashFetcherHeartbeatListener {
2 override fun togglesUpdated() {
3 // called when new toggles were fetched successfully
4 }
5 override fun togglesChecked() {
6 // called when server returns 304 Not Modified
7 }
8 override fun onError(event: HeartbeatEvent) {
9 // called when an error occurred while fetching
10 }
11}
12
13// add at start or at runtime
14unleash.start(eventListeners = listOf(heartbeatListener))
15// or
16unleash.addUnleashEventListener(heartbeatListener)
  • Register a state listener (fires when in-memory state/cache changes):
1val stateListener = object : UnleashStateListener {
2 override fun onStateChanged() {
3 // called when the toggle cache was updated
4 }
5}
6unleash.addUnleashEventListener(stateListener)
  • Ready listener (fires once when initial state arrives):
1val readyListener = object : UnleashReadyListener {
2 override fun onReady() {
3 // initial state received — safe to proceed
4 }
5}
6unleash.addUnleashEventListener(readyListener)

You can add or remove listeners at runtime with addUnleashEventListener/removeUnleashEventListener.

For a complete working example, see the sample app and its main activity, which demonstrates toggle state display and event listeners.

Default behavior

If you don’t provide an initial state, Unleash will use the default behavior for feature flags with unknown state. This means that if a feature flag is not found in the list of flags, it will be disabled by default.

Unleash context

The SDK uses UnleashContext as a Kotlin data class, so contexts are compared by value (all fields are compared). There are now two different fetch paths and it’s important to understand which one applies in each situation:

  • Scheduled polling and manual “fetch” API: refreshTogglesWithContext (internal fetch path) always attempts to fetch from upstream (subject to throttling and HTTP 304 handling). This is the path used by the scheduler so periodic polls will always check upstream for changes even when the context hasn’t changed.
  • Context-change-triggered fetch: refreshTogglesIfContextChanged is used when the SDK is asked to update the context (setContext / setContextWithTimeout). That helper compares the new context to the last fetched context and will skip the network call if the values are identical.

Important details:

  • setContext — when called after the SDK has started, the SDK will attempt a context-change-triggered fetch via refreshTogglesIfContextChanged. That helper will NOT perform a network request if the new context equals the last fetched context (prevents accidental redundant fetches when you update context with same values).
  • setContextAsync — updates the internal context asynchronously without forcing an immediate fetch. The scheduled polling path still runs independently and will call refreshTogglesWithContext periodically to check upstream for changes.
  • refreshTogglesNow / refreshTogglesNowAsync — these public methods force a fetch by calling refreshToggles and bypass the context equality check. They still respect throttling, so excessive forced fetches may be skipped due to throttling.