Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
244e04b
feat(events): summarize and buffer events on the recording thread (ti…
abelonogov-ld Sep 3, 2026
73dc7d3
build: build java-sdk-internal from a local checkout (temporary)
abelonogov-ld Sep 15, 2026
e5cb947
refactor(events): move AndroidEventBuffer out of java-sdk-internal's …
abelonogov-ld Sep 15, 2026
bf9466b
build: use java-sdk-internal 1.12.0
abelonogov-ld Sep 15, 2026
9201bfa
fix(events): do not send diagnostics while offline or backgrounded
abelonogov-ld Sep 15, 2026
66a92da
refactor(events): rename AndroidEventProcessor and AndroidEventBuffer
abelonogov-ld Sep 15, 2026
0fb38cb
chore(events): clear lint warnings on the event processor and buffer
abelonogov-ld Sep 17, 2026
80f3083
fix warning
abelonogov-ld Sep 18, 2026
9569078
Merge branch 'main' into andrey/event-durability-tier1-buffer
abelonogov-ld Sep 18, 2026
14349d5
docs(events): record that a failed diagnostic init is not retried
abelonogov-ld Sep 18, 2026
6d77bde
fix(events): deliver buffered events when connectivity returns
abelonogov-ld Sep 18, 2026
50110b2
fix(events): honour excludeFromSummaries when folding an evaluation
abelonogov-ld Sep 21, 2026
ca5971a
Update launchdarkly-android-client-sdk/src/main/java/com/launchdarkly…
abelonogov-ld Sep 21, 2026
88c132f
Hold buffered events in the processor and encode outside the lock
abelonogov-ld Sep 21, 2026
e408587
Drop the buffer's logger parameter
abelonogov-ld Sep 21, 2026
4f11eb0
Split internal quality controls into test app
abelonogov-ld Sep 21, 2026
927a2f7
Ignore local.properties in any module directory
abelonogov-ld Sep 21, 2026
30f7b1a
unserializable unit test
abelonogov-ld Sep 22, 2026
f3a19a2
logger
abelonogov-ld Sep 22, 2026
a339448
fix error handlong
abelonogov-ld Sep 22, 2026
bc3431d
Close Budget
abelonogov-ld Sep 22, 2026
04f1c03
Fold the online catch-up delivery into updateScheduledTasks
abelonogov-ld Sep 22, 2026
29991ab
Merge branch 'main' into andrey/event-durability-tier1-buffer
abelonogov-ld Sep 22, 2026
9d69347
Post diagnostic events off the analytics delivery thread
abelonogov-ld Sep 22, 2026
46cca56
Use volatile for the flags that never do a compound update
abelonogov-ld Sep 22, 2026
c949ab4
Make the close checks either locked, deleted, or documented
abelonogov-ld Sep 22, 2026
433cf2e
Bound how many contexts are summarized at once
abelonogov-ld Sep 22, 2026
8f7762c
Reset the counted contexts wherever the counters are reset
abelonogov-ld Sep 22, 2026
b35f4cd
Record an evaluation's counter and events under one lock
abelonogov-ld Sep 22, 2026
115a312
Build the diagnostic period on the thread that posts it
abelonogov-ld Sep 22, 2026
0df02b8
Treat a capacity of nought as one, in both places that bound by it
abelonogov-ld Sep 22, 2026
b414476
Report the capacity the SDK will use, and stop the race test overrunn…
abelonogov-ld Sep 22, 2026
67690b3
Clamp the capacity where it is set, not where it is used
abelonogov-ld Sep 22, 2026
38337ab
Make the diagnostics tests real, and cover shutdown and the flush race
abelonogov-ld Sep 22, 2026
af84244
Keep context-limit test payloads small enough for the test server
abelonogov-ld Sep 23, 2026
f3a0f75
Document that close() discards events held while offline
abelonogov-ld Sep 23, 2026
f1263bc
Keep the flush interval running while offline instead of delivering o…
abelonogov-ld Sep 23, 2026
9c8c889
Make stopping scheduled event tasks independent of the closed check
abelonogov-ld Sep 23, 2026
d27473a
Isolate recording exceptions and reset context limit warning per run
abelonogov-ld Sep 23, 2026
15d3ffa
Tighten recording error handling and the per-run context warning
abelonogov-ld Sep 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.gradle
/local.properties
local.properties
.DS_Store
build
/captures
Expand Down
2 changes: 0 additions & 2 deletions example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import java.util.Properties

plugins {
id("com.android.application")
// make sure this line comes *after* you apply the Android plugin
id("com.getkeepsafe.dexcount")
}

// local.properties is not checked in, so it is where machine-specific settings such as your mobile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@

import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;

import timber.log.Timber;
Expand All @@ -45,6 +41,9 @@ public class MainActivity extends AppCompatActivity {

private static final String DEFAULT_USER_KEY = "user key";

/** How long startup blocks waiting for the first flags to arrive. */
private static final int INIT_WAIT_SECONDS = 10;

private LDClient ldClient;
private LDStatusListener ldStatusListener;
private LDAllFlagsListener allFlagsListener;
Expand Down Expand Up @@ -142,15 +141,12 @@ public void onCreate(Bundle savedInstanceState) {
.set("email", "fake@example.com")
.build();

Future<LDClient> initFuture = LDClient.init(this.getApplication(), ldConfig, context);
try {
ldClient = initFuture.get(10, TimeUnit.SECONDS);
updateStatusString(ldClient.getConnectionInformation());
ldClient.registerStatusListener(ldStatusListener);
ldClient.registerAllFlagsListener(allFlagsListener);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Timber.e(e, "Exception when awaiting LaunchDarkly Client initialization");
}
// Returns the client either way: if the flags have not arrived within the wait, it is usable
// with whatever it has cached.
ldClient = LDClient.init(this.getApplication(), ldConfig, context, INIT_WAIT_SECONDS);
updateStatusString(ldClient.getConnectionInformation());
ldClient.registerStatusListener(ldStatusListener);
ldClient.registerAllFlagsListener(allFlagsListener);
}

private void setupListeners() {
Expand Down
2 changes: 1 addition & 1 deletion launchdarkly-android-client-sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ ext.versions = [
"jacksonDatabind": "2.10.5.1",
"junit": "4.13",
"launchdarklyJavaSdkCommon": "2.4.0",
"launchdarklyJavaSdkInternal": "1.9.0",
"launchdarklyJavaSdkInternal": "1.12.0",
"launchdarklyLogging": "1.1.1",
"okhttp": "4.12.0",
"testHelpers": "2.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,9 @@
import com.launchdarkly.sdk.android.subsystems.HookConfiguration;
import com.launchdarkly.sdk.android.subsystems.HttpConfiguration;
import com.launchdarkly.sdk.android.subsystems.PluginsConfiguration;
import com.launchdarkly.sdk.internal.events.DefaultEventProcessor;
import com.launchdarkly.sdk.internal.events.DefaultEventSender;
import com.launchdarkly.sdk.internal.events.Event;
import com.launchdarkly.sdk.internal.events.EventsConfiguration;
import com.launchdarkly.sdk.internal.events.EventSender;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -101,33 +98,33 @@ static final class EventProcessorBuilderImpl extends EventProcessorBuilder
@Override
public EventProcessor build(ClientContext clientContext) {
ClientContextImpl clientContextImpl = ClientContextImpl.get(clientContext);
EventsConfiguration eventsConfig = new EventsConfiguration(
allAttributesPrivate,
capacity,
null, // contextDeduplicator - not needed for client-side use
diagnosticRecordingIntervalMillis,
clientContextImpl.getDiagnosticStore(),
new DefaultEventSender(
LDUtil.makeHttpProperties(clientContext),
StandardEndpoints.ANALYTICS_EVENTS_REQUEST_PATH,
StandardEndpoints.DIAGNOSTIC_EVENTS_REQUEST_PATH,
0L, // use default retry delay
false, // disable gzip compression for Android
EventSender eventSender = new DefaultEventSender(
LDUtil.makeHttpProperties(clientContext),
StandardEndpoints.ANALYTICS_EVENTS_REQUEST_PATH,
StandardEndpoints.DIAGNOSTIC_EVENTS_REQUEST_PATH,
0L, // use default retry delay
false, // disable gzip compression for Android
clientContext.getBaseLogger());
return new DirectEventProcessor(
new OutboundEventBuffer(
allAttributesPrivate,
privateAttributes,
true, // perContextSummarization - enable for client SDK
capacity,
clientContext.getBaseLogger()),
1, // eventSendingThreadPoolSize
eventSender,
clientContext.getServiceEndpoints().getEventsBaseUri(),
clientContextImpl.getDiagnosticStore(),
capacity,
flushIntervalMillis,
diagnosticRecordingIntervalMillis,
DirectEventProcessor.DEFAULT_CLOSE_BUDGET_MILLIS,
clientContext.isInBackground(),
true, // initiallyOffline
privateAttributes,
true // perContextSummarization - enable for client SDK
);
return new DefaultEventProcessorWrapper(new DefaultEventProcessor(
eventsConfig,
EventUtil.makeEventsTaskExecutor(),
Thread.NORM_PRIORITY, // note, we may want to make this configurable as it is in java-server-sdk
EventUtil.makeDiagnosticsTaskExecutor(),
clientContext.getBaseLogger()
));
);
}

@Override
Expand All @@ -140,72 +137,6 @@ public LDValue describeConfiguration(ClientContext clientContext) {
.put("eventsFlushIntervalMillis", flushIntervalMillis)
.build();
}

/**
* Adapter from the public component interface of EventProcessor to the internal
* implementation class from java-sdk-internal.
*/
private final class DefaultEventProcessorWrapper implements EventProcessor {
private final DefaultEventProcessor eventProcessor;

DefaultEventProcessorWrapper(DefaultEventProcessor eventProcessor) {
this.eventProcessor = eventProcessor;
}

@Override
public void recordEvaluationEvent(
LDContext context,
String flagKey,
int flagVersion,
int variation,
LDValue value,
EvaluationReason reason,
LDValue defaultValue,
boolean requireFullEvent,
Long debugEventsUntilDate
) {
eventProcessor.sendEvent(new Event.FeatureRequest(
System.currentTimeMillis(), flagKey, context, flagVersion, variation,
value, defaultValue, reason, null, requireFullEvent,
debugEventsUntilDate, false));
}

@Override
public void recordIdentifyEvent(LDContext context) {
eventProcessor.sendEvent(new Event.Identify(System.currentTimeMillis(), context));
}

@Override
public void recordCustomEvent(LDContext context, String eventKey, LDValue data, Double metricValue) {
eventProcessor.sendEvent(new Event.Custom(System.currentTimeMillis(), eventKey,
context, data, metricValue));
}

@Override
public void setInBackground(boolean inBackground) {
eventProcessor.setInBackground(inBackground);
}

@Override
public void setOffline(boolean offline) {
eventProcessor.setOffline(offline);
}

@Override
public void flush() {
eventProcessor.flushAsync();
}

@Override
public void blockingFlush() {
eventProcessor.flushBlocking();
}

@Override
public void close() throws IOException {
eventProcessor.close();
}
}
}

static final class HttpConfigurationBuilderImpl extends HttpConfigurationBuilder
Expand Down
Loading
Loading