splitio / android-client Goto Github PK
View Code? Open in Web Editor NEWAndroid SDK client for Split Software
Home Page: https://split.io
License: Other
Android SDK client for Split Software
Home Page: https://split.io
License: Other
Hi Split,
I see Android sdk guide recommend us to call client.destroy()
before shut down app, is there a specific reason we need to do this? Is there any potential issue if we don't call it?
There are actually some cases we are having user issues if we call this client.destroy()
because it also clears cache.
Like for a logged in user, if we call this and clears cache, the next time user launches the app, and split for some reason failed, we don't have any cached config for this user, then we need to fall back to a very old state for this user, but not the latest cached state.
If we do need to call this client.destroy()
, would it make sense to break it into two APIs?
Something like client.close()
and client.clearCache()
? so we could decide if we want to keep the cache or not?
Looking forward to hear back from you,
Thanks & Regards,
Jiusong
The SDK in its current form doesn't appear to be handling network connectivity issues properly. The issue we're facing is the following:
SDK_READY
, and returns Control
flag, even though it actually has data loaded from cacheSDK_READY
flagWe expect that after a period of time (timeout), Split SDK would return the treatments from cache instead of still waiting for the network calls to finish.
As a possible fix, we tried to set connection timeouts for Split:
.connectionTimeout(2000)
.readTimeout(2000)
But this doesn't really have any impact, as they're not even used by Split SDK internally...
Also, when setting a timeout for the ready event:
.ready(2000)
Then the SDK_READY_TIMED_OUT
is triggered after the timeout period, but the HTTP calls that Split SDK makes are not cancelled and are still pending and after they finish, SDK_READY
still gets triggered.
So we don't really have any control over Split SDK's networking and it's a real blocker in our current implementation... We would require that connectionTimeout
and readTimeout
actually work internally inside Split SDK, so that Split would return the cached treatments after those specific timeouts.
Hey,
Today I realized that I'm seeing a lot of errors related to the latest split sdk (2.6.0) on Android that I can barely use my logcat for anything else.
E/SplitSDK: Invalide SSE event received: STREAMING_CONNECTED
E/SplitSDK: An error has ocurred while parsing stream from https://split-realtime.ably.io/sse : Socket closed
I/SplitSDK: Periodic fetcher tasks scheduled
I/SplitSDK: Polling enabled.
I/SplitSDK: Streaming connection opened
I/SplitSDK: Sending polling disabled message through event broadcaster.
I/SplitSDK: Stoping periodic fetching tasks 01d327b2-9c99-4fec-8c74-2b9406689649, 4bee70a9-5afc-4fae-87be-5c4438e20b2c
I/SplitSDK: Polling disabled.
I/SplitSDK: Disabling polling
E/SplitSDK: Invalide SSE event received: STREAMING_CONNECTED
Any reason why they are showing up and how can we turn them off?
We've been seeing intermittent null pointer exception reports (via Crashlytics) originating from SqLitePersistentMySegmentsStorage
from some of our users. This seems to have started ever since we upgraded the Split SDK dependency version to 2.6.6
. Is this a known issue?
Fatal Exception: java.lang.NullPointerException
at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:890)
at io.split.android.client.storage.mysegments.SqLitePersistentMySegmentsStorage.<init>(SqLitePersistentMySegmentsStorage.java:36)
at io.split.android.client.storage.db.StorageFactory.getMySegmentsStorage(StorageFactory.java:31)
at io.split.android.client.SplitFactoryHelper.buildStorageContainer(SplitFactoryHelper.java:68)
at io.split.android.client.SplitFactoryImpl.<init>(SplitFactoryImpl.java:129)
at io.split.android.client.SplitFactoryImpl.<init>(SplitFactoryImpl.java:80)
at com.envoy.app.utils.SplitProvider.init(SplitProvider.java:67)
at io.split.android.client.SplitFactoryBuilder.build(SplitFactoryBuilder.java:67)
at com.envoy.app.utils.SplitProvider.initScope(SplitProvider.java:77)
at com.envoy.app.utils.SplitProvider.onClientUpdate(SplitProvider.java:95)
at com.envoy.app.ui.screens.office.OfficeViewModel.loadOfficeActions(OfficeViewModel.java:210)
at com.envoy.app.ui.screens.office.OfficeViewModel$1.invoke(OfficeViewModel.java:155)
at com.envoy.app.ui.screens.office.OfficeViewModel$1.invoke(OfficeViewModel.java:83)
at com.envoy.app.account.UserManager$observeUserUpdates$1.invokeSuspend(UserManager.java:204)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(BaseContinuationImpl.java:33)
at kotlinx.coroutines.DispatchedContinuation.resumeUndispatchedWith(DispatchedContinuation.java:226)
at kotlinx.coroutines.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuationKt.java:333)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(CancellableKt.java:26)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.java:109)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.java:158)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(BuildersKt__Builders_commonKt.java:56)
at kotlinx.coroutines.BuildersKt.launch(BuildersKt.java:1)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(BuildersKt__Builders_commonKt.java:49)
at kotlinx.coroutines.BuildersKt.launch$default(BuildersKt.java:1)
at com.envoy.app.account.UserManager.observeUserUpdates(UserManager.java:203)
at com.envoy.app.ui.screens.office.OfficeViewModel.<init>(OfficeViewModel.java:154)
at java.lang.reflect.Constructor.newInstance0(Constructor.java)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:267)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at com.android.tools.r8.GeneratedOutlineSupport.outline7(GeneratedOutlineSupport.java:8)
at com.envoy.app.di.modules.ViewModelModule.providesOfficeViewModel(ViewModelModule.java:7)
at com.envoy.app.di.modules.ViewModelModule_ProvidesOfficeViewModelFactory.providesOfficeViewModel(ViewModelModule_ProvidesOfficeViewModelFactory.java:37)
at com.envoy.app.di.components.DaggerActivityComponent.getOfficeViewModel(DaggerActivityComponent.java:221)
at com.envoy.app.di.components.DaggerActivityComponent.injectOfficeActivity(DaggerActivityComponent.java:502)
at com.envoy.app.di.components.DaggerActivityComponent.inject(DaggerActivityComponent.java:358)
at com.envoy.app.ui.screens.office.OfficeActivity.onCreate(OfficeActivity.java:98)
at android.app.Activity.performCreate(Activity.java:8000)
at android.app.Activity.performCreate(Activity.java:7984)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3404)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3595)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7660)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Split 2.6.1 crashes on Android 4, I looked at split changelog + dependencies and this is simply because the OkHttp dependency that split uses is 4+
To keep support to android 4 the dependency should be 3.12+
https://cashapp.github.io/2019-02-05/okhttp-3-13-requires-android-5
When the latest version of the Android split.io SDK is used (2.10.2) and the app is started, the app crashes with the following exception:
java.lang.IllegalArgumentException: myapp: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent. Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles. at android.app.PendingIntent.checkFlags(PendingIntent.java:375) at android.app.PendingIntent.getBroadcastAsUser(PendingIntent.java:645) at android.app.PendingIntent.getBroadcast(PendingIntent.java:632) at androidx.work.impl.utils.ForceStopRunnable.getPendingIntent(ForceStopRunnable.java:285) at androidx.work.impl.utils.ForceStopRunnable.isForceStopped(ForceStopRunnable.java:158) at androidx.work.impl.utils.ForceStopRunnable.forceStopRunnable(ForceStopRunnable.java:185) at androidx.work.impl.utils.ForceStopRunnable.run(ForceStopRunnable.java:103) at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:920)
The reason is that split.io is using androidx.work:work-runtime-ktx:2.6.0 which doesn't use the currect flags that are required on SDK level 31 and laters.
As a workaround, androidx.work:work-runtime-ktx:2.7.0 or later can be added as a dependency in the app that uses the split.io SDK. However, the dependency work-runtime-ktx should be updated to 2.7.0 or later in the split.io SDK for a proper fix.
Stack trace:
Fatal Exception: java.lang.IllegalArgumentException: uk.co.tickr.android: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality depends on the PendingIntent being mutable, e.g. if it needs to be used with inline replies or bubbles.
at android.app.PendingIntent.checkFlags(PendingIntent.java:375)
at android.app.PendingIntent.getBroadcastAsUser(PendingIntent.java:645)
at android.app.PendingIntent.getBroadcast(PendingIntent.java:632)
at androidx.work.impl.utils.ForceStopRunnable.getPendingIntent(ForceStopRunnable.java:196)
at androidx.work.impl.utils.ForceStopRunnable.isForceStopped(ForceStopRunnable.java:128)
at androidx.work.impl.utils.ForceStopRunnable.run(ForceStopRunnable.java:93)
at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920)
We call SplitFactory.client()
on app startup, and our app crashes with the above stack trace on Android S devices.
Looks like the WorkManager dependency in Split is outdated.
This method seems to rely on CountDownLatches in SDKReadinessGates which are never decremented
Hi,
I am getting an issue similar to #397 with a listener. It only happens the first time, when the app is installed on the device. I am using the 2.11.0
version, but the problem is still there. I enabled debug 👇
Periodic recording tasks scheduled
2022-07-07 19:39:33.760 22547-22666/org.dev I/SplitSDK: Android SDK initialized!
2022-07-07 19:39:33.963 22547-22735/org.dev D/SplitSDK: Received from: https://sdk.split.io/api/splitChanges?since=-1 -> {"splits":[{"trafficTypeName":"user","name":"aa_ios_test","trafficAllocation":100,"trafficAllocationSeed":-908316409,"seed":899280478,"status":"ACTIVE","killed":false,"defaultTreatment":"excluded","changeNumber":1657036006539,"algo":2,"configurations":{},"conditions":[{"conditionType":"WHITELIST","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":null,"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":{"whitelist":["56d7bba0-9614-408f-aeed-2ae589515505","802b8154-6a27-4edb-84c3-1bfa4da5df23"]},"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"variant_1","size":100}],"label":"whitelisted"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"platform"},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":{"whitelist":["iOS","ios","IOS"]},"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"ctrl","size":34},{"treatment":"variant_1","size":33},{"treatment":"variant_2","size":33},{"treatment":"excluded","size":0}],"label":"platform in list [iOS, ios, ...]"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":null},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"ctrl","size":0},{"treatment":"variant_1","size":0},{"treatment":"variant_2","size":0},{"treatment":"excluded","size":100}],"label":"default rule"}]},{"trafficTypeName":"user","name":"aa_android_test","trafficAllocation":100,"trafficAllocationSeed":384098175,"seed":-779992597,"status":"ACTIVE","killed":false,"defaultTreatment":"excluded","changeNumber":1657035101661,"algo":2,"configurations":{},"conditions":[{"conditionType":"WHITELIST","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":null,"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":{"whitelist":["50016b7e-24b7-4983-bdbd-98724a3d5f1c","6c02333f-ac6e-444f-a1da-9014eb4c98ca"]},"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"variant_1","size":100}],"label":"whitelisted"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"platform"},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":{"whitelist":["Android","android"]},"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"ctrl","size":34},{"treatment":"variant_1","size":33},{"treatment":"variant_2","size":33},{"treatment":"excluded","size":0}],"label":"platform in list [Android, android]"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":null},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"ctrl","size":0},{"treatment":"variant_1","size":0},{"treatment":"variant_2","size":0},{"treatment":"excluded","size":100}],"label":"default rule"}]},{"trafficTypeName":"user","name":"aa_android_test_v3","trafficAllocation":100,"trafficAllocationSeed":567840604
2022-07-07 19:39:33.963 22547-22735/org.dev D/SplitSDK: ,"seed":-1889463240,"status":"ACTIVE","killed":false,"defaultTreatment":"excluded","changeNumber":1657020092957,"algo":2,"configurations":{},"conditions":[{"conditionType":"WHITELIST","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":null,"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":{"whitelist":["6c02333f-ac6e-444f-a1da-9014eb4c98ca"]},"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"variant_2","size":100}],"label":"whitelisted"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"platform"},"matcherType":"WHITELIST","negate":true,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":{"whitelist":["blblab"]},"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"ctrl","size":34},{"treatment":"variant_1","size":33},{"treatment":"variant_2","size":33},{"treatment":"excluded","size":0}],"label":"platform not in list [blblab]"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":null},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"ctrl","size":0},{"treatment":"variant_1","size":0},{"treatment":"variant_2","size":0},{"treatment":"excluded","size":100}],"label":"default rule"}]},{"trafficTypeName":"user","name":"aa_locale_quick_rampup_v2_test_copy","trafficAllocation":100,"trafficAllocationSeed":1795508390,"seed":-153495606,"status":"ACTIVE","killed":false,"defaultTreatment":"excluded","changeNumber":1656943759080,"algo":2,"configurations":{},"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"locale"},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":{"whitelist":["en",":en"]},"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"ctrl","size":34},{"treatment":"variant1","size":33},{"treatment":"variant2","size":33},{"treatment":"excluded","size":0},{"treatment":"no_english_locale","size":0},{"treatment":"no_en_locale","size":0}],"label":"locale in list [en, :en]"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":null},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"ctrl","size":0},{"treatment":"variant1","size":0},{"treatment":"variant2","size":0},{"treatment":"excluded","size":0},{"treatment":"no_english_locale","size":0},{"treatment":"no_en_locale","size":100}],"label":"default rule"}]},{"trafficTypeName":"user","name":"test-split","trafficAllocation":100,"trafficAllocationSeed":720217622,"seed":1168524908,"status":"ACTIVE","killed":false,"defaultTreatment":"excluded","changeNumber":1655888162342,"algo":2,"configurations":{},"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"locale"},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":{"whitelist":["en"]},"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":
2022-07-07 19:39:33.963 22547-22735/org.dev D/SplitSDK: 100},{"treatment":"off","size":0},{"treatment":"excluded","size":0}],"label":"locale in list [en]"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"locale"},"matcherType":"WHITELIST","negate":true,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":{"whitelist":["en"]},"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100},{"treatment":"excluded","size":0}],"label":"locale not in list [en]"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":null},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"excluded","size":100}],"label":"default rule"}]},{"trafficTypeName":"user","name":"aa_country_locale_quick_rampup","trafficAllocation":100,"trafficAllocationSeed":1742300774,"seed":2096363507,"status":"ACTIVE","killed":false,"defaultTreatment":"excluded","changeNumber":1655479780951,"algo":2,"configurations":{},"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"country"},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":{"whitelist":["US","UK"]},"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null},{"keySelector":{"trafficType":"user","attribute":"locale"},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":{"whitelist":["en"]},"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":50},{"treatment":"off","size":50},{"treatment":"excluded","size":0}],"label":"country in list [US, UK] and locale in list [en]"},{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":null},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":0},{"treatment":"excluded","size":100}],"label":"default rule"}]},{"trafficTypeName":"user","name":"show_mentored_course","trafficAllocation":100,"trafficAllocationSeed":437423529,"seed":-1483976337,"status":"ACTIVE","killed":false,"defaultTreatment":"off","changeNumber":1654249554906,"algo":2,"configurations":{},"conditions":[{"conditionType":"ROLLOUT","matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":null},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":null,"whitelistMatcherData":null,"unaryNumericMatcherData":null,"betweenMatcherData":null,"booleanMatcherData":null,"dependencyMatcherData":null,"stringMatcherData":null}]},"partitions":[{"treatment":"on","size":50},{"treatment":"off","size":50}],"label":"default rule"}]}],"since":-1,"till":1657036006539}
2022-07-07 19:39:33.989 22547-22735/org.dev D/SplitSDK: Features have been updated
2022-07-07 19:39:34.027 22547-22717/org.dev D/SplitSDK: Received from: https://sdk.split.io/api/mySegments/64773744-eaff-4542-afe3-d4ce360c9ef1 -> {"mySegments":[]}
2022-07-07 19:39:34.029 22547-22717/org.dev D/SplitSDK: My Segments have been updated
2022-07-07 19:39:34.126 22547-22666/org.dev W/SplitSDK: A listener was added for SDK_READY on the SDK, which has already fired and won’t be emitted again. The callback won’t be executed.
2022-07-07 19:39:38.755 22547-22724/org.dev D/SplitSDK: Push notification manager started
2022-07-07 19:39:39.692 22547-22749/org.dev D/SplitSDK: SSE Authentication done, now parsing token
2022-07-07 19:40:40.297 22547-22749/org.dev D/SplitSDK: Streaming connection opened
2022-07-07 19:40:40.299 22547-22749/org.dev D/SplitSDK: Streaming connection success
2022-07-07 19:40:40.300 22547-22749/org.dev D/SplitSDK: Push Subsystem Up event message received.
2022-07-07 19:40:40.376 22547-22725/org.dev D/SplitSDK: Received from: https://sdk.split.io/api/mySegments/64773744-eaff-4542-afe3-d4ce360c9ef1 -> {"mySegments":[]}
2022-07-07 19:40:40.380 22547-22725/org.dev D/SplitSDK: My Segments have been updated
2022-07-07 19:40:40.394 22547-22735/org.dev D/SplitSDK: Received from: https://sdk.split.io/api/splitChanges?since=1657036006539 -> {"splits":[],"since":1657036006539,"till":1657036006539}
2022-07-07 19:40:40.397 22547-22735/org.dev D/SplitSDK: Features have been updated
Thx
SplitIO doesn't work when an android phone connected to wifi with NTLM Proxy.
We had the same issue before stated working with SplitIO SDK but we used the retrofit SDK.
OkHTTP client builder:
public OkHttpClient createClient(boolean loggingEnabled, boolean tls12Enforced, int connectTimeout, int readTimeout, int writeTimeout) {
OkHttpClient.Builder defaultHttpClient = new OkHttpClient.Builder();
Authenticator proxyAuthenticator = new Authenticator() {
@Override
public Request authenticate(Route route, okhttp3.Response response) throws IOException {
if (response.code() == 407) {
ProxyCredentials credentials = getProxyCredentials();
if (credentials == null) {
throw new IOException("Failed to authenticate with proxy");
}
List<HTTPAuthMethod> httpAuthMethods = HTTPAuthMethod.ReadMethodsFromResponse(response);
ProxyAuthenticator resolvedAuthenticator = null;
HTTPAuthMethod resolvedMethod = null;
for (ProxyAuthenticator authenticator : proxyAuthenticators) {
for (HTTPAuthMethod authMethod : httpAuthMethods) {
if (authenticator.supportsAuthMethod(authMethod)) {
resolvedAuthenticator = authenticator;
resolvedMethod = authMethod;
break;
}
}
if (resolvedAuthenticator != null) {
break;
}
}
if (resolvedAuthenticator == null) {
return null;
}
try {
return resolvedAuthenticator.authenticate(credentials, resolvedMethod, route, response);
} catch (Throwable e) {
if (e instanceof ProxyErrors.CredentialsAreInvalidException) {
updateProxyCredentials(null);
}
throw new IOException("Failed to authenticate with proxy");
}
}
return null;
}
};
defaultHttpClient = defaultHttpClient
.proxyAuthenticator(proxyAuthenticator);
return modifyClient(defaultHttpClient.build(), loggingEnabled, tls12Enforced, connectTimeout, readTimeout, writeTimeout);
}
@Override
public void onFailure(Call<T> call, Throwable t) {
t.printStackTrace();
if (result == null) {
return;
} else if ("Canceled".equalsIgnoreCase(t.getMessage())) {
return;
}
if (t.getMessage() != null && t.getMessage().equals("Failed to authenticate with proxy")) {
handleProxyError(call, result, context, t);
return;
}
notifyErrorResult(call, result, context, t);
}
public static <T> void handleProxyError(final Call<T> call, final CallbackResponse<T> result, final Context context, final Throwable error) {
if (instance.proxyErrorHandler == null) {
notifyErrorResult(call, result, context, error);
return;
}
ErrorRequestFulfiller fulfiller = new ErrorRequestFulfiller() {
@Override
void notifyError() {
notifyErrorResult(call, result, context, error);
}
@Override
void redo() {
enqueue(call.clone(), result, context);
}
};
inflightRequests.add(fulfiller);
if (handlingProxyError) {
return;
}
handlingProxyError = true;
instance.proxyErrorHandler.handle(new ProxyErrorHandler.Callback() {
@Override
public void onResult(ProxyErrorHandler.Result resolveResult) {
handlingProxyError = false;
if (resolveResult.getError() != null) {
for (ErrorRequestFulfiller fulfiller : inflightRequests) {
fulfiller.notifyError();
}
} else {
ProxyData proxyData = resolveResult.getProxyData();
ProxyCredentials credentials = new ProxyCredentials(proxyData.getProxyUser(), proxyData.getProxyPass());
instance.updateProxyCredentials(credentials);
for (ErrorRequestFulfiller fulfiller : inflightRequests) {
fulfiller.redo();
}
}
}
});
}
In case of a proxy authentication error, we show a dialog and ask users to enter a login/password to save it for the current and next session.
Environment
Android:
implementation 'io.split.client:android-client:2.4.5'
NTLM setup on a local machine for testing
Requirements:
Windows
I use parallels, install windows there, establish a connection between host and virtual machine (ping to check)
Steps:
Install WinGate
a) https://www.wingate.com/download/wingate/download.php?gclid=EAIaIQobChMI0bbYzoT14wIVlOiaCh1XzQ_EEAAYASABEgIioPD_BwE
Start Wingate
a) Select the first square button
b) In wizard select win proxy as a provider (should be automatically though)
c) Select + on the same screen where we selected the first button and click on in popup
d). Select the created a button and choose to Use another account
e) Name: Administrator, Pass:
Enable NTLM and Select Port
a) Control panel -> services -> www proxy
b) In General select port (let's say 9020), enable NTLM, disable basic (we want to check NTLM right?)
Force NTLM authentication
a) Right-click, new policy.
b) Source Type: Any HTTPProxy, Event type: Request.
c) The easiest way to configure policy is to drag and drop some policy elements from other policies.
d) To do this Open web authenticate policy in another window (you can see this policy in the list)
e) Drag and drop to a new policy from authenticating policy: "Auth", "Allow", "Is authenticated?" elements.
f) Drag and drop to new policy from a list of elements "WWW Proxy Server: Request" element.
g) Connect "WWW Proxy Server: Request" to "Is authenticated" element
h) Connect the bottom of "Is authenticated" (Yes) to "Allow" element
i) Connect right of "Is authenticated" (No) to "Auth" element
j) Save (the policy will start to work automatically)
Create user.
a) Control Panel -> Users and Groups
b) Right-click -> new Users
c) Fill in name pass etc.
Check (For mac).
Add proxy.
a) Go to settings -> Network -> Choose network used for internet -> Advanced -> Proxy
b) Fill in IP and port of proxy for Web Proxy and Secure Web Proxy (Do not fill in pass etc)
Check proxy is used
c) Go to, say, google.com.
d) Name and Pass requests should be requested.
e) Use ones from 5.iii
Check (For Android Device and Wingate is on a virtual machine).
Add Proxy in android Settings
Add port forwarding to port selected on step 3(b)
Warnings.
Wingate caches auth status. To force re-auth change user password 5(c) and restart WinGate (Kill Wingate Manager from Tasks Manager (Ctrl + Shift + Esc))
Hello
After migrating from version 2.5.0-SNAPSHOT.1 to 2.6.0, a crash is happening for all devices.
Stacktrace:
Fatal Exception: java.lang.IllegalStateException
Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
androidx.room.RoomOpenHelper.checkIdentity (RoomOpenHelper.java:154)
androidx.room.RoomOpenHelper.onOpen (RoomOpenHelper.java:135)
androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onOpen (FrameworkSQLiteOpenHelper.java:195)
android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked (SQLiteOpenHelper.java:428)
android.database.sqlite.SQLiteOpenHelper.getWritableDatabase (SQLiteOpenHelper.java:317)
androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase (FrameworkSQLiteOpenHelper.java:145)
androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase (FrameworkSQLiteOpenHelper.java:106)
androidx.room.RoomDatabase.inTransaction (RoomDatabase.java:476)
androidx.room.RoomDatabase.assertNotSuspendingTransaction (RoomDatabase.java:281)
io.split.android.client.storage.db.GeneralInfoDao_Impl.getByName (GeneralInfoDao_Impl.java:68)
io.split.android.client.storage.db.migrator.StorageMigrator$MigrationChecker.run (StorageMigrator.java:92)
Hi Team,
I'm experiencing a problem with Split Configuration when using Split in Widgets and Keyboard, where our Widgets / Keyboard is not getting an updated value (After changes in Dashboard, of course) if i don't open the main app to foreground.
I've been trying to trace the problem until SplitsStorageImpl.java
class where after some delay after Dashboard changes, i'm getting an update and mInMemorySplits
variable is updated with proper value. But as long as i let the main apps stay in background, whenever i'm getting value from SplitsStorageImpl.java
from Widgets it'll still get the old non-updated value.
Currently i'm assuming that our Widgets / Keyboard is not holding the same instances of mInMemorySplits
that being updated in update()
method, and because split uses local variable instead of database update (i see that database update only called in loadLocal()
), our Widgets / Keyboard somehow miss the references. I'll keep trying to trace the problem on my side and get back if i got a new info.
My main question is whether there's any need for specific configuration for these cases? I'm currently toggling on all background-related config i found in https://help.split.io/hc/en-us/articles/360020343291-Android-SDK#configuration although to no result.
Thanks before.
Hey!
I have seen crashes with some of our customers related to these two files:
MySegmentsUpdateWorker.java line 33
io.split.android.client.service.sseclient.reactor.MySegmentsUpdateWorker.onWaitForNotificationLoop
SplitUpdatesWorker.java line 32
io.split.android.client.service.sseclient.reactor.SplitUpdatesWorker.onWaitForNotificationLoop
Unfortunately no stacktrace was generated from crashlytics, all I got was this:
Fatal Exception: java.lang.OutOfMemoryError
OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available
splitSDKVersion = '2.6.6'
android version = 6.0.1
device = Galaxy S5
Hi guys,
I'm seeing a new issue on startup causing our app to take 140 seconds to start and using 100% cpu during that time.
I've tracked the hot spot down to BCrypt.java:641-644 where it does 1024 rounds initializing the key.
crypt_raw:639, BCrypt (io.split.android.client.utils)
hashpw:710, BCrypt (io.split.android.client.utils)
convertApiKeyToFolder:88, Utils (io.split.android.client.utils)
buildDatabaseName:54, SplitFactoryHelper (io.split.android.client)
<init>:135, SplitFactoryImpl (io.split.android.client)
<init>:84, SplitFactoryImpl (io.split.android.client)
build:67, SplitFactoryBuilder (io.split.android.client)
<GoDaddy Code>
As an aside it is a universally bad idea to roll your own cryptography instead of using what is built into the platform, as you take on the responsibility of any security vulnerabilities and updates.
Hi, I am planning to use a same instance of SplitClient as a singleton instance, is there a way to set the user ID without creating a new instance? I am also having difficulty to set the user ID correctly, as the targeting rule is not working properly.
How I am setting the user ID now:
val config = SplitClientConfig.builder()
.impressionsRefreshRate(60)
.connectionTimeout(10000)
.readTimeout(10000)
.build()
val userId = "123"
val splitFactory = SplitFactoryBuilder.build(
configuration.splitKey,
Key(userId),
config,
application)
val instance = splitFactory.client()
instance.on(SplitEvent.SDK_READY, object: SplitEventTask() {
override fun onPostExecution(client: SplitClient?) {
Log.d("test", "SDK_READY")
}
})
I was using a Charles proxy when running the app and it crashed due to a NPE in SplitFactoryBuilder
, probably because it couldn't connect to the server / ERR_CONNECTION_REFUSED.
This should be avoidable by returning empty lists instead of nulls.
The call:
// Build SDK configuration by default
val config: SplitClientConfig = SplitClientConfig.builder().build()
// Create a new user key to be evaluated
val key = Key(lastId, null)
// Create factory
val splitFactory: SplitFactory = SplitFactoryBuilder.build(SPLIT_API_KEY, key, config, context)
Stacktrace:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List io.split.android.client.impressions.StoredImpressions.impressions()' on a null object reference
at io.split.android.client.impressions.ImpressionsFileStorage.read(ImpressionsFileStorage.java:75)
at io.split.android.client.impressions.ImpressionsStorageManager.loadEventsFilesByLine(ImpressionsStorageManager.java:187)
at io.split.android.client.impressions.ImpressionsStorageManager.loadImpressionsFromDisk(ImpressionsStorageManager.java:182)
2019-10-14 11:26:51.938 6534-6640/dk.coop.coopplus.demo E/AndroidRuntime: at io.split.android.client.impressions.ImpressionsStorageManager.<init>(ImpressionsStorageManager.java:63)
at io.split.android.client.impressions.ImpressionsStorageManager.<init>(ImpressionsStorageManager.java:55)
at io.split.android.client.SplitFactoryImpl.<init>(SplitFactoryImpl.java:151)
at io.split.android.client.SplitFactoryBuilder.build(SplitFactoryBuilder.java:67)
I'm seeing these errors in my logs:
java.lang.IllegalStateException: Method removeObserver must be called on the main thread
at androidx.lifecycle.LifecycleRegistry.enforceMainThreadIfNeeded(LifecycleRegistry.java:317)
at androidx.lifecycle.LifecycleRegistry.removeObserver(LifecycleRegistry.java:219)
at io.split.android.client.lifecycle.LifecycleManager.destroy(LifecycleManager.java:37)
at io.split.android.client.SplitFactoryImpl$1.run(SplitFactoryImpl.java:163)
at java.lang.Thread.run(Thread.java:919)
Looking at the code, you are indeed calling _lifecycleManager.destroy()
from a non-Main thread. See
Hi team,
We have an issue in our app where splitClient.getTreatment()
is returning control
after the app is brought back to the foreground after it had been killed in the background.
Our set-up involves initializing the SplitClient
in our app's splash screen. We also integrated a custom LifecycleObserver
which initializes the SplitClient
in Lifecycle.Event.ON_START
, to handle cases where user returns to the app.
Our custom LifecycleObserver
also calls splitClient.shutdown()
in Lifecycle.Event.ON_STOP
, which is a slight adjustment to the recommendation provided in the official Split docs.
Executing the client.destroy() method into onStop callback of your MainActivity is a good practice.
class LifecycleListener(
private val context: Context,
private val accountRepo: AccountRepo
) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
SplitUtils.initialize(context, uniqueId = accountRepo.getUniqueId()).subscribe {
// ignore result
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() {
SplitUtils.closeClient() // calls splitClient.destroy()
}
}
object class SplitUtils {
fun initialize(context: Context, uniqueId: String): Single<SplitClient> {
return Single.create { subscriber ->
val config = SplitClientConfig.builder()
.connectionTimeout(TIMEOUT_INITIALIZE) // sdk default is 15000 ms
.build()
val key = Key(uniqueId, null)
val splitFactory = SplitFactoryBuilder.build(apiKey, key, config, context)
splitClient = splitFactory.client()
splitClient.on(SplitEvent.SDK_READY_TIMED_OUT, object : SplitEventTask() {
override fun onPostExecution(splitClient: SplitClient) {
subscriber.onError(Throwable("Timeout"))
}
})
splitClient.on(SplitEvent.SDK_READY, object : SplitEventTask() {
override fun onPostExecution(client: SplitClient) {
subscriber.onSuccess(client)
}
})
}
}
fun closeClient() {
splitClient.destroy()
}
Our Activities call splitClient.getTreatment()
typically in onCreate()
.
My main questions are:
splitClient.destroy()
? The docs seem to recommend a slightly misleading approach, implying we should destroy in onStop()
for all our activities (hence our adjustment above using LifecycleObserver
)splitClient.destroy()
in onStop()
, what is the Split team's recommended approach for when to re-initialize SplitClient
? From our code, it seems we're re-initializing too late, after our Activity's onCreate()
is returned (which, after this time, our experiment treatments have been evaluated as control
)splitClient.destroy()
necessary? I've experimented with removing this line, and doing so appears to resolve the issue.To illustrate a hypothetical scenario with our testing strategy used:
split_experiment
defined with "on"
and "off"
treatmentssplit_experiment
as "on"split_experiment
as "control"Metadata:
2.6.7
Please advise if more info is needed. Thanks in advance.
This crash on Android app has seen an increase after 31st December 2020. Full crash stack below.
Attached is the report from Embrace.
Embrace - Crash Details.pdf
| at android.database.sqlite.SQLiteConnection.execute (SQLiteConnection.java:569)
| at android.database.sqlite.SQLiteSession.endTransactionUnchecked (SQLiteSession.java:439)
| at android.database.sqlite.SQLiteSession.endTransaction (SQLiteSession.java:403)
| at android.database.sqlite.SQLiteDatabase.endTransaction (SQLiteDatabase.java:588)
| at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.endTransaction (FrameworkSQLiteDatabase.java:90)
| at androidx.room.RoomDatabase.endTransaction (RoomDatabase.java:364)
| at io.split.android.client.storage.db.ImpressionDao_Impl.insert (ImpressionDao_Impl.java:5)
| at io.split.android.client.storage.impressions.SqLitePersistentImpressionsStorage.push (SqLitePersistentImpressionsStorage.java:20)
| at io.split.android.client.storage.impressions.SqLitePersistentImpressionsStorage.push (SqLitePersistentImpressionsStorage.java:20)
| at io.split.android.client.service.synchronizer.RecorderSyncHelperImpl$1.run (RecorderSyncHelperImpl.java:66)
| at java.lang.Thread.run (Thread.java:761)
Recently I have been profiling my app and I saw that the top threads that are eating CPU cycles are related to split.
Is there a reason for that? I already initialized split on app launch and this profile came from going to another activity in my app where I just query split one for a specific feature on activity create and I feel this affects the performance of the app.
Hi, when I integrate split and enable proguard, i got this warning, i believe it is from internal dependency of the split sdk which is the snackyaml. I try to keep class but it still fails
-keep class java.beans.** { *; }
-keep class org.yaml.snakeyaml.introspector.** { *; }
Warning thrown by proguard:
Warning: org.yaml.snakeyaml.introspector.MethodProperty: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.MethodProperty: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.MethodProperty: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.MethodProperty: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.MethodProperty: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.MethodProperty: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.MethodProperty: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.MethodProperty: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.MethodProperty: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.MethodProperty: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.IntrospectionException
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.Introspector
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.Introspector
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.BeanInfo
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.BeanInfo
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.FeatureDescriptor
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.FeatureDescriptor
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.PropertyDescriptor
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.IntrospectionException
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.FeatureDescriptor
Warning: org.yaml.snakeyaml.introspector.PropertyUtils: can't find referenced class java.beans.FeatureDescriptor
This crash happens since we fully migrate our online config to Split.io SDK, is it possible because our apps are having so much config that the database on Split causing this error?
I see on code you have this query which queries all cached config :
@Query("SELECT name, body, updated_at FROM splits") List<SplitEntity> getAll();
is it possible if you add @Transation on that query https://developer.android.com/reference/androidx/room/Transaction?authuser=2 ?
"If the result of the query is fairly big, it is better to run it inside a transaction to receive a consistent result. Otherwise, if the query result does not fit into a single CursorWindow, the query result may be corrupted due to changes in the database in between cursor window swaps."
Stack-trace
Fatal Exception: android.database.CursorWindowAllocationException: Could not allocate CursorWindow '/data/user/0//databases/2a108jbd3qchur8ehofuhfgquOVOoASMity1TtqCXkbQnI7WOrG0YZ1K' of size 2097152 due to error -25.
at android.database.CursorWindow.nativeCreate(CursorWindow.java)
at android.database.CursorWindow.<init>(CursorWindow.java:139)
at android.database.CursorWindow.<init>(CursorWindow.java:120)
at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:202)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:147)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:140)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:232)
at android.database.AbstractCursor.moveToNext(AbstractCursor.java:281)
at androidx.room.InvalidationTracker$1.DoublePoint(SourceFile:417)
at androidx.room.InvalidationTracker$1.run(SourceFile:388)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
.................
Split-EventsManager-0
at sun.misc.Unsafe.park(Unsafe.java)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:190)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2067)
at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:387)
at io.split.android.client.events.SplitEventsManager.equals(SourceFile:150)
at io.split.android.client.events.SplitEventsManager.run(SourceFile:144)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
Hello team, I faced some interesting issue.
When I use SplitFilter splitFilter = SplitFilter.byName(Arrays.asList("SPLIT_NAME_1", "SPLIT_NAME_2"));
split treatments data is not updated with time. Split data is loaded once during the first start of the application after install and then never updated. If I don't use filters everything works fine. I tried changing refresh rate using SplitClientConfig
, but this didn't help. I am using latest version 2.6.6
.
Thanks
The split client does not respect the treatment at times when used with SDK_READY
One of our security tools have detected a high security vulnerability on the okhttp3 version used by io.split.client
The SDK is currently using com.squareup.okhttp3:[email protected]
and the vulnerability has been fixed on version com.squareup.okhttp3:[email protected]
and above.
https://mvnrepository.com/artifact/io.split.client/android-client/3.1.0
Can we upgrade this library?
Best Regards,
Elder Baltazar
When setting the device date 2+ days in the future, the Split SDK HTTP calls fail with the below exception:
E/SplitSDK: RefreshableSplitFetcher failed: Problem fetching splitChanges: HttpException: Something happened while retrieving data: Chain validation failed
java.lang.IllegalStateException: Problem fetching splitChanges: HttpException: Something happened while retrieving data: Chain validation failed
at io.split.android.client.HttpSplitChangeFetcher.fetch(HttpSplitChangeFetcher.java:86)
at io.split.android.client.HttpSplitChangeFetcher.fetch(HttpSplitChangeFetcher.java:52)
at io.split.android.engine.experiments.RefreshableSplitFetcher.runWithoutExceptionHandling(RefreshableSplitFetcher.java:140)
at io.split.android.engine.experiments.RefreshableSplitFetcher.run(RefreshableSplitFetcher.java:117)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:307)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:302)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
Caused by: io.split.android.client.network.HttpException: HttpException: Something happened while retrieving data: Chain validation failed
at io.split.android.client.network.HttpRequestImpl.getRequest(HttpRequestImpl.java:64)
at io.split.android.client.network.HttpRequestImpl.execute(HttpRequestImpl.java:35)
at io.split.android.client.HttpSplitChangeFetcher.fetch(HttpSplitChangeFetcher.java:72)
at io.split.android.client.HttpSplitChangeFetcher.fetch(HttpSplitChangeFetcher.java:52)
at io.split.android.engine.experiments.RefreshableSplitFetcher.runWithoutExceptionHandling(RefreshableSplitFetcher.java:140)
at io.split.android.engine.experiments.RefreshableSplitFetcher.run(RefreshableSplitFetcher.java:117)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:307)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:302)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
After implementing Split into our native android app the following crash has started to occur.
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.FileOutputStream.close()' on a null object reference at io.split.android.client.storage.FileStorage.write + 81(FileStorage.java:81) at io.split.android.client.cache.SplitCache.writeSplitsToDisk + 147(SplitCache.java:147) at java.lang.reflect.Method.invoke(Method.java) at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback + 216(ClassesInfoCache.java:216) at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent + 194(ClassesInfoCache.java:194) at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks + 185(ClassesInfoCache.java:185) at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged + 36(ReflectiveGenericLifecycleObserver.java:36) at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent + 361(LifecycleRegistry.java:361) at androidx.lifecycle.LifecycleRegistry.backwardPass + 316(LifecycleRegistry.java:316) at androidx.lifecycle.LifecycleRegistry.sync + 334(LifecycleRegistry.java:334) at androidx.lifecycle.LifecycleRegistry.moveToState + 145(LifecycleRegistry.java:145) at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent + 131(LifecycleRegistry.java:131) at androidx.lifecycle.ProcessLifecycleOwner.dispatchPauseIfNeeded + 140(ProcessLifecycleOwner.java:140) at androidx.lifecycle.ProcessLifecycleOwner$1.run + 67(ProcessLifecycleOwner.java:67) at android.os.Handler.handleCallback + 739(Handler.java:739) at android.os.Handler.dispatchMessage + 95(Handler.java:95) at android.os.Looper.loop + 158(Looper.java:158) at android.app.ActivityThread.main + 7225(ActivityThread.java:7225) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run + 1230(ZygoteInit.java:1230) at com.android.internal.os.ZygoteInit.main + 1120(ZygoteInit.java:1120)
Seems the problem is in this method.
Sometimes, an error occurs during the execution of "new FileOutputStream (file)", which causes the property to remain in the value "fileOutputStream == null". And in the future, we try to execute the code "fileOutputStream.close ();" in finally block, when property equals null. Resulting in a crash with NPE.
Hi, we want to integrate instrumentation SDK that should be put on top of OkHttp instance.
Any plan to take out the OkHttp "instantiator" into caller code, so we can set/modify the OkHttp instance by ourself? or
Any workaround that we can do for now?
Currently we are planning to look at how Firebase Performance bytecode manipulation to put OkHttp instrumentation.
Thank you
Could you update the proguard rules file?
I'm trying to run Split.io with proguard but it was crashing on startup.
After adding those lines below, the crash stopped, but Split.io doesn't retrieve any Treatment when it is obfuscated.
-keep class io.split.android.client.dtos.* { *; }
-keep class io.split.android.client.storage.db.** { *; }
Hi, we are using android-client v2.13.0 and recently we found crashes in Firebase Crashlytics
(85% happen when app in background)
the stack trace
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'void io.split.android.client.events.SplitEventTask.onPostExecution(io.split.android.client.SplitClient)' on a null object reference
at io.split.android.client.events.executors.SplitEventExecutorWithClient$1.doInBackground(SplitEventExecutorWithClient.java:39)
at io.split.android.client.events.executors.SplitEventExecutorWithClient$1.doInBackground(SplitEventExecutorWithClient.java:28)
We also check on the code
public SplitEventExecutorWithClient(SplitEventTask task, SplitClient client) {
super(task);
checkNotNull(task);
_sclient = checkNotNull(client);
}
public void execute(){
_asyncTansk = new AsyncTask<SplitClient, Void, SplitClient>() {
@Override
protected SplitClient doInBackground(SplitClient... splitClients) {
if (splitClients.length > 0) {
SplitClient client = checkNotNull(splitClients[0]);
//BACKGROUND POST EXECUTION
try {
_task.onPostExecution(client);
but couldn't find a possibility of _task
field being null
Thank you
Our Android app started crashing on app launch after updating to the latest version of Split. I confirmed that the crash only occurs with the 2.7.3
version of the SDK, and does not with the 2.7.2
version. Based off the stack trace, it appears the issue has to do with a WorkManager
related change in the latest version of the SDK.
Stack trace:
2021-10-11 13:36:37.988 E/AndroidRuntime: FATAL EXCEPTION: main Process: com.latch.android.latchapp.dev, PID: 22120 java.lang.RuntimeException: Unable to create application com.latch.android.latchapp.app.LatchApplication: java.lang.IllegalStateException: WorkManager is already initialized. Did you try to initialize it manually without disabling WorkManagerInitializer? See WorkManager#initialize(Context, Configuration) or the class level Javadoc for more information. at android.app.ActivityThread.handleMakeApplication(ActivityThread.java:7512) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7446) at android.app.ActivityThread.access$1500(ActivityThread.java:301) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2148) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:246) at android.app.ActivityThread.main(ActivityThread.java:8512) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130) Caused by: java.lang.IllegalStateException: WorkManager is already initialized. Did you try to initialize it manually without disabling WorkManagerInitializer? See WorkManager#initialize(Context, Configuration) or the class level Javadoc for more information. at androidx.work.impl.WorkManagerImpl.initialize(WorkManagerImpl.java:183) at androidx.work.WorkManager.initialize(WorkManager.java:210) at com.latch.android.latchapp.app.initialization.impl.WorkerFactoryInitTask.init(WorkerFactoryInitTask.kt:18) at com.latch.android.latchapp.app.initialization.AppInitQueue.process(AppInitQueue.kt:72) at com.latch.android.latchapp.app.LatchApplication.initialize(LatchApplication.kt:63) at com.latch.android.latchapp.app.LatchApplication.onCreate(LatchApplication.kt:50) at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1192) at android.app.ActivityThread.handleMakeApplication(ActivityThread.java:7507) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7446) at android.app.ActivityThread.access$1500(ActivityThread.java:301) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2148) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:246) at android.app.ActivityThread.main(ActivityThread.java:8512) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
I would appreciate if you guys can investigate what could cause this crash to start happening in 2.7.3
, as our app will be unable to update until it is resolved.
Thank you!
Platform: Android
I have followed the documentation and used everything stated there, created my SDK API Key for client-side inside an Environment as below
I have hidden organization details.
But after adding everything just to test on my MainActivity
val sdkKey = "my-api-key-here"
val config: SplitClientConfig = SplitClientConfig.builder().build()
val matchingKey = "key"
val key = Key(matchingKey)
val splitFactory: SplitFactory =
SplitFactoryBuilder.build(sdkKey, key, config, applicationContext)
val client: SplitClient = splitFactory.client()
client.on(SplitEvent.SDK_READY, object : SplitEventTask() {
override fun onPostExecution(client: SplitClient) {
// Execute background logic here
when (client.getTreatment("my_feature_flag")) {
"on" -> {
// insert code here to show on treatment
Log.d("SPLIT", "onPostExecution: ON")
}
"off" -> {
// insert code here to show off treatment
Log.d("SPLIT", "onPostExecution: OFF")
}
else -> {
// insert your control treatment code here
Log.d("SPLIT", "onPostExecution: ELSE")
}
}
}
override fun onPostExecutionView(client: SplitClient) {
// Execute main thread logic here
when (client.getTreatment("ncuenta_version")) {
"on" -> {
// insert code here to show on treatment
Log.d("SPLIT", "onPostExecutionView: ON")
}
"off" -> {
// insert code here to show off treatment
Log.d("SPLIT", "onPostExecutionView: OFF")
}
else -> {
// insert your control treatment code here
Log.d("SPLIT", "onPostExecutionView: ELSE")
}
}
}
})
Nothing returns in the logs, I have already created the feature flag and everything, granted INTERNET access into the manifest, IDK what is happening, it should work
implementation("io.split.client:android-client:3.3.0")
compileSdk = 34
I'm only receiving
Also added
val config: SplitClientConfig = SplitClientConfig.builder().impressionsMode(ImpressionsMode.DEBUG).build()
but cannot see anything in the console
I have also followed the demo app here
but no success
I'm seeing a crash happen when trying to create a SplitClient
using a matchingKey that contains non-URL encoded characters (to be specific, |
).
We're getting this key from our auth provider, Auth0, and the key is in the format auth0|uuid
. We'd like to be able to use this string as the matching key, and not need to manually URL-encode that string everywhere else we use it in our systems.
Split SDK version: 2.6.8
Stacktrace:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: xx.xx.xxx.android.xxx, PID: 28442
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.net.URISyntaxException: Illegal character in path at index 41: https://sdk.split.io/api/mySegments/auth0|randomlygeneratedcode1203234
at java.net.URI$Parser.fail(URI.java:2893)
at java.net.URI$Parser.checkChars(URI.java:3066)
at java.net.URI$Parser.parseHierarchical(URI.java:3150)
at java.net.URI$Parser.parse(URI.java:3098)
at java.net.URI.<init>(URI.java:584)
at io.split.android.client.network.SdkTargetPath.buildUrl(SdkTargetPath.java:47)
at io.split.android.client.network.SdkTargetPath.buildUrl(SdkTargetPath.java:39)
at io.split.android.client.network.SdkTargetPath.mySegments(SdkTargetPath.java:23)
at io.split.android.client.service.ServiceFactory.getMySegmentsFetcher(ServiceFactory.java:63)
at io.split.android.client.SplitFactoryHelper.buildApiFacade(SplitFactoryHelper.java:97)
at io.split.android.client.SplitFactoryImpl.<init>(SplitFactoryImpl.java:164)
at io.split.android.client.SplitFactoryImpl.<init>(SplitFactoryImpl.java:84)
at io.split.android.client.SplitFactoryBuilder.build(SplitFactoryBuilder.java:67)
at xx.xxx.xxx.service.feature_flag.NativeSplitIoClient.<init>(NativeSplitIoClient.kt:29)
Hi, there is a crash on WorkManager which is caused when calling SplitFactoryBuilder.build
. It happens only on certain device, it happened on Samsung S20, Android OS 10. I tested on emulator OS 10 is actually working fine. Is there anything I need to define to avoid this? I am using version 2.6.4
My split client initialization code
private lateinit var instance: SplitClient
fun refreshInstance(userId: String?) {
if (this::instance.isInitialized) {
instance.flush()
instance.destroy()
}
val config = SplitClientConfig.builder()
.build()
val splitFactory = SplitFactoryBuilder.build(
configuration.splitKey,
Key(userId ?: "GUEST"),
config,
application.applicationContext
)
instance = splitFactory.client()
}
Location of crash at the Split SDK:
//SplitFactoryHelper line 94
WorkManagerWrapper buildWorkManagerWrapper(Context context, SplitClientConfig splitClientConfig,
String apiKey, String key, String databaseName) {
return new WorkManagerWrapper(
WorkManager.getInstance(context), splitClientConfig, apiKey, key, databaseName);
}
Hi,
We're using the Android Client v2.13.1 in our app and lately all the splits that have custom targeting rules are coming back with a value of control
instead of on
or off
We've followed the docs here and are using the SplitEvent.SDK_READY
to get the treatments but that hasn't helped
Here's the code snippet
override fun retrieveFeatureWithAttributeFromApi(
feature: SplitFeature,
attributes: Map<String, String>,
statusRetrieved: (status: FeatureStatus) -> Unit
) {
if (this::splitClient.isInitialized) {
splitClient.on(
SplitEvent.SDK_READY,
object : SplitEventTask() {
override fun onPostExecution(client: SplitClient) {}
override fun onPostExecutionView(client: SplitClient) {
when (client.getTreatment(feature.label, attributes).uppercase()) {
"ON" -> statusRetrieved(FeatureStatus.ON)
"OFF" -> statusRetrieved(FeatureStatus.OFF)
else -> statusRetrieved(FeatureStatus.UNKNOWN)
}
}
}
)
} else {
Log.e(
TAG_FEATURE_FLIPPER,
"Cannot retrieve features on uninitialized feature flipper instance"
)
}
}
And here's what we get when we call client.getTreatment(features, attributes)
{
"android-split-test": "control",
"cross-platform-upgrades": "control",
"ideas-v0": "off",
"android-selected-profile-id-refactor": "control",
"android-ideas-url-highlighting": "control",
"android-attributes-test": "off",
"android-edit-update-using-model": "control",
"android-app-tour": "off",
"ideas-android-share-extension": "control"
}
Would help us a lot if we got a pair of eyes on this. Thanks! 😇
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.