Giter Site home page Giter Site logo

experiment-android-client's Introduction

experiment-android-client

Android Client SDK for Experiment

Getting Started

See the Documentation for getting started.

experiment-android-client's People

Contributors

bgiori avatar amplitude-sdk-bot avatar tyiuhc avatar qingzhuozhen avatar yuhao900914 avatar curtisliu avatar zhukaihan avatar

Stargazers

Jeffrey Wang avatar Justin Fiedler avatar  avatar

Watchers

James Cloos avatar Shreyas avatar Jeffrey Wang avatar Nirmal Utwani avatar Julien Dubeau avatar AJ Horst avatar  avatar Izaaz Yunus avatar Daniel Jih avatar Dean Shi avatar Justin Fiedler avatar Marvin Liu avatar  avatar Kelvin Lu avatar Kevin Pagtakhan avatar  avatar

Forkers

bgiori

experiment-android-client's Issues

ANRs in Amplitude Experiments

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?

What is the right way to handle user session?

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

JsonDecodingException

We are currently seeing an intermittent crash when the SDK is fetching flags in 1.11.1

Information

  • SDK 1.11.1
  • Manufacturers [Samsung, Motorola, Xiaomi, Asus, etc.]
  • OS Versions [11, 12, 13]
Stack Trace
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')

Passing an instanceName other than "\$default_instance" fails with TimeoutException

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

Variant value is missing in some configs on version 1.11.1

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.

Crash: AmplitudeClient.saveEvent

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:

  • com.amplitude:android-sdk: 2.39.8
  • com.amplitude:experiment-android-client: 1.12.0

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)

Proguard Rules + Kotlin Serialization

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.

Errors updating to 1.10.1

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.

test issue

this is a issue to test the jira integration

Unexpected Variant payload structure change on version 1.11.2

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)

Network failures when offline mode + too many requests

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.