test(sample): add opt-in deferred-init edge-case example (Android)#341
Conversation
Adds a self-contained, disabled-by-default example that reproduces the Android late-initialisation Activity-capture race: starting mParticle from a native module at first-frame paint instead of Application.onCreate(). The Rokt SDK (<= v5) caches the current Activity only on onActivityResumed (registered during Rokt.init() via RoktKit.onKitCreate()), so deferring init past the host Activity's resume leaves overlay placements unable to display until the next resume. iOS is unaffected. Fixed upstream in the Rokt Android SDK (ROKT/sdk-android-source #1062, #1063; v5 backport #1082). Default sample behaviour is unchanged (standard eager init). Flip DeferredInitModule.DEFERRED_INIT_EXAMPLE to true to reproduce; the EAGER vs DEFERRED ActivityTracker logs (adb logcat -s DeferredInitRepro) make the race observable. Kept around as an easy regression/verification fixture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR SummaryLow Risk Overview
Reviewed by Cursor Bugbot for commit 5c643ac. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Pull request overview
Adds an opt-in (disabled-by-default) repro path in the sample app to demonstrate the Android deferred-initialization / missed-onActivityResumed race that affects Rokt overlay placements when MParticle.start() happens after the host Activity is already resumed.
Changes:
- Document the deferred-init edge case and repro steps in the sample README.
- Trigger a first-frame
requestAnimationFramecall in JS to start mParticle via a native module (for the repro). - Add Android sample native code (
DeferredInitModule,DeferredInitPackage,ActivityTracker) and wire it intoMainApplication.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| sample/README.md | Adds documentation and steps to enable/reproduce the Android deferred-init race. |
| sample/index.js | Adds first-frame JS hook to invoke the Android deferred-init native module. |
| sample/android/app/src/main/java/com/mparticlesample/MainApplication.kt | Adds the package + conditional eager-vs-deferred init path. |
| sample/android/app/src/main/java/com/mparticlesample/DeferredInitPackage.kt | Registers the Android native module via a ReactPackage. |
| sample/android/app/src/main/java/com/mparticlesample/DeferredInitModule.kt | Implements the deferred-start entry point called from JS. |
| sample/android/app/src/main/java/com/mparticlesample/ActivityTracker.kt | Adds a lifecycle tracker to mirror Rokt’s Activity capture behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…empotent Addresses review feedback: - index.js: only run the first-frame DeferredInit call on Android, so iOS no longer logs a spurious 'module not available' warning every launch. - DeferredInitModule: short-circuit startMParticle() when MParticle is already started, so repeat JS calls (Fast Refresh / remounts) don't double-register lifecycle callbacks or re-start the SDK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 52e1059. Configure here.
Documents the expected secondary deferred-init hazard flagged in review: with DEFERRED_INIT_EXAMPLE on, mParticle JS calls made before first-frame start are no-ops until the SDK starts. Default (eager) behaviour is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

What
Adds a small, disabled-by-default example to the sample app that reproduces the Android deferred-initialisation edge case: starting mParticle from a native module at first-frame paint instead of
MainApplication.onCreate().Why
A partner deferring
MParticle.start()(to shave init cost off startup) saw flaky Rokt overlay placements on Android but not iOS. Root cause: the Rokt Android SDK (≤ v5) caches the currentActivityonly ononActivityResumed, via an observer registered duringRokt.init()(which mParticle calls fromRoktKit.onKitCreate()). When init runs after the host Activity has already resumed — exactly what deferred init does — that resume is missed, so overlay/bottom-sheet placements can't display until the next resume (the "press home and reopen" workaround). iOS resolves the presenter lazily at execute time, so it's unaffected.Fixed upstream in the Rokt Android SDK:
ROKT/sdk-android-source#1062 / #1063 (v5 backport #1082). This example is kept around as an easy way to re-trigger the scenario and confirm the fix / catch regressions.How it works
DeferredInitModule(native) starts mParticle when JS calls it at first-frame paint (requestAnimationFrameinindex.js).ActivityTrackermirrors Rokt's Activity-capture: anEAGERinstance registered at process start vs aDEFERREDinstance registered at init time. With the example on,EAGERcapturesMainActivitywhileDEFERREDstaysnull— the race, visible viaadb logcat -s DeferredInitRepro.Default behaviour unchanged
DeferredInitModule.DEFERRED_INIT_EXAMPLEdefaults tofalse, so the sample uses the standard eager init inMainApplication.onCreate(). Flip it totrueto reproduce. Seesample/README.md→ "Deferred-init edge case (Android)".Validation
:app:compileDebugKotlinpasses (default flag off).