diff --git a/example/src/main/java/com/launchdarkly/example/MainActivity.java b/example/src/main/java/com/launchdarkly/example/MainActivity.java index 9b604324..35d44d16 100644 --- a/example/src/main/java/com/launchdarkly/example/MainActivity.java +++ b/example/src/main/java/com/launchdarkly/example/MainActivity.java @@ -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; @@ -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; @@ -102,6 +101,7 @@ public void onCreate(Bundle savedInstanceState) { setupFlushButton(); setupTrackButton(); setupIdentifyButton(); + setupKillUnsentButton(); setupOfflineSwitch(); setupListeners(); updateDedupeStatus(); @@ -142,15 +142,12 @@ public void onCreate(Bundle savedInstanceState) { .set("email", "fake@example.com") .build(); - Future 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() { @@ -215,6 +212,29 @@ private void setupTrackButton() { }); } + /** + * Reproduces in-memory event loss: evaluate (exposure) and track (stand-in for an error), + * wait 5s so both calls are queued, then kill the process before the 30s flush. + * {@code finish()} or backgrounding would run the SDK's background flush, so this uses + * {@link android.os.Process#killProcess}. + */ + private void setupKillUnsentButton() { + Button killUnsentButton = findViewById(R.id.kill_unsent_button); + killUnsentButton.setOnClickListener(v -> { + final String typedKey = ((EditText) findViewById(R.id.feature_flag_key)).getText().toString().trim(); + final String flagKey = typedKey.isEmpty() ? "kill-flag" : typedKey; + Timber.w("eval+track+kill flag=%s", flagKey); + doSafeClientAction(() -> { + ldClient.boolVariation(flagKey, false); + ldClient.track("$ld:telemetry:error"); + ldClient.flush(); + new Handler(Looper.getMainLooper()).postDelayed( + () -> android.os.Process.killProcess(android.os.Process.myPid()), + 5_000); + }); + }); + } + private void setupIdentifyButton() { Button identify = findViewById(R.id.identify_button); identify.setOnClickListener(v -> { diff --git a/example/src/main/res/layout/activity_main.xml b/example/src/main/res/layout/activity_main.xml index 8657d607..06c5edb9 100644 --- a/example/src/main/res/layout/activity_main.xml +++ b/example/src/main/res/layout/activity_main.xml @@ -114,6 +114,17 @@ android:layout_alignParentRight="true" android:minLines="4" /> +