diff --git a/README.md b/README.md index 3e623ce3..c5e7d79c 100644 --- a/README.md +++ b/README.md @@ -41,4 +41,3 @@ We encourage pull requests and other contributions from the community. Check out * [docs.launchdarkly.com](https://docs.launchdarkly.com/ "LaunchDarkly Documentation") for our documentation and SDK reference guides * [apidocs.launchdarkly.com](https://apidocs.launchdarkly.com/ "LaunchDarkly API Documentation") for our API documentation * [blog.launchdarkly.com](https://blog.launchdarkly.com/ "LaunchDarkly Blog Documentation") for the latest product updates - * [Feature Flagging Guide](https://github.com/launchdarkly/featureflags/ "Feature Flagging Guide") for best practices and strategies \ No newline at end of file diff --git a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/android/DiagnosticEventProcessorTest.java b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/android/DiagnosticEventProcessorTest.java index 91f8c544..810ec98c 100644 --- a/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/android/DiagnosticEventProcessorTest.java +++ b/launchdarkly-android-client-sdk/src/androidTest/java/com/launchdarkly/android/DiagnosticEventProcessorTest.java @@ -122,4 +122,15 @@ public void defaultDiagnosticRequestIncludingAdditionalHeaders() throws Interrup assertEquals("foo", r.getHeader("Authorization")); assertEquals(GsonCache.getGson().toJson(testEvent), r.getBody().readUtf8()); } + + @Test + public void closeWithoutStart() { + ForegroundTestController.setup(false); + OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); + + LDConfig ldConfig = new LDConfig.Builder().setMobileKey("test-mobile-key").build(); + DiagnosticStore diagnosticStore = new DiagnosticStore(activityTestRule.getActivity().getApplication(), "test-mobile-key"); + DiagnosticEventProcessor diagnosticEventProcessor = new DiagnosticEventProcessor(ldConfig, "default", diagnosticStore, okHttpClient); + diagnosticEventProcessor.close(); + } } diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/DiagnosticEventProcessor.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/DiagnosticEventProcessor.java index 351865b4..63a47a6c 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/DiagnosticEventProcessor.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/DiagnosticEventProcessor.java @@ -85,7 +85,9 @@ public void run() { } private void stopScheduler() { - executorService.shutdown(); + if (executorService != null) { + executorService.shutdown(); + } } void close() { diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/LDClient.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/LDClient.java index 3a3d3bdf..45ab3530 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/LDClient.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/LDClient.java @@ -424,7 +424,8 @@ private EvaluationDetail variationDetailInternal(String flagKey, T fallba result = new EvaluationDetail<>(flag.getReason(), flag.getVariation(), fallback); valueJson = fallbackJson; } else { - T value = typeConverter.valueFromJson(valueJson); + T value = typeConverter.valueFromJson(flagKey, valueJson); + if (value == null) { Timber.e("Attempted to get flag with wrong type for key: %s Returning fallback: %s", flagKey, fallback); result = EvaluationDetail.error(EvaluationReason.ErrorKind.WRONG_TYPE, fallback); diff --git a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/ValueTypes.java b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/ValueTypes.java index 4e04b34a..fc964466 100644 --- a/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/ValueTypes.java +++ b/launchdarkly-android-client-sdk/src/main/java/com/launchdarkly/android/ValueTypes.java @@ -23,7 +23,7 @@ public interface Converter { * @return the converted value, or null if the JSON value was not of the correct type */ @Nullable - T valueFromJson(@NonNull JsonElement jsonValue); + T valueFromJson(String key, @NonNull JsonElement jsonValue); /** * Converts a value to JSON. The value is guaranteed to be non-null. @@ -36,7 +36,7 @@ public interface Converter { static final Converter BOOLEAN = new Converter() { @Override - public Boolean valueFromJson(@NonNull JsonElement jsonValue) { + public Boolean valueFromJson(String key, @NonNull JsonElement jsonValue) { return (jsonValue.isJsonPrimitive() && jsonValue.getAsJsonPrimitive().isBoolean()) ? jsonValue.getAsBoolean() : null; } @@ -49,7 +49,7 @@ public JsonElement valueToJson(@NonNull Boolean value) { static final Converter INT = new Converter() { @Override - public Integer valueFromJson(@NonNull JsonElement jsonValue) { + public Integer valueFromJson(String key, @NonNull JsonElement jsonValue) { return (jsonValue.isJsonPrimitive() && jsonValue.getAsJsonPrimitive().isNumber()) ? jsonValue.getAsInt() : null; } @@ -62,7 +62,7 @@ public JsonElement valueToJson(@NonNull Integer value) { static final Converter FLOAT = new Converter() { @Override - public Float valueFromJson(@NonNull JsonElement jsonValue) { + public Float valueFromJson(String key, @NonNull JsonElement jsonValue) { return (jsonValue.isJsonPrimitive() && jsonValue.getAsJsonPrimitive().isNumber()) ? jsonValue.getAsFloat() : null; } @@ -75,7 +75,7 @@ public JsonElement valueToJson(@NonNull Float value) { static final Converter DOUBLE = new Converter() { @Override - public Double valueFromJson(@NonNull JsonElement jsonValue) { + public Double valueFromJson(String key, @NonNull JsonElement jsonValue) { return (jsonValue.isJsonPrimitive() && jsonValue.getAsJsonPrimitive().isNumber()) ? jsonValue.getAsDouble() : null; } @@ -88,7 +88,7 @@ public JsonElement valueToJson(@NonNull Double value) { static final Converter STRING = new Converter() { @Override - public String valueFromJson(@NonNull JsonElement jsonValue) { + public String valueFromJson(String key, @NonNull JsonElement jsonValue) { return (jsonValue.isJsonPrimitive() && jsonValue.getAsJsonPrimitive().isString()) ? jsonValue.getAsString() : null; } @@ -103,13 +103,14 @@ public JsonElement valueToJson(@NonNull String value) { // TODO(gwhelanld): remove in 3.0.0 static final Converter STRINGCOMPAT = new Converter() { @Override - public String valueFromJson(@NonNull JsonElement jsonValue) { + public String valueFromJson(String key, @NonNull JsonElement jsonValue) { if (jsonValue.isJsonPrimitive() && jsonValue.getAsJsonPrimitive().isString()) { return jsonValue.getAsString(); } else if (!jsonValue.isJsonPrimitive() && !jsonValue.isJsonNull()) { - Timber.w("JSON flag requested as String. For backwards compatibility " + + Timber.w("JSON flag `" + key + "` requested as String. For backwards compatibility " + "returning a serialized representation of flag value. " + "This behavior will be removed in the next major version (3.0.0)"); + return GsonCache.getGson().toJson(jsonValue); } return null; @@ -124,7 +125,7 @@ public JsonElement valueToJson(@NonNull String value) { static final Converter JSON = new Converter() { @Override - public JsonElement valueFromJson(@NonNull JsonElement jsonValue) { + public JsonElement valueFromJson(String key, @NonNull JsonElement jsonValue) { return jsonValue; }