Android Client SDK for Experiment
See the Documentation for getting started.
License: MIT License
Android Client SDK for Experiment
See the Documentation for getting started.
implementation("com.amplitude:experiment-android-client:1.8.1")
With this version Crashlyitcs is showing ANR's in the get()
using the sample code from the Quickstart Documentation.
try {
client.fetch(user).get()
} catch (e: Exception) {
e.printStackTrace()
}
We had to bandaid this with a 4 second timeout.
kotlin.runCatching {
val client : ExperimentClient = Experiment.initialize(this, BuildConfig.amplitudeKey, config)
// Added a 4 sec timeout due to production ANR's from this call previously when we did not have a timeout.
client.fetch().get(TIMEOUT_AMPLITUDE_SETUP_SEC, TimeUnit.SECONDS)
// etc.
}.onFailure {
Timber.e("Amplitude experiment failed to initialize")
}
Any thoughts on why we are getting ANR's in the first place in production env?
We run an experiment before the user log in and after they log in. We initialize the client like so:
val config = ExperimentConfig.builder()
.build()
val experiment = Experiment.initializeWithAmplitudeAnalytics(app, $API_KEY config,)
val user = ExperimentUser.builder()
.userId(userAuth0Id)
.deviceId(deviceIdFromAnalytics)
.build()
experiment?.start(user)?.get()
Before the user logs in, userAuth0Id
is null
and calling experiment?.variant(variantKey)
returns the expected variant.
After logging in, we call the code above with a valid userAuth0Id
. But the now logged-in user doesn't get the expected variant of the experiment that is assigned to their userId
, the properties in the object returned in experiment?.variant(variantKey)
are just null
. I have tried calling experiment?.clear()
after login but that doesn't fix it.
However, if you restart the app, they get the right values.
What is the right way to handle this?
I am using v1.11.0
We are currently seeing an intermittent crash when the SDK is fetching flags in 1.11.1
1.11.1
Samsung
, Motorola
, Xiaomi
, Asus
, etc.]11
, 12
, 13
]Fatal Exception: kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 0: Expected start of the array '[', but had 'c' instead at path: $
JSON input: connection failure
at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24)
at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:32)
at kotlinx.serialization.json.internal.AbstractJsonLexer.fail(AbstractJsonLexer.kt:598)
at kotlinx.serialization.json.internal.AbstractJsonLexer.fail$default(AbstractJsonLexer.kt:596)
at kotlinx.serialization.json.internal.AbstractJsonLexer.fail$kotlinx_serialization_json(AbstractJsonLexer.kt:233)
at kotlinx.serialization.json.internal.AbstractJsonLexer.fail$kotlinx_serialization_json$default(AbstractJsonLexer.kt:228)
at kotlinx.serialization.json.internal.AbstractJsonLexer.unexpectedToken(AbstractJsonLexer.kt:225)
at kotlinx.serialization.json.internal.StringJsonLexer.consumeNextToken(StringJsonLexer.kt:74)
at kotlinx.serialization.json.internal.StreamingJsonDecoder.beginStructure(StreamingJsonDecoder.kt:100)
at kotlinx.serialization.internal.AbstractCollectionSerializer.merge(CollectionSerializers.kt:29)
at kotlinx.serialization.internal.AbstractCollectionSerializer.deserialize(CollectionSerializers.kt:43)
at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:70)
at kotlinx.serialization.json.Json.decodeFromString(Json.kt:107)
at com.amplitude.experiment.SdkFlagApi$getFlags$2.onResponse(FlagApi.kt:80)
In a few of the crashes we see ...Expected start of the array '[', but had 'i'
('i'
instead of 'c'
)
When you pass an instanceName
other than "\$default_instance"
to the Configuration, it throws:
java.util.concurrent.ExecutionException: java.lang.IllegalStateException: java.util.concurrent.TimeoutException: Timed out waiting for Amplitude Analytics SDK to initialize. You should ensure that the analytics SDK is initialized prior to calling fetch()
This works:
val config = ExperimentConfig.builder()
.build()
val experiment = Experiment.initializeWithAmplitudeAnalytics(app, $API_KEY config,)
val user = ExperimentUser.builder()
.userId(userAuth0Id)
.deviceId(deviceIdFromAnalytics)
.build()
experiment?.start(user)?.get()
This also works:
val config = ExperimentConfig.builder()
.instanceName("\$default_instance")
.build()
val experiment = Experiment.initializeWithAmplitudeAnalytics(app, $API_KEY config,)
val user = ExperimentUser.builder()
.userId(userAuth0Id)
.deviceId(deviceIdFromAnalytics)
.build()
experiment?.start(user)?.get()
But this throws the exception mentioned earlier:
val config = ExperimentConfig.builder()
.instanceName("lorem_ipsum")
.build()
val experiment = Experiment.initializeWithAmplitudeAnalytics(app, $API_KEY config,)
val user = ExperimentUser.builder()
.userId(userAuth0Id)
.deviceId(deviceIdFromAnalytics)
.build()
experiment?.start(user)?.get()
Why do I need multiple instances? This is e trying to solve #40, one for logged in session and the other before they log in.
I am using v1.11.0
Hello, after we updated the sdk to the latest version 1.11.1, we started to receive null for some config variants, looks like the expected value is assigned to the key
field instead.
For example:
Variant(value=null, payload=null, expKey=null, key=off, metadata={default=true})
Note: The issue is not reproducible on 1.10.1, we'll rollback for now.
Summary
After updating to the latest Android Amplitude and Amplitude Experiment SDK to fix a bug with Local Bucketing, we noticed during rollout of a new app version a fresh crash in the AmplitudeClient
.
Platform: Android
SDKs:
Stack Trace
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'org.json.JSONObject com.amplitude.api.IdentifyInterceptor.intercept(java.lang.String, org.json.JSONObject)' on a null object reference
at com.amplitude.api.AmplitudeClient.saveEvent(AmplitudeClient.java:1359)
at com.amplitude.api.AmplitudeClient.logEvent(AmplitudeClient.java:1322)
at com.amplitude.api.AmplitudeClient$4.run(AmplitudeClient.java:1180)
at com.amplitude.api.AmplitudeClient.runOnLogThread(AmplitudeClient.java:2463)
at com.amplitude.api.AmplitudeClient.logEventAsync(AmplitudeClient.java:1174)
at com.amplitude.api.AmplitudeClient.logEventAsync(AmplitudeClient.java:1137)
at com.amplitude.api.AmplitudeClient.lambda$null$1(AmplitudeClient.java:400)
at com.amplitude.analytics.connector.EventBridgeImpl.setEventReceiver(EventBridge.kt:41)
at com.amplitude.api.AmplitudeClient.lambda$initializeInternal$2(AmplitudeClient.java:396)
at android.os.Handler.handleCallback(Handler.java:888)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:213)
at android.os.HandlerThread.run(HandlerThread.java:67)
This is more of FYI than an open issue, but I spent too long on this problem.
If the client doesn't use kotlinx.serialization
then in release these types of problems will come up
Error decoding JSON: Serializer for class 'EvaluationFlag' is not found.
Mark the class as @Serializable or provide the serializer explicitly.
We don't use kotlinx.serialization
but seeing as the library does, we needed to add proguard rules to stop the classes from being obfuscated during run-time for our release builds. You can find the rules here.
Might be worth adding this note to your documentation + the rules for such cases.
I get the following compile errors when I update for 1.10.0
to 1.10.1
also the changelog
wasn't updated.
org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration$ArtifactResolveException: Could not resolve all files for configuration ':data:debugCompileClasspath'.
org.gradle.internal.resolve.ModuleVersionNotFoundException: Could not find com.amplitude:experiment-android-client:1.10.1.
this is a issue to test the jira integration
Hi, we're trying to migrate the SDK version, but we faced an unexpected variant payload structure changes, could you please point me out if it's expected? And where I can find the changelog related to this change, it seems fundamental and breaks almost all of our payload parsing. Thanks
Link to the config:
https://app.amplitude.com/experiment/bandlab/293492/config/36135/configure
Before (v1.10.1)
Variant(value=true, payload={"url":"https:\/\/bnd.la\/deals"}, expKey=null)
After (v1.11.2)
Variant(value=true, payload={url=https://bnd.la/deals}, expKey=null, key=true, metadata=null)
I am looking at the network failures in my analytics for my app, and I see a lot of request being done for the Experiments API, I would say unnecessary calls.
My first item is that the API keeps counting how many API requests missed when the app is in background, and when I open the app again, all those missed API calls (which is to fetch the Feature Flags and variants) are fired at the same time, meaning you can have the app in background for 4 hours, and if you have the pollOnStart
enabled (which is by default), it would do (4 times 60 minutes/ hour) 240 requests all at once.
The second is that the polling is done 1 time per minute, which seems a bit too much to fetch something that will not change much in that period of time, and I don't see a configuration setting that could change the polling intervals.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.