[Android] Add preload state observability#451
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Package Size
Android file breakdown
Measured from the PR base SHA and PR head SHA. The file breakdown shows uncompressed sizes within each package artifact, so individual files do not sum to the compressed artifact total. This comment reports package artifact sizes only; it is not a final app binary-size report. |
87cb6db to
89e6e70
Compare
89e6e70 to
284f325
Compare
284f325 to
bd21ffc
Compare
73a7f40 to
2cac69b
Compare
eeb5755 to
95c911b
Compare
2cac69b to
4087102
Compare
95c911b to
a0d96f6
Compare
0c56668 to
9e05f19
Compare
55ef015 to
b59bb3e
Compare
00d2d04 to
c8f76e8
Compare
381d5ca to
65a51ef
Compare
2cfbff0 to
912a186
Compare
3209276 to
ec711d4
Compare
912a186 to
6c6f096
Compare
ec711d4 to
16dd07a
Compare
6c6f096 to
6876cd9
Compare
16dd07a to
880c148
Compare
6876cd9 to
599dafd
Compare
880c148 to
f0af5fd
Compare
599dafd to
aaf688c
Compare
f0af5fd to
0c29857
Compare
| terminate(if (cached.key == key) PreloadState.Expired else PreloadState.Idle) | ||
| null | ||
| } else { | ||
| ShopifyCheckoutKit.log.d(LOG_TAG, "Returning cached preloaded WebView.") |
There was a problem hiding this comment.
[Re: line +69]
Do we also need a transition here? E.g. a state of PreloadState.consumed or similar
Or from another angle, do we need to actually consume preloads in this way :hmm:
See this comment inline on Graphite.
There was a problem hiding this comment.
I'm just leaving it as .ready across platforms for now. I didn't introduce any state to indicate that it's been presented, but it's worth considering.
Something like this might work:
.idle → .loading → .ready → .presented → (dismiss) .ready → .expired
b0aff16 to
e67aa39
Compare
0c29857 to
3980889
Compare
e67aa39 to
a896563
Compare
3980889 to
8410f99
Compare
Install this buildOpen Tophat, select your target device, then click Install. Links open on the Mac running Tophat.
Checkout Kit E2E results
|
f5fa14a to
ba71690
Compare
a896563 to
198bbd0
Compare
Assisted-By: devx/42793469-bf64-49c6-b1db-f4d6c628a330
ba71690 to
affdb58
Compare
| /** | ||
| * Called on the main thread whenever the preload state changes. | ||
| */ | ||
| public var listener: PreloadStateListener? = null |
There was a problem hiding this comment.
On iOS, assigning onStateChange immediately replays the current state via a didSet; on Android listener is a plain property with no replay, so a consumer who attaches a listener after obtaining the handle receives nothing until the next transition.
I think this gets us closer to iOS / didSet
public var listener: PreloadStateListener? = null
set(value) {
field = value
value?.onStateChanged(cache.state)
}There was a problem hiding this comment.
Updated. Assigning listener now immediately replays the current state on the main thread, matching iOS.
| public fun preload( | ||
| checkoutUrl: String, | ||
| context: ComponentActivity, | ||
| listener: PreloadStateListener? = null, |
There was a problem hiding this comment.
The cache stores the observer as a WeakReference<CheckoutPreload>, and preload(url, activity) { state -> ...} returns the handle but nothing else retains it.
Only Loading fires synchronously, al the later states are driven by async WebView callbacks.
Because the old API returned Unit, folks migrating might keep that call shape, and add the trailing lambda (i.e. no assigning handle to a variable). That would mean CheckoutPreload is unreferenced and eligible for garbage collection right after the preload call returns.
If it gets collected, consumers might not get the other state transitions.
I wonder if we should keep a handle to CheckoutPreload in the library (until a terminal state / another preload call / preload consumed / preload fails)? Maybe passing the listener into CheckoutWebView.preload(), passing into preloadCache.store() storing as an instance var in the cache, and then setting to null at various transitions
There was a problem hiding this comment.
Agreed. The cache now strongly retains the current CheckoutPreload, so callback-only usage continues receiving asynchronous transitions. It releases the observer on Idle, Expired, or Failed, or replaces it when another preload starts. Cleanup is reentrancy-safe, so callbacks can start a replacement preload.

Adds observable lifecycle state to preloaded checkouts on Android, mirroring the iOS
CheckoutPreloadAPI.preload(checkoutUrl, context, onStateChange)now returns a retainedCheckoutPreloadhandle exposing astateproperty and anonStateChangecallback that emitLoading → Ready → Expired, plusFailed(reason)for HTTP and navigation failures, andIdleon manual invalidation. State is driven from a singletransitionchoke point in the preload cache and broadcast to a weak observer, so consumers can drive spinners or fall back to a fresh preload when a warm entry lapses.Android has no keep-alive timer, so there is no
keepAliveLostreason and expiry is evaluated lazily when a cached entry is taken.