Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 0 additions & 3 deletions .github/workflows/sonarqube.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ on:
pull_request:
branches:
- '*'
push:
branches:
- master

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
5.5.1 (May 22, 2026)
- Fixed race condition in which killing the SDK process during first initialization could leave cache in an inconsistent state.
- Refactored internal SDK components into standalone modules.

5.5.0 (Jan 28, 2026)
- Added functionality to provide metadata alongside SDK update, ready and ready from cache events. Read more in our docs.
- Fixed issue in which TLS 1.2 was being forced for new connections.
Expand Down
1 change: 1 addition & 0 deletions android-commons-root.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Minimal root build file used when android-client is included as a subproject.
2 changes: 2 additions & 0 deletions api/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/build
.classpath
.settings
2 changes: 1 addition & 1 deletion api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ plugins {
id 'com.android.library'
}

apply from: "$rootDir/gradle/common-android-library.gradle"
apply from: "$projectDir/../gradle/common-android-library.gradle"

android {
namespace 'io.split.android.client.api'
Expand Down
6 changes: 6 additions & 0 deletions backoff/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/build
.gradle
*.iml
.DS_Store
.classpath
.settings
42 changes: 42 additions & 0 deletions backoff/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Backoff module

This module contains the backoff counter logic for the Split SDK.

It provides the types used to calculate retry delays in HTTP infrastructure components such as `RetryableHttpClient`.

Key types:
- `BackoffCounter` — interface with `getNextRetryTime()` and `resetCounter()`
- `ExponentialBackoffCounter` — exponential backoff implementation (base * 2^attempt, capped at a configurable max)
- `FixedIntervalBackoffCounter` — fixed-interval implementation (no-op reset)

## Usage

**Exponential backoff** (doubles each attempt, capped at 30 minutes by default):

```java
BackoffCounter counter = new ExponentialBackoffCounter(1); // base of 1 second

long delay = counter.getNextRetryTime(); // 1s
delay = counter.getNextRetryTime(); // 2s
delay = counter.getNextRetryTime(); // 4s
delay = counter.getNextRetryTime(); // 8s
// ... capped at 1800s (30 min)

counter.resetCounter(); // start over
delay = counter.getNextRetryTime(); // 1s again
```

A custom cap can be specified via the two-argument constructor:

```java
BackoffCounter counter = new ExponentialBackoffCounter(1, /* maxTimeLimit= */ 60);
```

**Fixed-interval backoff** (always returns the same delay, `resetCounter()` is a no-op):

```java
BackoffCounter counter = new FixedIntervalBackoffCounter(5); // 5 seconds

long delay = counter.getNextRetryTime(); // 5s
delay = counter.getNextRetryTime(); // 5s
```
18 changes: 18 additions & 0 deletions backoff/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
plugins {
id 'com.android.library'
}

apply from: "$projectDir/../gradle/common-android-library.gradle"

android {
namespace 'io.split.android.client.backoff'

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {
testImplementation libs.junit4
}
3 changes: 3 additions & 0 deletions backoff/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.split.android.client.service.sseclient;
package io.split.android.client.backoff;

public interface BackoffCounter {
long getNextRetryTime();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.split.android.client.service.sseclient;
package io.split.android.client.backoff;

import java.util.concurrent.atomic.AtomicLong;

public class ReconnectBackoffCounter implements BackoffCounter {
public class ExponentialBackoffCounter implements BackoffCounter {
private final static int MAX_TIME_LIMIT_IN_SECS = 1800; // 30 minutes (30 * 60)
private final static int RETRY_EXPONENTIAL_BASE = 2;
private final int mBackoffBase;
Expand All @@ -12,15 +12,15 @@ public class ReconnectBackoffCounter implements BackoffCounter {
/**
* @param backoffBase the base of the backoff in seconds
*/
public ReconnectBackoffCounter(int backoffBase) {
public ExponentialBackoffCounter(int backoffBase) {
this(backoffBase, MAX_TIME_LIMIT_IN_SECS);
}

/**
* @param backoffBase the base of the backoff in seconds
* @param maxTimeLimit the maximum time limit in seconds
*/
public ReconnectBackoffCounter(int backoffBase, int maxTimeLimit) {
public ExponentialBackoffCounter(int backoffBase, int maxTimeLimit) {
mBackoffBase = backoffBase;
mAttemptCount = new AtomicLong(0);
mMaxTimeLimit = maxTimeLimit;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.split.android.client.service.sseclient;
package io.split.android.client.backoff;

public class FixedIntervalBackoffCounter implements BackoffCounter {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package io.split.android.client.service.sseclient;
package io.split.android.client.backoff;

import org.junit.Assert;
import org.junit.Test;

public class ReconnectBackoffCounterTest {
public class ExponentialBackoffCounterTest {

@Test
public void base1() {
Expand Down Expand Up @@ -32,7 +32,7 @@ public void base8() {
@Test
public void maxWaitTimeIsTakenIntoAccount() {
int maxTimeLimit = 100;
BackoffCounter counter = new ReconnectBackoffCounter(1, maxTimeLimit);
BackoffCounter counter = new ExponentialBackoffCounter(1, maxTimeLimit);

long lastTime = 0;
for (int i = 0; i < 8; i++) {
Expand All @@ -45,7 +45,7 @@ public void maxWaitTimeIsTakenIntoAccount() {
@Test
public void defaultMaxWaitTimeIsTakenIntoAccount() {
int maxTimeLimit = 1800;
BackoffCounter counter = new ReconnectBackoffCounter(1, maxTimeLimit);
BackoffCounter counter = new ExponentialBackoffCounter(1, maxTimeLimit);

long lastTime = 0;
for (int i = 0; i < 12; i++) {
Expand All @@ -57,7 +57,7 @@ public void defaultMaxWaitTimeIsTakenIntoAccount() {

private void testWithBase(int base, long[] results) {
BackoffCounter counter
= new ReconnectBackoffCounter(base);
= new ExponentialBackoffCounter(base);
long v1 = counter.getNextRetryTime();
long v2 = counter.getNextRetryTime();
long v3 = counter.getNextRetryTime();
Expand Down
29 changes: 21 additions & 8 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ buildscript {

apply plugin: 'com.android.fused-library'
apply plugin: 'com.vanniktech.maven.publish'
apply from: "$rootDir/gradle/jacoco-root.gradle"
if (rootProject.name == 'android-client') {
apply from: "$projectDir/gradle/jacoco-root.gradle"
} else {
logger.lifecycle("Skipping android-client root JaCoCo config in nested build: ${rootProject.name}")
}

ext {
splitVersion = '5.5.0'
splitVersion = '5.5.1'
jacocoVersion = '0.8.8'
}

Expand Down Expand Up @@ -68,7 +72,7 @@ tasks.register('sonar') {
if (sonarOrg) cmd.add("-Dsonar.organization=${sonarOrg}")
cmd.add("-Dsonar.projectVersion=${splitVersion}")

def proc = new ProcessBuilder(cmd).directory(rootDir).inheritIO().start()
def proc = new ProcessBuilder(cmd).directory(projectDir).inheritIO().start()
if (proc.waitFor() != 0) {
throw new GradleException("sonar-scanner failed")
}
Expand Down Expand Up @@ -135,11 +139,20 @@ repositories {
}

dependencies {
include project(':main')
include project(':logger')
include project(':events')
include project(':events-domain')
include project(':api')
def resolveProjectPath = { String moduleName ->
def nestedPath = (project.path != ':') ? "${project.path}:${moduleName}" : null
def candidates = [":${moduleName}", nestedPath].findAll { it != null }
return candidates.find { findProject(it) != null }
}

['main', 'logger', 'events', 'events-domain', 'api', 'http-api', 'http', 'fallback', 'backoff', 'tracker', 'submitter', 'streaming', 'streaming-support', 'executor'].each { moduleName ->
def resolvedPath = resolveProjectPath(moduleName)
if (resolvedPath != null) {
include project(resolvedPath)
} else {
logger.lifecycle("Skipping fused include for '${moduleName}' because no matching project path was found.")
}
}
}

def javadocSourceProjects = providers.provider {
Expand Down
2 changes: 2 additions & 0 deletions events-domain/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/build
.classpath
.settings
9 changes: 5 additions & 4 deletions events-domain/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ plugins {
id 'com.android.library'
}

apply from: "$rootDir/gradle/common-android-library.gradle"
apply from: "$projectDir/../gradle/common-android-library.gradle"

android {
namespace 'io.split.android.client.events'
Expand All @@ -16,9 +16,10 @@ android {
dependencies {
implementation libs.annotation

implementation project(':api')
implementation project(':events')
implementation project(':logger')
api clientModuleProject('executor')
implementation clientModuleProject('api')
implementation clientModuleProject('events')
implementation clientModuleProject('logger')

testImplementation libs.junit4
testImplementation libs.mockitoCore
Expand Down

This file was deleted.

4 changes: 3 additions & 1 deletion events/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/build
/build
.classpath
.settings
2 changes: 1 addition & 1 deletion events/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ plugins {
id 'com.android.library'
}

apply from: "$rootDir/gradle/common-android-library.gradle"
apply from: "$projectDir/../gradle/common-android-library.gradle"

android {
namespace 'io.harness.events'
Expand Down
76 changes: 76 additions & 0 deletions events/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>io.harness</groupId>
<artifactId>events</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<description>Generic event-bus framework (pure Java)</description>

<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<!-- Annotations -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>23.0.0</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.8.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.8.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
5 changes: 5 additions & 0 deletions executor/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/build
.gradle
local.properties
.classpath
.settings
Loading
Loading