Optimizely will be sunsetting Full Stack Experimentation on July 29, 2024. See the recommended Feature Experimentation migration timeline and documentation.

Dev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunitySumbit a ticketLog In
GitHubNuGetDev CommunitySumbit a ticket

Initialize SDK

This topic describes how to initialize the Optimizely Android SDK in your application.

Use the instantiate method to initialize the Android SDK and instantiate an instance of the Optimizely client class that exposes API methods like Get Enabled Features. Each client corresponds to the datafile representing the state of a project for a certain environment.

In the Android SDK, you do not need to manage the datafile directly. The SDK has an additional abstraction called a manager that handles getting the datafile and instantiating the client for you during the SDK's initialization process. The manager includes support for datafile polling to automatically update the datafile on a regular interval.

Version

3.2.1 and higher

Description

The constructor accepts a configuration object to configure Optimizely.

Some parameters are optional because the SDK provides a default implementation, but you may want to override these for your production environments. For example, you may want to override these to set up an error handler and logger to catch issues, an event dispatcher to manage network calls, and a User Profile Service to ensure sticky bucketing.

Parameters

The table below lists the required and optional parameters in Android (client constructed using Builder class and passing the following to Build()).

ParameterTypeDescription
datafile
optional
stringThe JSON string representing the project.
errorhandler
optional
IErrorHandlerAn error handler object to handle errors.
_SDK Key
_required
stringThe SDK key used to define the config file for the environment

Example

The manager is implemented as a factory for Optimizely client instances. To use it, create a manager object by supplying your SDK key and optional configuration settings for datafile polling and datafile bundling. You can find the SDK key in the Settings > Environments tab.

Next, choose whether to instantiate the client synchronously or asynchronously and use the appropriate initialize method to instantiate a client.

If you use a bundled datafile, copy the datafile JSON string from the Optimizely application. Typically you would want to copy the datafile string at the latest point possible pre-release.

// In your Application#onCreate

import com.optimizely.ab.android.sdk.OptimizelyManager;
import com.optimizely.ab.android.sdk.OptimizelyClient;
import com.optimizely.ab.OptimizelyUserContext;
import com.optimizely.ab.optimizelydecision.OptimizelyDecision;

// Build a manager
OptimizelyManager optimizelyManager = OptimizelyManager.builder()
    .withSDKKey("SDK_KEY_HERE")
    .withEventDispatchInterval(60L * 15L)
    .withDatafileDownloadInterval(60L * 15L)
    .build(getApplicationContext());

// Instantiate a client synchronously with a bundled datafile
String datafile = "REPLACE_WITH_YOUR_DATAFILE";
OptimizelyClient optimizelyClient = optimizelyManager.initialize(this, datafile);

// Or, instantiate it asynchronously with a callback
optimizelyManager.initialize(context, null, (OptimizelyClient client) -> {
    // flag decision
    OptimizelyUserContext user = client.createUserContext("USER_ID_HERE");
    OptimizelyDecision decision = user.decide("FLAG_KEY_HERE");
});

See the Android example: _MainActivity.java.

Get a client

Each manager retains a reference to its most recently initialized client. You can use the getOptimizely function to return that instance.

If this method is called before any client objects have been initialized, a dummy client instance is created and returned. This dummy instance logs errors when any of its methods are used.

OptimizelyClient optimizelyClient = optimizelyManager.getOptimizely();

Use synchronous or asynchronous initialization

You have two choices for when to instantiate the Optimizely client: synchronous and asynchronous. The behavioral difference between the two methods is whether your application pings the Optimizely servers for a new datafile during initialization. The functional difference between the two methods is whether your app prioritizes accuracy or speed.

🚧

Important

You must decide to initialize the Android SDK either synchronously or asynchronously. You cannot use both initialization methods.

Synchronous initialization

The synchronous method prioritizes speed over accuracy. Instead of attempting to download a new datafile every time you initialize an Optimizely client, your app uses a local version of the datafile. This local datafile can be cached from a previous network request (see more about configuring datafile polling) or embedded within your app (see more about enabling bundled datafiles).

When you initialize a client synchronously, the Optimizely manager first searches for a cached datafile. If one is available, the manager uses it to complete the client initialization. If the manager can't find a cached datafile, the manager searches for a bundled datafile. If the manager finds a the bundled datafile, it uses the datafile to complete the client initialization. If the manager can't find a bundled datafile, the manager can't initialize the client.

1200

To initialize an OptimizelyClient object synchronously, call OptimizelyManager#initialize() and provide two arguments:

  • The Application instance (from android.app.Application)
  • An Integer pointer to the application resource datafile

For more details on the specific requirements, view OptimizelyManager.java from the publicly available Android SDK.

Asynchronous initialization

The asynchronous method prioritizes accuracy over speed. During initialization, your app requests the newest datafile from the CDN servers. Requesting the newest datafile ensures that your app always uses the most current project settings, but it also means your app cannot instantiate a client until it downloads a new datafile, discovers the datafile has not changed, or until the request times out. This process takes time.

Initializing a client asynchronously executes like the synchronous initialization, except the manager will first attempt to download the newest datafile. This network activity is what causes an asynchronous initialization to take longer to complete.

If the network request returns an error (such as when network connectivity is unreliable) or if the manager discovers that the cached datafile is identical to the newest datafile, the manager then uses the synchronous approach. If manager discovers that the datafile has been updated and now differs from the cached datafile, the manager downloads the new datafile and uses it to initialize the client.

1013

To initialize an OptimizelyClient object asynchronously, call OptimizelyManager#initialize() and three arguments:

  • The Application instance (from android.app.Application)
  • An Integer pointer to the application resource datafile
  • A listener object

For more details on the specific requirements and to see how to build each object, view OptimizelyManager.java from the publicly available Android SDK.

Configure datafile polling

During its initialization, the manager attempts to pull the newest datafile from the CDN servers. After this, the Optimizely manager can periodically check for and pull a new datafile at the time interval you set during its initialization. The process of checking for and downloading a new datafile is called datafile polling.

By default, datafile polling is disabled, so the manager only checks for a new datafile during initialization. To enable polling, set a non-zero interval value. This value is the number of seconds the manager waits between datafile polling attempts.

// Poll every 15 minutes
OptimizelyManager optimizelyManager = OptimizelyManager.builder()
          .withSDKKey("SDK_KEY_HERE")
          .withDatafileDownloadInterval(15, TimeUnit.MINUTES)
          .build(context);

Usage notes

  • The minimum polling interval is 900 secs (which is 15 minutes, enforced by the Android JobScheduler API).
  • Updated datafiles are automatically picked up because the default datafile handler now support the ProjectConfigManager API.
  • You can register for datafile change notifications as well via the UpdateConfigNotification.
OptimizelyClient optimizelyClient = optimizelyManager.initialize(context, R.raw.datafile);
        optimizelyClient.getNotificationCenter().addNotificationHandler(UpdateConfigNotification.class, (UpdateConfigNotification notification) -> {
            System.out.println("got datafile change");
        });

Learn more about Automatic Datafile Management
To override the default datafile handler and use your own, implement DatafileHandler.

Enable bundled datafiles

When your customer opens your app for the first time after installing or updating it, the manager attempts to pull the newest datafile from Optimizely's CDN. If your app is unable to contact the servers, the manager can substitute a datafile that you included (bundled) when you created your app.

By bundling a datafile within your app, you ensure that the Optimizely manager can initialize without waiting for a response from the CDN. This lets you prevent poor network connectivity from causing your app to hang or crash while it loads.

Datafile bundling works with both synchronous and asynchronous initialization because reverting to a bundled datafile happens during the Optimizely manager's initialization, before a client is instantiated.

/**
 * Initialize Optimizely asynchronously with a datafile.
 *  If it is not able to download a new datafile, it will
 *  initialize an OptimizelyClient with the one provided.
 */
optimizelyManager.initialize(context, R.raw.datafile, (OptimizelyClient optimizelyClient) -> {
    OptimizelyUserContext user = optimizelyClient.createUserContext("USER_ID_HERE");
    OptimizelyDecision decision = user.decide("FLAG_KEY_HERE");
});

/**
 * Initialize Optimizely synchronously
 *  This will immediately instantiate and return an
 *  OptimizelyClient with the datafile that was passed in.
 *  It'll also download a new datafile from the CDN and
 *  persist it to local storage.
 *  The newly downloaded datafile will be used the next
 *  time the SDK is initialized.
 */
optimizelyManager.initialize(context, R.raw.datafile);

More Sample Code

// Here are more sample codes for synchronous and asynchronous SDK initializations with multiple options

// [Synchronous]

// [S1] Synchronous initialization
//      1. SDK is initialized instantly with a cached (or bundled) datafile
//      2. A new datafile can be downloaded in background and cached after the SDK is initialized.
//         The cached datafile will be used only when the SDK re-starts in the next session.
        
optimizelyManager =  OptimizelyManager.builder()
                .withSDKKey("<Your_SDK_Key>")
                .build(context);
optimizelyClient = optimizelyManager.initialize(context, R.raw.datafile);
variation = optimizelyClient.activate("<Experiment_Key>", "<User_ID>");

// [S2] Synchronous initialization
//      1. SDK is initialized instantly with a cached (or bundled) datafile
//      2. A new datafile can be downloaded in background and cached after the SDK is initialized.
//         The cached datafile is used immediately to update the SDK project config.
        
optimizelyManager =  OptimizelyManager.builder()
                .withSDKKey("<Your_SDK_Key>")
                .build(context);
optimizelyClient = optimizelyManager.initialize(context, R.raw.datafile, true, true);
variation = optimizelyClient.activate("<Experiment_Key>", "<User_ID>");

// [S3] Synchronous initialization
//      1. SDK is initialized instantly with a cached (or bundled) datafile
//      2. A new datafile can be downloaded in background and cached after the SDK is initialized.
//         The cached datafile is used immediately to update the SDK project config.
//      3. Polling datafile periodically.
//         The cached datafile is used immediately to update the SDK project config.
        
optimizelyManager =  OptimizelyManager.builder()
                .withSDKKey("<Your_SDK_Key>")
                .withDatafileDownloadInterval( TimeUnit.MINUTES.toSeconds(15))
                .build(context);
optimizelyClient = optimizelyManager.initialize(context, R.raw.datafile);
variation = optimizelyClient.activate("<Experiment_Key>", "<User_ID>");

// [Asynchronous]

// [A1] Asynchronous initialization
//      1. A datafile is downloaded from the server and the SDK is initialized with the datafile
        
optimizelyManager =  OptimizelyManager.builder()
                .withSDKKey("<Your_SDK_Key>")
                .build(context);
optimizelyManager.initialize(context, null, (OptimizelyClient client) -> {
		Variation variation2 = client.activate("<Experiment_Key>", "<User_ID>");
});

// [A2] Asynchronous initialization
//      1. A datafile is downloaded from the server and the SDK is initialized with the datafile
//      2. Polling datafile periodically.
//         The cached datafile is used immediately to update the SDK project config.
       
optimizelyManager =  OptimizelyManager.builder()
                .withSDKKey("<Your_SDK_Key>")
                .withDatafileDownloadInterval( TimeUnit.MINUTES.toSeconds(15))
                .build(context);
optimizelyManager.initialize(context, null, (OptimizelyClient client) -> {
		Variation variation2 = client.activate("<Experiment_Key>", "<User_ID>");
});

Source files

The language/platform source files containing the implementation for Android are on Github.