MOBILE-269: Webview cache #735
Conversation
There was a problem hiding this comment.
Pull request overview
Adds WebView in-app prewarming + cache policy wiring (feature-toggled), and makes WebView readiness checking resilient to slow/late JS bridge initialization to avoid prematurely closing healthy in-apps.
Changes:
- Introduces WebView prewarm manager (init-time + post-config) with learned-hosts persistence and settle-release logic.
- Adds a latched, cached-config-driven WebView cache toggle policy and passes it into WebView creation.
- Reworks WebView readiness detection to poll briefly after
onPageFinished, with new unit tests and updated repository/DI wiring.
Reviewed changes
Copilot reviewed 16 out of 18 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/presentation/view/WebViewReadyCheckerTest.kt | JVM tests for retrying readiness polling + cancellation. |
| sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/presentation/InAppWebViewPrewarmManagerImplTest.kt | Coroutine tests for prewarm one-shot, preemption, settle release, and cached-config priming. |
| sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/presentation/InAppWebViewCachePolicyTest.kt | Tests for latched cache-toggle behavior and prime() semantics. |
| sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/data/repositories/MobileConfigRepositoryImplTest.kt | Updates repository construction for new prewarm manager dependency. |
| sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/data/managers/InAppWebViewLearnedHostsStoreTest.kt | Robolectric tests for learned-host persistence/merge/cap behavior. |
| sdk/src/main/java/cloud/mindbox/mobile_sdk/Mindbox.kt | Triggers prewarm stage 1 during SDK init. |
| sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/presentation/view/WebViewReadyChecker.kt | Adds retrying readiness poller with injected evaluate/schedule for testability. |
| sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/presentation/view/WebViewInappViewHolder.kt | Integrates readiness polling, prewarm preemption, cache policy, and learned-host capture on close. |
| sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/presentation/InAppWebViewPrewarmManager.kt | Implements staged prewarm, settle-release, and observed-host parsing/merging. |
| sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/presentation/InAppWebViewCachePolicy.kt | Adds latched cache-toggle decision derived from cached config. |
| sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/data/repositories/MobileConfigRepositoryImpl.kt | Calls prewarm stage 2 after config application. |
| sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/data/managers/InAppWebViewLearnedHostsStore.kt | Persists per-endpoint learned hosts to support preconnect warming. |
| sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/data/managers/FeatureToggleManagerImpl.kt | Adds prewarm/cache toggle keys and shared default behavior constant. |
| sdk/src/main/java/cloud/mindbox/mobile_sdk/di/modules/MindboxModule.kt | Exposes prewarm manager + cache policy in DI module contract. |
| sdk/src/main/java/cloud/mindbox/mobile_sdk/di/modules/DataModule.kt | Wires prewarm manager/cache policy and injects into repository. |
| .gitmodules | Minor formatting change in submodule config. |
| .gitignore | Ignores example properties file. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
MOBILE-0000: Bump kmp submodule (drain evaluate callbacks before destroy) MOBILE-0000: Cover the stranded-preconnect release and cap budget with tests MOBILE-0000: Release the stranded preconnect WebView and honest-cap the settle poll A no-layers config landing while the init prewarm was suspended latched the verdict but the resumed prewarm bare-returned after the fetch: the preconnect load had already created a WebView and no settle poll would ever free it. The verdict is now re-checked before the preconnect load and the post-fetch path releases. Also: null probes are charged the probe timeout so the settle cap is a real wall-clock bound; ready-check give-up defers to the init timer instead of closing; teardown-race evaluate misses no longer feed telemetry; GatewayManager is injected lazily (Volley was constructed on the main thread during SDK init); learned-hosts merge is synchronized; kmp submodule bump (destroy fix, prewarm navigation pinning, httpsOrigin charset). MOBILE-0000: Cover the settle-poll guards, one-shot survival and hard cap MOBILE-0000: Harden the prewarm service and show holder after the audit - the settle poll checks the terminal-preempt latch before it is stored and on every tick — a real show could otherwise inherit a 30s zombie poll in the slot onRealShowWillStart() just cleared; - a no-layers config latches: an init prewarm still suspended in the content fetch can no longer resurrect a WebView the config retired; - the one-shot is consumed only by attempts that reach the engine, so a transient configuration read failure cannot block a later valid warm; - the poll budget is counted in ticks, not wall clock (same 30s, fully virtual-time testable); - onClose detaches the event listener and startReadyCheck refuses a torn-down holder — a late onPageFinished queued on the main looper produced a spurious presentation failure and a second close; - controller.destroy() is called once, not twice; - checkEvaluateJavaScript tracks a failed outgoing call but no longer force-closes the in-app — that is the caller's policy, and one transient miss used to tear down a healthy show. MOBILE-0000: Cover the network-idle settle release MOBILE-0000: Release the prewarm WebView by network idle, not a blind timer The hidden instance lived a fixed 30s after the content page load; a Resource Timing poll (stable entry count + 2s quiet window) releases it as soon as the page has settled — ~3s in practice. The 30s stays as a hard cap: entries appear only on completion, so a transfer slower than the quiet window can still be cut, same worst case as the cap. MOBILE-0000: Cover the WebView readiness probe retry MOBILE-0000: Retry the WebView readiness probe before closing the in-app onPageFinished can fire before the page's module scripts have evaluated (slow device, cold cache), and the single-shot bridge check then closed a healthy in-app. The probe now polls briefly (WebViewReadyChecker, mirrors the iOS fix); the outgoing-message verification stays single-shot — that path talks to a page that already proved itself ready. MOBILE-0000: Update prewarm service tests for the stub-less engine MOBILE-0000: Drop the legacy prewarm stub bridge Bumps kmp-common: the engine loads the content page without the stub JavascriptInterface; old pages degrade to a plain page warm until the web prewarm mode is deployed. MOBILE-0000: Update prewarm service tests for the params contract MOBILE-0000: Load the prewarm content page with official prewarm params A web runtime that knows the contract boots tracker-only from the document URL params; older runtimes ignore them and keep using the engine's legacy stub bridge. Bumps kmp-common with prewarmContentBaseUrl and the stub-call logging. MOBILE-0000: Preempt prewarm terminally + thread-safe settle job Real-show preemption now calls engine.abort() (synchronous latch closes the race where an already-posted prewarm load recreated the WebView mid-show and, with the settle job cancelled, kept it alive forever). settleJob becomes an AtomicReference. Bump kmp-common. MOBILE-0000: Gate prewarm light parse by sdkVersion like the real pipeline The endpoint config carries webview-typed form variants for newer SDK versions; without the version gate the head-start parse logs a JsonParseException per such in-app on every launch. MOBILE-0000: Wire production WebView prewarm service into SDK lifecycle InAppWebViewPrewarmService: stage 1 at init (head start from cached config, light layer parse), stage 2 after each config parse in MobileConfigRepositoryImpl (release when no webview in-apps), preemption from WebViewInAppViewHolder before a real show, learned-hosts capture at bridge close feeding next launch's preconnect. Everything derived from config + API domain; no hardcoded hosts. Bump kmp-common with cache/prewarm primitives. MOBILE-0000: Prewarm WebView at SDK init + bump kmp-common Call MindboxWebViewLab.prewarm() from Mindbox.initialize() to warm the renderer process/connections early. Bump kmp-common-sdk submodule to the prototype branch carrying the cache+preconnect prototype. Throwaway measurement scaffolding.
6636b54 to
2357aaf
Compare
2357aaf to
7c31a18
Compare
| private fun softReinitialization( | ||
| context: Context, | ||
| ) { | ||
| MindboxDI.appModule.inAppWebViewPrewarmManager.terminate() |
There was a problem hiding this comment.
FYI, не блокер. После terminate() прогрев не оживает до перезапуска приложения — флаг взводится навсегда, а объекты при soft re-init не пересоздаются. То есть: приложение сменило endpoint → до конца этой сессии прогрева нет → первый показ инаппа холодный. Со следующего запуска всё снова работает.
Кейс редкий, так что ок как есть. Если захочется добить: вместо вечного флага сделать у менеджера reset() и звать его после пересоздания scope.
https://tracker.yandex.ru/MOBILE-269