Skip to content

MOBILE-78: Refactor LifecycleManager with startup initializer and fix TrackVisit#714

Closed
enotniy wants to merge 1 commit into
developfrom
feature/MOBILE-78-code
Closed

MOBILE-78: Refactor LifecycleManager with startup initializer and fix TrackVisit#714
enotniy wants to merge 1 commit into
developfrom
feature/MOBILE-78-code

Conversation

@enotniy
Copy link
Copy Markdown
Collaborator

@enotniy enotniy commented May 18, 2026

Summary

  • Fix race condition (UninitializedPropertyAccessException): replaced lateinit var lifecycleManager with a nullable singleton (LifecycleManager.instance) so that invokeOnCompletion callbacks on Dispatchers.Default can never access an uninitialised reference.
  • Add MindboxLifecycleInitializer (androidx.startup): registers the lifecycle manager during the ContentProvider phase, before Application.onCreate, ensuring the instance is always set before any Activity lifecycle events fire.
  • Fix TrackVisit suppression on first init: wasReinitialized() (which sets skipNextTrackVisit = true) is now guarded by !firstInitCall.get(), so it is only called on re-initialisation — not on the very first Mindbox.init().
  • Fix TrackVisit not sent in fallback path: when MindboxLifecycleInitializer is absent from the manifest and init is called from an Activity, the NOT_UPDATED branch now checks isTrackVisitSent() and sends TrackVisit directly if it was missed.

Test plan

  • Init from Application.onCreate — TrackVisit sent once, no crash
  • Init from Activity.onCreate (no MindboxLifecycleInitializer in manifest) — TrackVisit still sent
  • Re-init (call Mindbox.init a second time) — TrackVisit not sent again (skipNextTrackVisit respected)
  • Debug breakpoint on invokeOnCompletion body — lifecycleManager is never null, no UninitializedPropertyAccessException
  • Unit tests in the companion PR (feature/MOBILE-78-tests) pass

🤖 Generated with Claude Code

… TrackVisit

- Replace `lateinit var lifecycleManager` with nullable singleton via
  `LifecycleManager.instance` (eliminates UninitializedPropertyAccessException
  race in `invokeOnCompletion` callback on Dispatchers.Default)
- Add `MindboxLifecycleInitializer` (androidx.startup) so the lifecycle
  manager is registered in ContentProvider phase, before Application.onCreate
- Guard `wasReinitialized()` with `!firstInitCall.get()` so TrackVisit is not
  suppressed on the very first `Mindbox.init()` call
- Send TrackVisit in the NOT_UPDATED branch when initialised from an Activity
  (fallback when MindboxLifecycleInitializer is absent from the manifest)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors SDK lifecycle tracking by introducing an androidx.startup initializer and reworking LifecycleManager to be a singleton with callback-based wiring, aiming to prevent init-time races and ensure TrackVisit dispatch reliability.

Changes:

  • Replace Mindbox’s lateinit lifecycleManager with LifecycleManager.instance access and split init flow into helper methods.
  • Add MindboxLifecycleInitializer + manifest/provider metadata to register lifecycle tracking during the Startup/ContentProvider phase.
  • Refactor LifecycleManager to support “pending” TrackVisit dispatch until callbacks are attached; minor WebView in-app threading changes.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
sdk/src/main/java/cloud/mindbox/mobile_sdk/Mindbox.kt Refactors init flow; uses singleton lifecycle manager; attaches lifecycle callbacks.
sdk/src/main/java/cloud/mindbox/mobile_sdk/managers/MindboxLifecycleInitializer.kt New Startup initializer to register LifecycleManager early.
sdk/src/main/java/cloud/mindbox/mobile_sdk/managers/LifecycleManager.kt Converts to singleton + callback interface; implements pending TrackVisit behavior.
sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/presentation/view/WebViewInappViewHolder.kt Adjusts close calls to run on the view thread; modifies payload models.
sdk/src/main/AndroidManifest.xml Adds Startup provider metadata for MindboxLifecycleInitializer.
sdk/build.gradle Adds androidx.startup dependency.
modulesCommon.gradle Removes Kover plugin application.
gradle/libs.versions.toml Adds Startup version; removes Kover entries.
build.gradle Removes Kover plugin/task configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +600 to +608
if (checkResult == ConfigUpdate.UPDATED) {
setPushServiceHandler(context, pushServices)
firstInitialization(context.applicationContext, validatedConfiguration)
} else {
mindboxScope.launch {
setPushServiceHandler(context, pushServices)
}
MindboxPreferences.uuidDebugEnabled = configuration.uuidDebugEnabled
}.initState(InitializeLock.State.SAVE_MINDBOX_CONFIG)
.invokeOnCompletion { throwable ->
if (throwable == null) {
if (firstInitCall.get()) {
val activity = context as? Activity
if (activity != null && lifecycleManager.isCurrentActivityResumed) {
inAppMessageManager.registerCurrentActivity(activity)
mindboxScope.launch {
inAppMutex.withLock {
logI("Start inapp manager after init. firstInitCall: ${firstInitCall.get()}")
if (!firstInitCall.getAndSet(false)) return@launch
inAppMessageManager.listenEventAndInApp()
inAppMessageManager.initLogs()
MindboxEventManager.eventFlow.emit(MindboxEventManager.appStarted())
inAppMessageManager.requestConfig().join()
}
}
MindboxEventManager.sendEventsIfExist(context.applicationContext)
}
Comment on lines 784 to 790
private data class NavigationInterceptedPayload(
@SerializedName("url")
val url: String
)

private data class ErrorPayload(
@SerializedName("error")
val error: String
)
@enotniy enotniy closed this May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants