Android SDK
Learn how to set up the Harness FME Android SDK to manage feature flags and experiments in Android applications.
This guide provides detailed information about our Android SDK. All of our SDKs are open source. Go to our Android SDK GitHub repository to see the source code.
Before you begin
This library is designed for Android applications written in Java or Kotlin and is compatible with Android SDK versions 19 and later (4.4 Kit Kat).
IMPORTANT
Starting with Android v3.0.0, this SDK now relies on WorkManager v2.7.1. This requires your application to use at least compileSdk 31.
If you haven't upgraded to use API 31, you can force the downgrade of the WorkManager dependency.
implementation("androidx.work:work-runtime") {
version {
strictly("2.6.0")
}
}Initialization
To get started, set up FME in your code base with the following two steps.
1. Import the SDK into your project
Import the SDK into your project using the following line:
2. Instantiate the SDK and create a new SDK factory client
The first time the SDK is instantiated, it starts background tasks to update an in-memory cache and in-storage cache with small amounts of data fetched from Harness servers. This process can take up to a few hundred milliseconds, depending on the size of the data.
If the SDK is asked to evaluate which treatment to show to a customer for a specific feature flag while it is in this intermediate state, it may not have the data necessary to run the evaluation. In this circumstance, the SDK does not fail, rather, it returns the control treatment.
After the first initialization, the fetched data is stored. Further initializations fetch data from that cache and the configuration is immediately available.
We recommend instantiating the SDK factory once as a singleton and reusing it throughout your application.
Configure the SDK with the SDK key for the FME environment that you would like to access. In legacy Split (app.split.io) the SDK key is found on your Admin settings page, in the API keys section. Select a client-side SDK API key. This is a special type of API token with limited privileges for use in browsers or mobile clients. See API keys to learn more.
Use the SDK
The following explains how to use this SDK.
Basic usage
To make sure the SDK is properly loaded before asking it for a treatment, wait until the SDK is ready as shown below. We set the client to listen for the SDK_READY event triggered by the SDK before asking for an evaluation. Once the SDK_READY event fires, use the getTreatment method to return the proper treatment based on the FEATURE_FLAG_NAME you pass and the key you passed when instantiating the SDK. From there, use an if-else-if block as shown below and plug the code in for the different treatments that you defined in Harness FME. Make sure to remember the final else branch in your code to handle the client returning control.
Also, a SDK_READY_FROM_CACHE event is available, which allows to be aware of when the SDK has loaded data from cache. This way, it is ready to evaluate feature flags using those locally cached definitions.
Attribute syntax
To target based on custom attributes, the SDK's getTreatment methods need to be passed an attribute map at runtime.
In the example below, we are rolling out a feature flag to users. The provided attributes plan_type, registered_date, permissions, paying_customer, and deal_size are passed to the getTreatment call. These attributes are compared and evaluated against the attributes used in the Rollout plan as defined in Harness FME to decide which treatment is assigned to this key.
The getTreatment method supports five types of attributes: strings, numbers, dates, booleans, and sets. The proper data type and syntax for each are:
Strings: Use type String.
Numbers: Use type
java.lang.Longorjava.lang.Integer.Dates: Express the value in
milliseconds since epoch. In Java,milliseconds since epochis of typejava.lang.Long. For example, the value for theregistered_dateattribute below isSystem.currentTimeInMillis(), which is a long.Booleans: Use type
java.lang.boolean.Sets: Use type
java.util.Collection.
Binding attributes to the client
Attributes can be bound to the client at any time during the SDK lifecycle. These attributes are stored in memory and used in every evaluation to avoid the need for keeping the attribute set accessible through the whole app. These attributes can be cached into the persistent caching mechanism of the SDK making them available for future sessions, as well as part of the SDK_READY_FROM_CACHE flow by setting the persistentAttributesEnabled to true. There is no need to wait for your attributes to be loaded at every session before evaluating flags that use them.
When an evaluation is called, the attributes provided (if any) at evaluation time are combined with the ones already loaded into the SDK memory, with the ones provided at function execution time take precedence, enabling for those attributes to be overridden/hidden for specific evaluations.
An attribute is considered valid if it follows one of the types listed below:
String
Number
Boolean
Array
The SDK validates these before storing them and if there are invalid or missing values, possibly indicating an issue, the methods return the boolean false and do not update any value.
See below the definitions for the API which is exposed on the client:
Refer to the example below to see how to use these methods:
Multiple evaluations at once
In some instances, you may want to evaluate treatments for multiple feature flags at once. Use the different variations of getTreatments method of the SDK factory client to do this.
getTreatments: Pass a list of the feature flag names you want treatments for.getTreatmentsByFlagSet: Evaluate all flags that are part of the provided set name and are cached on the SDK instance.getTreatmentsByFlagSets: Evaluate all flags that are part of the provided set names and are cached on the SDK instance.
Get treatments with configurations
To leverage dynamic configurations with your treatments, use the getTreatmentWithConfig method. This method returns an object containing the treatment and associated configuration.
The config element is a stringified version of the configuration JSON defined in Harness FME. If there is no configuration defined for a treatment, the SDK returns null for the config parameter.
This method takes the exact same set of arguments as the standard getTreatment method. See below for examples on proper usage:
If you need to get multiple evaluations at once, you can also use the getTreatmentsWithConfig methods. These methods take the exact same arguments as the getTreatments methods but return a mapping of feature flag names to SplitResult objects instead of strings. Example usage below.
If a flag cannot be evaluated, the SDK returns the fallback treatment value (default "control" unless overridden globally or per flag). For more information, see Fallback treatments.
Append properties to impressions
Impressions are generated by the SDK each time a getTreatment method is called. These impressions are periodically sent back to Harness servers for feature monitoring and experimentation.
You can append properties to an impression by passing an object of key-value pairs to the getTreatment method. These properties are then included in the impression sent by the SDK and can provide useful context to the impression data.
Three types of properties are supported: strings, numbers, and booleans.
Shutdown
It is good practice to call the destroy method before your app shuts down or is destroyed, as this method gracefully shuts down the SDK by stopping all background threads, clearing caches, closing connections, and flushing the remaining unpublished impressions.
After destroy() is called, any subsequent invocations to the client.getTreatment() or manager methods result in control or empty list respectively.
IMPORTANT!
A call to the destroy() method also destroys the factory object. When creating new client instance, first create a new factory instance.
Track
Use the track method to record any actions your customers perform. Each action is known as an event and corresponds to an event type. Calling track through one of our SDKs or via the API is the first step to getting experimentation data into Harness FME and allows you to measure the impact of your feature flags on your users' actions and metrics. Refer to the Events guide to learn about using track events. In the examples below, you can see that the .track() method can take up to four arguments. The proper data type and syntax for each are:
TRAFFIC_TYPE: The traffic type of the key in the track call. The expected data type is String. You can only pass values that match the names of traffic types that you have defined Harness FME.
EVENT_TYPE: The event type that this event should correspond to. The expected data type is String. Full requirements on this argument are:
Contains 80 characters or fewer.
Starts with a letter or number.
Contains only letters, numbers, hyphen, underscore, or period.
This is the regular expression we use to validate the value:
[a-zA-Z0-9][-_.:a-zA-Z0-9]{0,79}
VALUE: (Optional) The value to be used in creating the metric. This field can be sent in as null or 0 if you intend to purely use the count function when creating a metric. The expected data type is Float.
PROPERTIES: (Optional) An object of key value pairs that can be used to filter your metrics. Learn more about event property capture in the Events guide. FME currently supports three types of properties: strings, numbers, and booleans.
The track method returns a boolean value of true or false to indicate whether or not the SDK was able to successfully queue the event to be sent back to Harness servers on the next event post. The SDK will return false if the current queue size is equal to the config set by eventsQueueSize or if an incorrect input to the track method has been provided.
In case a bad input is provided, refer to the Track events guide for information about our SDK's expected behavior.
Configuration
The SDK has a number of knobs for configuring performance. Each knob is tuned to a reasonable default. However, you can override the value while instantiating the SDK. The parameters available for configuration are shown below.
Configuration
Description
Default value
featuresRefreshRate
The SDK polls Harness servers for changes to feature flags at this rate (in seconds).
3600 seconds
segmentsRefreshRate
The SDK polls Harness servers for changes to segments at this rate (in seconds).
1800 seconds
impressionsRefreshRate
Controls how frequently the impressions cache expires after a write (in seconds). The treatment log captures which customer saw which treatment (on, off, etc.) and is periodically flushed back to Harness servers.
1800 seconds
telemetryRefreshRate
The SDK caches diagnostic data that it periodically sends to Harness servers. This configuration controls how frequently this data is sent back to Harness servers (in seconds).
3600 seconds (1 hour)
eventsQueueSize
When using .track, the number of events to be kept in memory.
10000
eventFlushInterval
When using .track, how often is the events queue flushed to Harness servers.
1800 seconds
eventsPerPush
Maximum size of the batch to push events.
2000
trafficType
When using .track, the default traffic type to be used.
not set
connectionTimeout
HTTP client connection timeout (in ms).
10000 ms
readTimeout
HTTP socket read timeout (in ms).
10000 ms
impressionsQueueSize
Default queue size for impressions.
30K
disableLabels
Disable labels from being sent to Harness servers. Labels may contain sensitive information.
true
logLevel
Enables logging according to the level specified. Options are NONE, VERBOSE, DEBUG, INFO, WARNING, ERROR, and ASSERT.
NONE
proxyHost
The location of the proxy using standard URI: scheme://user:password@domain:port/path. If no port is provided, the SDK defaults to port 80.
null
ready
Maximum amount of time in milliseconds to wait before notifying a timeout.
-1 (not set)
synchronizeInBackground
Activates synchronization when application host is in background.
false
synchronizeInBackgroundPeriod
Rate in minutes in which the background synchronization would check the conditions and trigger the data fetch if those are met. Minimum rate allowed is 15 minutes.
15
backgroundSyncWhenBatteryNotLow
When set to true, synchronize in background only if battery level is not low.
true
backgroundSyncWhenWifiOnly
When set to true, synchronize in background only when the available connection is wifi (unmetered). When false, background synchronization takes place as long as there is an available connection.
false
streamingEnabled
Boolean flag to enable the streaming service as default synchronization mechanism when in foreground. In the event of an issue with streaming, the SDK will fallback to the polling mechanism. If false, the SDK will poll for changes as usual without attempting to use streaming.
true
syncConfig
Optional SyncConfig instance. Use it to filter specific feature flags to be synced and evaluated by the SDK. These filters can be created with the SplitFilter::bySet static function (recommended, flag sets are available in all tiers), or SplitFilter::byName static function, and appended to this config using the SyncConfig builder. If not set or empty, all feature flags are downloaded by the SDK.
null
persistentAttributesEnabled
Enables saving attributes on persistent cache which is loaded as part of the SDK_READY_FROM_CACHE flow. All functions that mutate the stored attributes map affect the persistent cache.
false
syncEnabled
Controls the SDK continuous synchronization flags. When true, a running SDK processes rollout plan updates performed in Harness FME (default). When false, it fetches all data upon init, which ensures a consistent experience during a user session and optimizes resources when these updates are not consumed by the app.
true
impressionsMode
This configuration defines how impressions (decisioning events) are queued on the SDK. Supported modes are OPTIMIZED, NONE, and DEBUG. In OPTIMIZED mode, only unique impressions are queued and posted to Harness; this is the recommended mode for experimentation use cases. In NONE mode, no impression is tracked in Harness FME and only minimum viable data to support usage stats is, so never use this mode if you are experimenting with that instance impressions. Use NONE when you want to optimize for feature flagging only use cases and reduce impressions network and storage load. In DEBUG mode, ALL impressions are queued and sent to Harness; this is useful for validations. This mode doesn't impact the impression listener which receives all generated impressions locally.
OPTIMIZED
userConsent
User consent status used to control the tracking of events and impressions. Possible values are GRANTED, DECLINED, and UNKNOWN. See User consent for details.
GRANTED
encryptionEnabled
If set to true, the local database contents is encrypted.
false
prefix
If set, the prefix will be prepended to the database name used by the SDK.
null
certificatePinningConfiguration
If set, enables certificate pinning for the given domains. For details, see the Certificate pinning section below.
null
expirationDays
Specifies how long rollout data is kept in local storage before expiring (in days). Used to speed up initialization and support offline behavior.
10 days
clearOnInit
If set to true, clears any previously stored rollout data when the SDK initializes. Useful to force fresh data fetch on startup.
false
rolloutCacheConfiguration
Specifies how long rollout data is kept in local storage before expiring.
null
To set each of the parameters defined above, use the syntax below.
Configure cache behavior
The SDK stores rollout data locally to speed up initialization and support offline behavior. By default, the cache expires after 10 days. You can override this or force clear the cache on SDK initialization.
The minimum value for cache expiration is 1 day. Any lower value will revert to the default of 10 days. Even if you enable the option to clear the cache on initialization, the SDK will only clear it once per day to avoid excessive network usage.
You can configure cache behavior using the rolloutCacheConfiguration setting:
expirationDays: Number of days to keep cached data before it is considered expired. Default: 10 days.clearOnInit: If set totrue, clears previously stored rollout data when the SDK initializes. Default:false.
The Android SDK does not use SharedPreferences to store the FME cache. It stores the cache directly on internal storage in the app’s context folder.
Integrate with the Harness Proxy
The Harness Proxy allows SDK traffic to securely route through a centralized, authenticated point before reaching the Harness SaaS backend. This provides full visibility and control over network traffic while keeping API keys secure and isolated.
To use the proxy, configure the SDK to point to the proxy host and port during initialization. All SDK requests are then routed through the proxy.
Configure the proxy
Set the proxy host and port when initializing the SDK. These values correspond to your proxy’s address and the port it listens on.
You can optionally configure a CA certificate and/or client certificate and key for mTLS. Certificates should be in PKCS#8 format.
You can also provide credentials using a Bearer Token or basic authentication:
Finally, set the ProxyConfiguration in the SDK config builder:
You can optionally configure a CA certificate and/or client certificate and key for mTLS. Certificates should be in PKCS#8 format.
You can also provide credentials using a Bearer Token or basic authentication:
Finally, set the ProxyConfiguration in the SDK config builder:
Verify connection
After initialization, all SDK requests are routed through the configured proxy. You can verify successful routing by checking your proxy logs or a network monitor for SDK traffic.
Localhost mode
For testing, a developer can put code behind feature flags on their development machine without the SDK requiring network connectivity. To achieve this, you can start the SDK in localhost mode (aka, off-the-grid mode). In this mode, the SDK neither polls or updates Harness servers. Instead, it uses an in-memory data structure to determine what treatments to show to the logged in customer for each of the feature flags. To use the SDK in localhost mode, replace the API Key with localhost, as shown in the example below:
Since version 2.2.0, our SDK supports a new type of localhost feature flag definition file, using the YAML format. This new format allows the user to map different keys to different treatments within a single feature flag, and also add configurations to them. The new format is a list of single-key maps (one per mapping feature_flag-keys-config), defined as follows:
In the example above, we have four entries:
The first entry defines that for feature flag
my_feature, the keykeyreturns the treatmentonand theontreatment is tied to the configuration{"desc" : "this applies only to ON treatment"}.The second entry defines that the feature flag
some_other_featurealways returns theofftreatment and no configuration.The third entry defines that
my_featurealways returnsofffor all keys that don't match another entry (in this case, any key other thankey).The fourth entry shows an example on how to override a treatment for a set of keys.
In this mode, the SDK loads the yaml file from a resource bundle file at the assets' project src/main/assets/splits.yaml.
If a split.yaml or split.yml is not found in assets, the SDK maintains backward compatibility by trying to load the legacy file (split.properties), which is now deprecated.
The format of this file is a properties file as key-value line. The key is the feature flag name, and the value is the treatment name. The following is a sample split.properties file:
Manager
Use the Split Manager to get a list of feature flags available to the SDK factory client. To instantiate a Manager in your code base, use the same factory that you used for your client.
The Manager then has the following methods available.
The SplitView object referenced above has the following structure.
Listener
FME SDKs send impression data back to Harness servers periodically and as a result of evaluating feature flags. To additionally send this information to a location of your choice, define and attach an impression listener.
The SDK sends the generated impressions to the impression listener right away. Because of this, be careful while implementing handling logic to avoid blocking the main thread. Generally speaking, you should create a separate thread to handle incoming impressions. Refer to the snippet below:
In regards with the data available here, refer to the Impression objects interface and information about each field below:
Name
Type
Description
key
String
Key used for targeting (matching). Also used for bucketing unless a specific bucketingKey was provided.
bucketingKey
String
Optional. Key used to control rollout assignment (bucketing) separately from the matching key.
split
String
Feature flag which is evaluated.
treatment
String
Treatment that is returned.
time
Long
Timestamp of when the impression is generated.
appliedRule
String
Targeting rule in the definition that matched resulting in the treatment being returned.
changeNumber
Long
Date and time of the last change to the targeting rule that the SDK used when it served the treatment. It is important to understand when a change made to a feature flag is picked up by the SDKs and whether one of the SDK instances is not picking up changes.
attributes
Map<String, Object>
A map of attributes passed to getTreatment/getTreatments, if any.
previousTime
Long
If SDK is deduping and a matching impression is seen before on the lifetime of the instance this is its timestamp.
Flush
The flush() method sends the data stored in memory (impressions and events) to the Harness FME servers and clears the successfully posted data. If a connection issue is experienced, the data is sent on the next attempt. If you want to flush all pending data when your app goes to background, a good place to call this method is the onPause callback of your MainActivity.
Logging
To enable SDK logging, the logLevel setting is available in SplitClientConfig class:
The following shows an example output:

Configure fallback treatments
Fallback treatments let you define a treatment value (and optional configuration) to be returned when a flag cannot be evaluated. By default, the SDK returns control, but you can override this globally or for individual flags at the SDK level.
This is useful when you want to:
Maintain a predictable user experience during outages or evaluation failures (avoid unexpected
controlin production)Protect critical user flows by returning a safe, stable treatment (for example, forcing
offduring an incident)Customize behavior per flag so each evaluation inherits appropriate safe defaults if something goes wrong
Global fallback treatment
Set a global fallback treatment when initializing the SDK factory. This value is returned whenever any flag cannot be evaluated.
Flag-level fallback treatment
You can also set a fallback treatment per flag when calling getTreatment or getTreatmentWithConfig. This flag-level fallback always takes precedence over the global fallback treatment, so if both are defined, the SDK will return the flag-level value when that flag cannot be evaluated.
For more information, see Fallback treatments.
Advanced use cases
This section describes advanced use cases and features provided by the SDK.
Instantiate multiple SDK clients
In versions previous to 2.10.0, you had to create more that one SDK instance to evaluate for different users IDs. From 2.10.0 on, FME supports the ability to create multiple clients, one for each user ID. For example, if you need to roll out feature flags for different user IDs, you can instantiate multiple clients, one for each ID. You can then evaluate them using the corresponding client.
You can do this using the example below:
Subscribe to events
You can listen for four different events from the SDK.
SDK_READY_FROM_CACHE. This event fires once the SDK is ready to evaluate treatments. The SDK will use a locally cached version of your rollout plan from a previous session if available. In this case, the event fires almost immediately, since access to the cache is fast, but data might be stale. Otherwise, it fires together with theSDK_READYevent when the SDK downloads the rollout plan from Harness servers.SDK_READY. This event fires once the SDK is ready to evaluate treatments using the most up-to-date version of your rollout plan, downloaded from Harness servers.SDK_READY_TIMED_OUT. This event fires if the SDK could not fully download the data from Harness servers (SDK_READYevent), within the time specified by thereadysetting of theSplitClientConfigobject. This event does not indicate that the SDK initialization was interrupted. The SDK continues downloading the rollout plan and fires theSDK_READYevent when finished. This delayedSDK_READYevent may happen with slow connections or large rollout plans with many feature flags, segments, or dynamic configurations.SDK_UPDATE. This event fires whenever your rollout plan is changed. Listen for this event to refresh your app whenever a feature flag or segment is changed in Harness FME.
An event is an extension of a SplitEventTask.
onPostExecution is executed in the background when the event is triggered. This step is used to perform background computation, which can take a long time.
onPostExecutionView is invoked on the UI thread after onPostExecution finishes.
The syntax to listen for an event can be seen below.
Include metadata
metadata provides additional context for events:
onReady/onReadyFromCache: IncludesisInitialCacheLoad(true if no cached data from a previous session was available) andlastUpdateTimestamp(milliseconds since epoch when the cache was last updated).onUpdate: Includestype(FLAGS_UPDATEorSEGMENTS_UPDATE) andnames(list of impacted flags; empty for segment-only updates).
For example:
User consent
The SDK allows you to disable the tracking of events and impressions until user consent is explicitly granted or declined.
The userConsent configuration parameter lets you set the initial consent status of the SDK instance, and the factory method setUserConsent(enabled: Bool) lets you grant (enable) or decline (disable) dynamic data tracking.
There are three possible initial states:
'GRANTED': The user grants consent for tracking events and impressions. The SDK sends them to Harness FME servers. This is the default value ifuserConsentparam is not defined.'DECLINED': The user declines consent for tracking events and impressions. The SDK does not send them to Harness FME servers.'UNKNOWN': The user neither grants nor declines consent for tracking events and impressions. The SDK tracks them in its internal storage, and eventually either sends them or not if the consent status is updated to'GRANTED'or'DECLINED'respectively.
The status can be updated at any time with the setUserConsent factory method.
Working with user consent is demonstrated below.
Certificate pinning
STREAMING INFRASTRUCTURE MIGRATION
If your application uses certificate pinning with streaming.split.io, you may need to update your trusted certificate hashes to support streaming infrastructure migrations and future SDK capabilities.
Add the required SHA-256 hashes for streaming.split.io before removing any existing streaming pins.
For required hashes and migration guidance, see the Certificate Pinning Migration Guide.
The SDK allows you to constrain the certificates that the SDK trusts, using one of the following techniques:
Pin a certificate's
SubjectPublicKeyInfo, by providing the public key as a base64 SHA-256 hash or a base64 SHA-1 hash.Pin a certificate's entire certificate chain (the root, all intermediate, and the leaf certificate), by providing the certificate chain as a .der file.
Each pin corresponds to a host. For subdomains, you can optionally use wildcards, where * will match one subdomain (e.g. *.example.com), and ** will match any number of subdomains (e.g **.example.com).
You can optionally configure a listener to execute on certificate validation failure for a host.
To set the SDK to require pinned certificates for specific hosts, add the CertificatePinningConfiguration object to SplitClientConfig.Builder, as shown below.
Troubleshooting
Impressions not posted when using client.Destroy()
When using the Android SDK, calling client.Destroy() before the app exits is intended to clear the SDK cache and post all impressions.
However, if the app process shuts down before or during the post request, the cached impressions may not appear in the Live Tail tab of the Split user interface because the request fails to reach Harness servers.
To resolve this, either add a call to the client.Flush() method in your app workflow (this is the recommended approach) to post cached impressions and events before shutdown, or add a short delay (2–3 seconds, adjusted for network speed) after calling client.Destroy() to allow enough time for the impressions to be sent before the app exits.
Duplicate class FinalizableReferenceQueue$DirectLoader error when compiling Android SDK app
When compiling an Android app using the Android SDK, you may encounter the following build error:
This error occurs because the Android SDK depends on the Google Guava 18.0 library, while Checkstyle 5.3 depends on an older library com.google.collections » google-collections 1.0. These conflicting dependencies cause the duplicate class definition during compilation.
Upgrade Checkstyle to version 7.0 or higher. Checkstyle 7.0 uses the Google Guava library, which resolves the duplication conflict and allows the project to compile successfully.
SDK takes too long to get ready on Android
When using the Android SDK, the first time the app loads, the SDK takes some time to download feature definitions from Harness FME servers and cache them locally. However, on subsequent app launches, the SDK may still take a long time to get ready even though the cache already exists in the app file system.
This issue occurs in Android SDK versions 2.4.2 and below, where the SDK factory still makes a full data request to Harness FME servers during initialization, regardless of existing cached data on the device.
Upgrade to the latest Android SDK version, which fixes this behavior by properly utilizing the local cache. Additionally, to avoid your app waiting indefinitely on the SDK in case of network issues, listen for the SDK_READY_TIMED_OUT event with a configured timeout. This allows your app to continue functioning even if the SDK fails to become ready promptly.
Another helpful event is SDK_READY_FROM_CACHE, which fires when the SDK uses cached data on initialization, allowing your app to proceed without waiting for network synchronization.
Using Kotlin, SDK always returns the control treatment
When using the Android SDK in a Kotlin project, calling getTreatment() immediately inside the SDK_READY event listener returns the control treatment instead of the expected value. The code works as expected in Swift projects but not in Kotlin.
In Kotlin, the SDK_READY event listener requires overriding the onPostExecution method inside SplitEventTask for the treatment call to work correctly. Using the event listener without this override causes the treatment to be fetched before the SDK is fully ready.
Override the onPostExecution function inside the SplitEventTask to ensure the code runs only after the SDK is ready, as shown below:
HTTP Exception: Chain validation failed
When running the Android app in an emulator, the SDK shows the following error immediately after initialization:
This error originates from the SSL handshake process during the SDK’s network call to https://sdk.split.io. A common cause is the device’s system time being incorrect or out of sync.
Ensure the device’s Date and Time settings are synchronized with the current time. After correcting the time, restart the app to resolve the error.
Last updated
Was this helpful?