-
Notifications
You must be signed in to change notification settings - Fork 0
feat: expose getAnonymousID() #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
22edba4
251bd71
9ce1137
464ebef
eaedb0b
90e7c19
6ae456c
0edb928
3d80bdb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ package com.metarouter.analytics | |
| import android.content.Context | ||
| import com.metarouter.analytics.lifecycle.AppLifecycleObserver | ||
| import com.metarouter.analytics.utils.Logger | ||
| import kotlinx.coroutines.CompletableDeferred | ||
| import kotlinx.coroutines.CoroutineScope | ||
| import kotlinx.coroutines.Dispatchers | ||
| import kotlinx.coroutines.Job | ||
|
|
@@ -30,6 +31,10 @@ object MetaRouter { | |
| /** Atomic flag to ensure only one initialization attempt proceeds. */ | ||
| private val initializationStarted = AtomicBoolean(false) | ||
|
|
||
| /** Signal that completes when the real client is bound to the proxy. */ | ||
| @Volatile | ||
| private var boundSignal = CompletableDeferred<Unit>() | ||
|
|
||
| @Volatile | ||
| private var lifecycleObserver: AppLifecycleObserver? = null | ||
|
|
||
|
|
@@ -57,7 +62,11 @@ object MetaRouter { | |
| initializeInternal(context, options) | ||
| } catch (e: Exception) { | ||
| Logger.error("Background initialization failed: ${e.message}") | ||
| // Reset flag to allow retry | ||
| // Wake current awaiters with failure, then swap in a fresh | ||
| // signal so a retry's later boundSignal.complete(Unit) isn't | ||
| // swallowed by an already-completed deferred. | ||
| boundSignal.completeExceptionally(e) | ||
| boundSignal = CompletableDeferred() | ||
| initializationStarted.set(false) | ||
| } | ||
| } | ||
|
|
@@ -115,6 +124,7 @@ object MetaRouter { | |
| lifecycleObserver?.register() | ||
|
|
||
| proxy.bind(client) | ||
| boundSignal.complete(Unit) | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -185,6 +195,39 @@ object MetaRouter { | |
| resetInternal() | ||
| } | ||
|
|
||
| /** | ||
| * Get the current anonymous ID, suspending until the SDK is ready. | ||
| * | ||
| * If initialization is still in progress, this suspends until the | ||
| * client is bound and ready, then returns the anonymous ID. | ||
| * If already initialized, returns immediately. | ||
| * | ||
| * @return The current anonymous ID | ||
| * @throws IllegalStateException if MetaRouter has not been initialized | ||
| */ | ||
| suspend fun getAnonymousId(): String { | ||
| // Capture the current boundSignal reference under the same lock | ||
| // reset uses, so we can't observe a mid-reset state where the flag | ||
| // is still true but boundSignal has already been swapped for a | ||
| // fresh (never-completing) deferred. | ||
| val signal: CompletableDeferred<Unit> = initMutex.withLock { | ||
| if (!initializationStarted.get()) { | ||
| throw IllegalStateException( | ||
| "MetaRouter not initialized. Call MetaRouter.Analytics.initialize() first." | ||
| ) | ||
| } | ||
| boundSignal | ||
| } | ||
|
|
||
| signal.await() | ||
|
|
||
| val client = store.get() | ||
| ?: throw IllegalStateException( | ||
| "MetaRouter bound signal completed but client store is empty (likely reset during getAnonymousId)" | ||
| ) | ||
| return client.getAnonymousId() | ||
| } | ||
|
Comment on lines
+208
to
+229
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another claude proxy! I was actually reading about this in Go for atomic vs mutex, so it semi-made sense. Take it or leave it.
Claude says something like (heavily recommending you have claude double check this, don't take it as matter of fact). And a "thank you for your patience" I really do not like leaving "claude says..." as PR review comments, but leveraging the review skill after human review because I don't know kotlin that well.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ended up making this change and it is a bit better organized. |
||
|
|
||
| /** | ||
| * Enable or disable debug logging. | ||
| * | ||
|
|
@@ -215,6 +258,9 @@ object MetaRouter { | |
| // Reset the proxy so it can be bound to a new client | ||
| proxy.unbind() | ||
|
|
||
| // Reset bound signal for next initialization | ||
| boundSignal = CompletableDeferred() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💪 |
||
|
|
||
| // Reset initialization flag | ||
| initializationStarted.set(false) | ||
|
|
||
|
|
@@ -236,6 +282,7 @@ object MetaRouter { | |
| lifecycleObserver = null | ||
| store.clear() | ||
| proxy.resetForTesting() | ||
| boundSignal = CompletableDeferred() | ||
| initializationStarted.set(false) | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Claude proxying on this one:
If
createAnalyticsClientis called fire + forget style and bg coroutine fails,boundSignalis never completedPossible fix:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a great and valid callout thank you!