Skip to content

MOBILE-269: Webview cache #735

Open
enotniy wants to merge 7 commits into
developfrom
feature/MOBILE-269
Open

MOBILE-269: Webview cache #735
enotniy wants to merge 7 commits into
developfrom
feature/MOBILE-269

Conversation

@enotniy

@enotniy enotniy commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment thread .gitmodules Outdated
justSmK and others added 5 commits July 14, 2026 13:59
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.
@enotniy enotniy force-pushed the feature/MOBILE-269 branch from 6636b54 to 2357aaf Compare July 14, 2026 11:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 18 changed files in this pull request and generated 2 comments.

Comment thread .gitmodules Outdated
Comment thread sdk/src/main/java/cloud/mindbox/mobile_sdk/Mindbox.kt
@enotniy enotniy requested a review from justSmK July 14, 2026 14:59
private fun softReinitialization(
context: Context,
) {
MindboxDI.appModule.inAppWebViewPrewarmManager.terminate()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

FYI, не блокер. После terminate() прогрев не оживает до перезапуска приложения — флаг взводится навсегда, а объекты при soft re-init не пересоздаются. То есть: приложение сменило endpoint → до конца этой сессии прогрева нет → первый показ инаппа холодный. Со следующего запуска всё снова работает.

Кейс редкий, так что ок как есть. Если захочется добить: вместо вечного флага сделать у менеджера reset() и звать его после пересоздания scope.

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.

3 participants