From 5581d5e3373432b09c49788ff8c3148bba0f5b1d Mon Sep 17 00:00:00 2001 From: Bee Klimt Date: Wed, 18 Mar 2026 09:51:15 -0700 Subject: [PATCH 1/4] fix: Call identify hooks during init. --- .../sdk/android/LDClientHooksTest.java | 22 ++++++++++--------- .../sdk/android/LDClientPluginsTest.java | 4 ++++ .../launchdarkly/sdk/android/LDClient.java | 3 +++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientHooksTest.java b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientHooksTest.java index 36b24bae..485765ea 100644 --- a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientHooksTest.java +++ b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientHooksTest.java @@ -61,11 +61,12 @@ public void executesHooksRegisteredDuringConfiguration() throws Exception { IdentifySeriesContext identifySeriesContext = new IdentifySeriesContext(newContext, null); IdentifySeriesResult identifySeriesResult = new IdentifySeriesResult(IdentifySeriesResult.IdentifySeriesStatus.COMPLETED); - assertEquals(1, testHook.beforeIdentifyCalls.size()); - assertEquals(identifySeriesContext, testHook.beforeIdentifyCalls.get(0).get("seriesContext")); - assertEquals(1, testHook.afterIdentifyCalls.size()); - assertEquals(identifySeriesContext, testHook.afterIdentifyCalls.get(0).get("seriesContext")); - assertEquals(identifySeriesResult, testHook.afterIdentifyCalls.get(0).get("result")); + // index 0 is the implicit identify from init; index 1 is the explicit identify call + assertEquals(2, testHook.beforeIdentifyCalls.size()); + assertEquals(identifySeriesContext, testHook.beforeIdentifyCalls.get(1).get("seriesContext")); + assertEquals(2, testHook.afterIdentifyCalls.size()); + assertEquals(identifySeriesContext, testHook.afterIdentifyCalls.get(1).get("seriesContext")); + assertEquals(identifySeriesResult, testHook.afterIdentifyCalls.get(1).get("result")); ldClient.trackMetric("test-event", LDValue.buildObject().put("data", "test").build(), 123.45); @@ -149,11 +150,12 @@ public void executesBothInitialHooksAndHooksAddedWithAddHooks() throws Exception IdentifySeriesContext identifySeriesContext = new IdentifySeriesContext(newContext, null); IdentifySeriesResult identifySeriesResult = new IdentifySeriesResult(IdentifySeriesResult.IdentifySeriesStatus.COMPLETED); - assertEquals(1, testHook.beforeIdentifyCalls.size()); - assertEquals(identifySeriesContext, testHook.beforeIdentifyCalls.get(0).get("seriesContext")); - assertEquals(1, testHook.afterIdentifyCalls.size()); - assertEquals(identifySeriesContext, testHook.afterIdentifyCalls.get(0).get("seriesContext")); - assertEquals(identifySeriesResult, testHook.afterIdentifyCalls.get(0).get("result")); + // index 0 is the implicit identify from init; index 1 is the explicit identify call + assertEquals(2, testHook.beforeIdentifyCalls.size()); + assertEquals(identifySeriesContext, testHook.beforeIdentifyCalls.get(1).get("seriesContext")); + assertEquals(2, testHook.afterIdentifyCalls.size()); + assertEquals(identifySeriesContext, testHook.afterIdentifyCalls.get(1).get("seriesContext")); + assertEquals(identifySeriesResult, testHook.afterIdentifyCalls.get(1).get("result")); assertEquals(1, addedHook.beforeIdentifyCalls.size()); assertEquals(identifySeriesContext, addedHook.beforeIdentifyCalls.get(0).get("seriesContext")); diff --git a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java index 8f08239b..bf809988 100644 --- a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java +++ b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java @@ -51,6 +51,9 @@ public void registerIsCalledForPlugins() throws Exception { MockPlugin testPlugin = new MockPlugin(Collections.singletonList(testHook)); try (LDClient ldClient = LDClient.init(application, makeOfflineConfig(List.of(testPlugin)), ldContext, 1)) { + // This should get called because of an implicit identity. + assertEquals(1, testHook.beforeIdentifyCalls.size()); + ldClient.boolVariation("test-flag", false); assertEquals(1, testPlugin.getHooksCalls.size()); assertEquals(1, testPlugin.registerCalls.size()); @@ -84,6 +87,7 @@ public void pluginRegisterCalledForEachClientEnvironment() throws Exception { "secondaryEnvironment", secondaryMobileKey )) .plugins(Components.plugins().setPlugins(Collections.singletonList(testPlugin))) + //.hooks(Components.hooks().setHooks(Collections.singletonList(testHook))) .offline(true) .events(Components.noEvents()) .logAdapter(logging.logAdapter); diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java index d081870e..8919d702 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java @@ -248,11 +248,14 @@ public static Future init(@NonNull Application application, } } + HookRunner.AfterIdentifyMethod afterIdentify = primaryClient.hookRunner.identify(modifiedContext, null); + final AtomicInteger initCounter = new AtomicInteger(config.getMobileKeys().size()); Callback completeWhenCounterZero = new Callback() { @Override public void onSuccess(Void result) { if (initCounter.decrementAndGet() == 0) { + afterIdentify.invoke(new IdentifySeriesResult(IdentifySeriesResult.IdentifySeriesStatus.COMPLETED)); resultFuture.set(primaryClient); } } From c9d16b751369e95b6f4b4125fe15fde5657eedaa Mon Sep 17 00:00:00 2001 From: Bee Klimt Date: Thu, 19 Mar 2026 12:11:14 -0700 Subject: [PATCH 2/4] remove a commented out line --- .../java/com/launchdarkly/sdk/android/LDClientPluginsTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java index bf809988..ffb06d38 100644 --- a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java +++ b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/sdk/android/LDClientPluginsTest.java @@ -87,7 +87,6 @@ public void pluginRegisterCalledForEachClientEnvironment() throws Exception { "secondaryEnvironment", secondaryMobileKey )) .plugins(Components.plugins().setPlugins(Collections.singletonList(testPlugin))) - //.hooks(Components.hooks().setHooks(Collections.singletonList(testHook))) .offline(true) .events(Components.noEvents()) .logAdapter(logging.logAdapter); From a593d1b687822ae1a2e9264c93d6e3beb53bac36 Mon Sep 17 00:00:00 2001 From: Bee Klimt Date: Thu, 19 Mar 2026 15:07:14 -0700 Subject: [PATCH 3/4] fix: call afterIdenfity in the error path --- .../src/main/java/com/launchdarkly/sdk/android/LDClient.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java index 8919d702..19710949 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java @@ -251,6 +251,7 @@ public static Future init(@NonNull Application application, HookRunner.AfterIdentifyMethod afterIdentify = primaryClient.hookRunner.identify(modifiedContext, null); final AtomicInteger initCounter = new AtomicInteger(config.getMobileKeys().size()); + final AtomicBoolean initErrorOccurred = new AtomicBoolean(false); Callback completeWhenCounterZero = new Callback() { @Override public void onSuccess(Void result) { @@ -262,6 +263,9 @@ public void onSuccess(Void result) { @Override public void onError(Throwable e) { + if (initErrorOccurred.compareAndSet(false, true)) { + afterIdentify.invoke(new IdentifySeriesResult(IdentifySeriesResult.IdentifySeriesStatus.ERROR)); + } resultFuture.setException(e); } }; From 291c11f5a2bd766d178c53c235509065698cafbe Mon Sep 17 00:00:00 2001 From: Bee Klimt Date: Fri, 20 Mar 2026 15:03:18 -0700 Subject: [PATCH 4/4] fix: run the identify hooks for each instance, not just the primary one --- .../launchdarkly/sdk/android/LDClient.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java index 19710949..7e7af17b 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/sdk/android/LDClient.java @@ -248,31 +248,33 @@ public static Future init(@NonNull Application application, } } - HookRunner.AfterIdentifyMethod afterIdentify = primaryClient.hookRunner.identify(modifiedContext, null); - final AtomicInteger initCounter = new AtomicInteger(config.getMobileKeys().size()); - final AtomicBoolean initErrorOccurred = new AtomicBoolean(false); - Callback completeWhenCounterZero = new Callback() { + class CompleteWhenCounterZero implements Callback { + final private HookRunner.AfterIdentifyMethod afterIdentify; + + CompleteWhenCounterZero(HookRunner.AfterIdentifyMethod afterIdentify) { + this.afterIdentify = afterIdentify; + } + @Override public void onSuccess(Void result) { + afterIdentify.invoke(new IdentifySeriesResult(IdentifySeriesResult.IdentifySeriesStatus.COMPLETED)); if (initCounter.decrementAndGet() == 0) { - afterIdentify.invoke(new IdentifySeriesResult(IdentifySeriesResult.IdentifySeriesStatus.COMPLETED)); resultFuture.set(primaryClient); } } @Override public void onError(Throwable e) { - if (initErrorOccurred.compareAndSet(false, true)) { - afterIdentify.invoke(new IdentifySeriesResult(IdentifySeriesResult.IdentifySeriesStatus.ERROR)); - } + afterIdentify.invoke(new IdentifySeriesResult(IdentifySeriesResult.IdentifySeriesStatus.ERROR)); resultFuture.setException(e); } }; // Start up all instances for (final LDClient instance : instances.values()) { - if (instance.connectivityManager.startUp(completeWhenCounterZero)) { + HookRunner.AfterIdentifyMethod afterIdentify = instance.hookRunner.identify(modifiedContext, null); + if (instance.connectivityManager.startUp(new CompleteWhenCounterZero(afterIdentify))) { instance.eventProcessor.recordIdentifyEvent(modifiedContext); } }