Java/Android via REST Helper

Overview

Conductrics provides a Java helper library to make it easy to work with our HTTP API from within your Android or Java code. Behind the scenes, the wrapper will communicate with Conductrics via HTTPS just as any other application would.

📘

This is just a helper library

This helper library is just here to make things a bit easier. If you prefer, you are welcome to call our HTTP API directly from your code, or fork/adapt this helper library to suit your needs.

See also: the corresponding Swift/iOS Helper Library and our Implementation Options for Mobile overview page.

Installation

Using Maven (Android Studio)

Open your app's build.gradle file.

Add a reference to the Conductrics Maven repository.

repositories {
    maven {
        url "https://conductrics-maven-repo.s3.amazonaws.com/snapshot/"
    }
}

You can specify either "snapshot" or "release" branches of the repo.
In that same build.gradle file, in the dependencies section, add:

    implementation('com.conductrics:Conductrics:1.0.4')

Sync the project.

In order to communicate with our HTTP API, your app will also need permission to access the internet.

Edit your AndroidManifest.xml to include:

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

Getting Started with a Simple Example

Before you start, you'll want the following from your Conductrics account, which you can get from the Conductrics Admin. Go to Settings > Distribution, then hit Setup for your API "Deploy Target" (it may be named "Server-Side API" or similar). From there, make a note of your:

  • Runtime API Key (looks like api-xxxxxxxxxxxxx)
  • REST API Endpoint URL (will start with something like https://api-v3.conductrics.com, but depends on region and whether your company is using a dedicated Conductrics environment).

Basic Implementation Steps
Let's say you want to instrument a basic A/B test in your application. Let's assume that you've created an agent in the Conductrics console with an id of my-first-test, and that it is configured with two variations: A and B.

The basic steps in your code are:

  1. Initialize an instance of the main class by calling new Conductrics(url, api_key);

  2. Create an instance of RequestOptions, providing it with a unique identifier for the visitor if possible. See the "About Session Identifiers" note below.

  3. Call api.select() to ask for a selection from your agent. You'll get back a SelectResponse object which includes the variation ("A" or "B") that was selected, plus some other information. You use this to display or present the appropriate variation content/functionality in your application.

  4. Later, when a conversion event occurs, call api.reward() to send some "reward" value to a Goal. This is how Conductrics will measure the success rate of each variation as the test proceeds.

Let's put that together in a basic example, first importing some classes:

import com.conductrics.Conductrics;
import com.conductrics.RequestOptions;
import com.conductrics.SelectResponse;
import com.conductrics.Callback;

Then later in your code...

Conductrics api = new Conductrics(
  "https://api-v3.conductrics.com/ac-xxxxxxx/v3/agent-api",
  "apikey-1234567890"
);

// the sessionId can come from your visitor data, or be a random string
// it should remain stable for at least the duration of the tested experience
String sessionId = "my-visitor-id-1234";
// the agent code comes from the Agent home page (under the Agent title)
String agentCode = "my-first-test-agent";
// the goal code can be found on any Edit Goal modal
String goalCode = "g-JXTc3gha89"; // the API code value, not the title

// Get ready to call Conductrics
// You can also pass Visitor Traits, timeouts, and other options at this point
RequestOptions options = new RequestOptions(sessionId);

// Get a variation assignment (A or B, etc) from Conductrics
api.select( options, agentCode, new Callback<SelectResponse>() {
  public void onValue(SelectResponse response) {
    String variant = response.getCode(); // "A" or "B"

    // here's where you do whatever is needed in your app
    // to display/expose the variation content/functionality
    if (variant == "B" ) {
      System.out.println("Time to show the 'B' Variation");
    }
  }
});

// Later, in response to a conversion event (as defined by you) 
api.reward(options, goalCode);
📘

About Session Identifiers

As shown in the example above, you can provide an identifier to the RequestOptions constructor. We call this a "session" identifier, but if you have a stable visitor identifier, you can provide that, which will cause variation selections to "stick" to the given visitor/user. That will cause Conductrics to keep the visitor assigned to the same variations, and you can do things like select a variation in your app but have the conversion event be on a web page or vice-versa (as long as you can keep the identifier consistent via logging in or some other means).

Alternatively, you can omit the session identifier, which will cause this helper library to make up a random one internally which will be used for the lifetime of the RequestOptions instance. This gives you a way to do "anonymous" testing/optimizations. Note that you would need to provide the same RequestOptions instance when calling reward() for any goal/conversion events.

How to get a Persistent Session ID

If you want a session that will persist even after your Android app has been closed and re-opened, you can use this example code:

// How to get a session ID that will persist across app launches.
private String getSessionID() {
  Context context = getApplicationContext();
  SharedPreferences settings = 
    context.getSharedPreferences("Conductrics", Context.MODE_PRIVATE);
  // If an ID is already persisted, use it.
  String id = settings.getString("Session-ID", null);
  if( id == null ) {
    // If we create a new ID, also persist it
    id = UUID.randomUUID().toString();
    settings.edit()
      .putString("Session-ID", id)
      .apply();
  }
  return id;
}

You use this value when constructing the RequestOptions, eg:

RequestOptions options = new RequestOptions( getSessionID() );

More About Getting Selections

The first example (above) shows a very simple usage scenario. That may cover most of your needs, at least at first. The following sections discuss some other things this helper library provides with respect to getting variation selections for your tests/agents.

Using Variation Meta-Data

As the examples above show, you can get the selected variation ("A" or "B", etc) easily using the SelectResponse.getCode() method. But it can often be handy to get additional information back from Conductrics along with the selected variation code. You can use Variation Meta-Data for this purpose.

For instance, if your variations are about changing the text of a CTA button, it might be helpful for the text itself to live in your Conductrics agent. In such a case, you could use the Conductrics Admin to provide a piece of meta-data called text or cta-text for each variation. Then, in your Java / Android code, you can use the SelectResponse.getMeta() method to get the value as a string:

String ctaText = response.getMeta("cta-text");

You could do the same for some other aspects of the variations, such as the CTA color, or conceptual "foreign keys" like offer, article, or property identifiers. See "Using Variation Meta-Data" in the Runtime API Reference page for more details about adding meta-data to your variations.

Getting Selections from Multiple Agents

You may have multiple agents/tests running at the same time in your app. You can ask for multiple selections by using the List<String> form of the select() method. This will cause the helper library to get the selections from Conductrics in one HTTP request (rather than multiple HTTP requests), which will generally be more efficient.

You'll get back a Map filled with SelectResponse instances for each of the agent codes you specified:

List<String> agents = Arrays.asList("my-agent-x", "my-agent-y");
api.select( options, agents, new Callback<Map<String,SelectResponse>>() {
  public void onValue(Map<String,SelectResponse> outcomes) {
    SelectResponse outcome = outcomes.get("my-agent-x");
    String variationForX = outcome.getCode();
  }
});

Working with Multivariate Tests (MVT)

In a Multivariate Testing (MVT) scenario, you bundle two or more "normal" API agents into an MVT "Group". See Multivariate (MVT) Agents for details about how Conductrics treats multivariate scenarios.

Just as a simple example, let's say you wanted to run an MVT test that tried different combinations of CTA Color and CTA Text:

  1. In the Conductrics Admin, create one agent for each aspect of the test. Let's say those agents are called cta-color and cta-text.

  2. Also in the Conductrics Admin, group the two agents together as a MVT Agent. Your two agents are now displayed as conceptual "children" of the MVT "parent" agent.

  3. In your code, call both of the child agents simultaneously, as described in the "Getting Selections from Multiple Agents" section above. Selections and rewards will get counted at both the "child" agent level, and also at the MVT level so you can see whether CTA Color or Text (or whatever you are testing) is more informative and so on. See the MVT Report doc page for details.

📘

Please make sure to ask for all of the "child" agents at the same time via the List<String> form of the select() method when instrumenting an MVT test.

Providing Custom Visitor Traits

Conductrics lets you provide custom "visitor traits", which will show up in the Conductrics so you can evaluate the results of your tests for various visitor segments and so on. They can also be used in targeting rules and include/exclude conditions. See Custom Visitor Traits for more information about visitor traits in general.

To use visitor traits with this helper library, provide them via the RequestOptions.setTrait() method, for instance:

options.setTrait("tier", "silver").setTrait("interest", "music");

📘

Note that you'll need to declare the visitor traits ahead of time in the Conductrics Admin, or they will be silently ignored at runtime. See Custom Visitor Traits for more information about visitor traits in general.

Providing Custom Input Params

Similarly to Custom Visitor Traits (see above), you can also provide ad-hoc information to Conductrics via Custom Input Params. Unlike Visitor Traits, these input params are not retained between calls as part of the visitor's "session" and don't show up in the reporting. But they can be used in conditions for include/exclude rules and targeting rules.

To use custom input params with this helper library, provide them via the RequestOptions.setInput() method. For instance, you could use options.setInput("dollar-value", 50);, which would make it possible to use the "dollar-value" params in your conditions in the Conductrics Admin. See Custom Input Params for details.

Using Provisional Selections

You may sometimes have a situation when you want a variation to be selected for a visitor, but where you don't yet know whether it will be possible to actually display/render/provide the selected experience to the visitor. For instance, let's say you want to run an A/B test, where "A" means waiting 30 seconds before displaying an offer or prompt of some kind, and "B" means waiting 60 seconds. But you know that the user may very well move on before either 30 or 60 seconds elapse.

In such a case, you can use RequestOptions.setProvisional() before calling select() to indicate that you want a "provisional selection" for the visitor. Then, if you are able to actually display the variation, you can "confirm" the selection by calling select() for the same agent again, this time using RequestOptions.setConfirm(true). Only then will the selection be counted in the Conductrics reporting.

See Using Provisional Selections for details about provisional selections.

RequestOptions opts = new RequestOptions("session-id")
  .setProvisional(true);
api.select(opts, "a-example", new Callback<SelectResponse>() {
  public void onValue(SelectResponse outcome) {
    if( outcome.getStatus() == Status.Provisional ) { // always true
      // usually here, you would store the selection variation somewhere
      String variation = outcome.getCode();
    }
  }
});

// sometime later... once the user has seen the variation, we confirm it:
// (make sure to use the same session id as before)
// or you can re-use the same options object (which already has session id)
RequestOptions opts = new RequestOptions("session-id")
  .setConfirm(true);
api.select(opts, "a-example", new Callback<SelectResponse>() {
  public void onValue(SelectResponse outcome) {
    if( outcome.getStatus() == Status.Confirmed ) { // always true
    }
  }
});

Forcing Selections and QA

You'll likely want to QA your variations, which generally means needing to force your app into a particular variation. There are a few ways you can do this:

  1. You can use the QA Sessions window in the Conductrics Admin to pre-select the desired variation(s) for a given session identifier. Assuming that you know the identifier that your app will be providing to RequestOptions(), you can provide that same identifier to create a QA Session on the Conductrics side. Conductrics will reply with the variation(s) you specify, and you'll be able to view a history of the calls made to the Conductrics servers, with debugging messages and variation selections for each call. See Using QA Sessions for details.

  2. If you've rather do things programmatically, perhaps during some conceptual QA-initialization step in your code, you can use setAllowedVariations().

options.setAllowedVariations("example-agent", "B");
api.select( options, "example-agent", new Callback<SelectResponse>() {
    public void onValue(SelectResponse response) {
        response.getCode(); // will always == "B" here, because it was the only allowed variation
    }
});
  1. You can provide an ad-hoc flag of your own choosing as a "custom input param", via the RequestOptions.setInput() method. For instance, you might do something like options.setInput("qa-group", "alpha"), and then add a targeting rule at the agent level to always pick "B" or whatever you want when the "qa-group" custom input is set to "alpha". Such selections would return Policy.Fixed if you call getPolicy() on the SelectResponse. See setInput() in the reference section below, and Custom Input Params for info about custom input parameters in general.

  2. Alternatively, you can simply "pause" the agent at a given variation. This will immediately cause all calls to the agent (including for the general public) to return that variation. If you call SelectResponse.getPolicy() on a paused agent, you'll get back Policy.Paused (see reference section below). Please note that variation selections made while an agent is paused are NOT counted in the reporting, and do not qualify for goals/rewards.

Fallbacks and Timeouts

Under normal conditions, calls to the select() method will interact with Conductrics quickly, and return the corresponding SelectResponse object to your callback. But what if the network connection is slow or offline, or some other error occurs?

When requesting a variation selection with select()
When you call select(), this helper library attempts to make a call to the Conductrics REST-style API via HTTP. If the connection is unsuccessful (perhaps due to a network error or slow connection, or because your app is offline), the library will return a SelectResponse that gives you a "fallback" selection. In such a case:

  • SelectResponse.getCode() will return "A" by default.

  • If you want a different variation code to be returned in error/timeout/fallback cases, you can call RequestOptions.setDefault(agentCode, variationCode) before calling the select() method.

  • Calls to SelectResponse.getMeta() will return null.

  • SelectResponse.getError() will provide the reason why you got a fallback response (network timeout, no connection, malformed API URL, etc).

  • SelectResponse.getPolicy() will return Policy.None.

📘

Adjusting the timeout

By default, the timeout is set to 2000 milliseconds (two seconds). You can provide a different timeout via the RequestOptions.setTimeout() method (see reference below).

More About Sending Rewards

Dealing with Multiple Goals / Rewards

You can have multiple goal types associated with a given test/agent, and each goal may be associated with multiple agents. Just call reward() whenever the actual events occur in your app. Conductrics will "credit" the appropriate variation(s) for the given visitor accordingly.

Providing a Numeric Value

In some types of applications, it makes sense to associate a numeric value with each goal/conversion event. An obvious example is e-commerce, where you might want to be able to evaluate the efficacy of your variations with respect to the amounts of actual purchases.

In such a case, turn on the option to accept numeric rewards for the Goal (you can do this in the Conductrics Admin). You'll be able to set a minimum and maximum value that should be accepted, as a sanity check at runtime. See the Goals / Conversions doc page for more details.

Then provide the numeric value as the value argument when you call reward(). For instance, if you have a Goal with identifier purchase, you might call api.reward(opts, "purchase", 19.99); depending on the actual amount of the purchase.

📘

If you don't provide a numeric value when calling reward(), a default value of 1.0 is assumed.

Knowing whether the Reward was accepted

In many cases, you may think of your calls to reward() in a "fire and forget" manner, because the responses from reward() aren't really "actionable" - that is, your app doesn't necessarily need to store or do anything with the responses for purposes of instrumenting a test or optimization correctly.

However, you may want to know whether your calls to reward() were actually counted on the Conductrics side for QA or logging purposes. For instance, by default most goals only accept a reward once per agent, per visitor. So, if your app calls reward() multiple times for the same goal, the first one would typically be counted but the second one would be (correctly) ignored.

To know whether the reward was accepted, provide a callback when calling reward() and check GoalResponse.getAcceptedValue() within your callback handler. If accepted, the value will be 1 (or the numeric value for the goal per the "Providing a Numeric Value" section above), indicating that the goal will be counted in the Conductrics reporting for the agent. If the value is zero, the goal was not accepted/counted.

Reasons why a goal might not be accepted for a given test/agent:

  • A reward has already been accepted for the given agent (for this visitor). If you want Conductrics to accept multiple goals/rewards for a given agent/visitor, you can change the "Accept Up To" limit at the Goal type level in the Conductrics Admin. See Goals / Conversions for details.

  • The Goal is not associated with the agent in the Conductrics Admin.

  • If the Goal uses numeric reward values, the numeric value you sent is outside of that range.

  • The Goal code in question no longer exists.

  • The Agent in question no longer exists or is no longer Running.

Java Library Reference

The Conductrics package defines these classes:

  • Conductrics - The main class which exposes the core select(), reward(), and exec() methods.
  • RequestOptions - Used to provide information about the visitor context and other options.
  • SelectResponse - Returned by a call to select(), providing info about the selected variation
  • RewardResponse - Returned by a call to reward(), providing feedback about how the conversion event was processed.
  • ExecResponse - Returned by a call to exec(), providing feedback about how the provided commands were processed.

Conductrics Instance Methods

Arguments

Description

select

RequestOptions options String agentCode Callback<SelectResponse>

Ask an agent to make a selection for this user session, call back with a SelectResponse.

The callback is required.

select

RequestOptions options
List<String> agentCode; Callback Map<String, SelectResponse>

Ask multiple agents to make selections for this user session, call back with a Map of agent codes to their corresponding SelectResponse.

The callback is required.

reward

RequestOptions options
String goalCode (opt) Double value = 1.0 (opt) Callback<GoalResponse>

Send a reward to a goalCode, call back with a GoalResponse.

Optionally, you may provide a numeric value (see "Providing a Numeric Value" section above).

The callback is optional.

exec

RequestOptions options
JSONArray commands Callback<ExecResponse>

Execute any commands supported by the Runtime API, call back with an ExecResponse.

The callback is required.

RequestOptions

Description

setSession(String sessionID)

Set the value that will identify this user/visitor session. Repeated calls to Select() with the same sessionID will get the same result.

setInput(String key, String value)

Use to provide ad-hoc information as custom "input" values, which you can then use in conditions/rules in the Conductrics Admin to include/exclude visitors or target certain variations to certain users, etc.

See Custom Input Params for details.

setTrait(String group, String trait)

Use to specify custom Visitor Traits for the visitor. Visitor Traits show up in the Conductrics reporting, and can also be used in conditions/rules to include/exclude visitors or target certain variations to certain users, etc.

Traits must be declared in the Conductrics Admin; any unknown traits will be silently ignored. See Custom Visitor Traits for details.

setParam(String key, String value)

Sets an arbitrary URL parameter for the API request. You can use this to set additional/future parameters that are supported by the underlying REST API that aren't explicitly included in this helper library.

setTimeout(int ms)

The number of milliseconds to wait for a response from the Conductrics API Server. Default is 2000 if not provided. See the "Fallbacks and Timeouts" section above.

setDefault(String agent, String variant)

Use to provide the "fallback" variation code that should be returned if select() encounters a timeout or network error, etc. See the "Fallbacks and Timeouts" section above.

setUserAgent(String userAgent)

Provide a custom user-agent string. You can base conditions/rules in the Conductrics admin to include/exclude visitors based on the user agent, if needed.

setAllowedVariations( String agent, List<String> variants)

Or,

setAllowedVariations( String agent, String... variants)

Constrain which variations are allowed to be selected by the API. Example:

options.setAllowedVariations("example-agent", "B");
api.select(options, "example-agent", new Callback<SelectResponse>() {
  public void onValue(SelectResponse response) {
    response.getCode(); // will always be == "B"
  }
});

setProvisional(boolean value)

Indicate whether requests should be marked as provisional. Must later call select() with an options.setConfirm(true).

setConfirm(boolean value)

Indicate that this call to select() should confirm a prior provisional selection.

SelectResponse Method

Description

getAgent()

String. The agent code that was provided given to select().

getCode()

String. The variation code selected by the agent.

getPolicy()

Policy enum value. The Selection Policy used to make the selection, one of:

  • Random - a (normal) random selection
  • Paused - the agent is paused
  • Fixed - a selection due to a targeting rule
  • Adaptive - an agent in Adaptive mode
  • Sticky - same as a prior result for this session
  • Control - session was put in the control group
  • Bot - bot detection, events will be ignored.
  • None - an error or forced outcome

getMeta(String key)

String or null. If the selection variation has custom meta-data saved in the Conductrics Console, you can access it here after a selection is made. Returns null if no meta-data exists for the given key,

See notes about meta-data in the sections above, and the "Using Variation Meta-Data" section on the Runtime API Reference page.

getError()

Exception or null. If an error occurred, such as a timeout, the exception returned here will describe it.

getExecResponse()

ExecResponse. The underlying message that this SelectResponse was built from. Most useful in combination with: options.setParam("debug", "true") and response.getExecResponse().getLog().

GoalResponse MethodDescription
getGoalCode()String. The goal code given to Reward().
getAcceptedValue(agentCode)Double. The amount of value that was acknowledged by a particular agent. The Agent screen of the Console must have this Goal selected in order to accept value.
getError()Exception or null.
ExecResponse MethodDescription
getSelection(agentCode)SelectResponse. Extracts the corresponding selection message from the response.
getReward(goalCode)GoalResponse. Extracts the corresponding reward message from the response.
getLog()List<String>. For debugging purposes. Will be populated with debugging-style messages, if you set setParam("debug", "true") on your RequestOptions. The messages will include notes about the evaluation of conditions/rules and so on.
getTraits()List<String>. Returns a list of Custom Visitor Traits that are associated with this session. For debugging purposes only; Conductrics is not designed to be used as a system of record for custom visitor traits.
getError()Exception or null.
getJSONObject()JSONObject. The underlying JSON response from the Conductrics server. See API Usage via REST API for details.