Skip to content

Set trackUpdateTime in Android MetricPlugin - #928

Draft
cehan-Chloe wants to merge 7 commits into
mainfrom
metric-plugin-trackupdatetime
Draft

Set trackUpdateTime in Android MetricPlugin#928
cehan-Chloe wants to merge 7 commits into
mainfrom
metric-plugin-trackupdatetime

Conversation

@cehan-Chloe

@cehan-Chloe cehan-Chloe commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Enables trackUpdateTime by default in the JVM/Android MetricsPlugin, matching the web wrapper's default (trackRenderTime + trackUpdateTime both true). Previously JVM only set trackRenderTime: true, so update timings were never captured on Android.

Since JVM's renderEnd() is called manually by the host (not automatically like web/React), an update timing only completes when the host calls renderEnd() again after that update paints. Streaming integrations already do this; non-streaming hosts that don't simply produce no update data — render tracking is unaffected either way.

Docs updated for android, ios, and core to clarify this per-platform update-time behavior, including why iOS can't support it yet (onAppear only fires on first appearance, no per-update paint signal).

Added a JVM test (tracks view update times) covering the update-time path, using the action-counter mock.

Change Type (required)

Indicate the type of change your pull request is:

  • patch
  • minor
  • major
  • N/A

Does your PR have any documentation updates?

  • Updated docs
  • No Update needed
  • Unable to update docs

Release Notes

Android's MetricsPlugin now tracks update time by default (trackUpdateTime: true), matching the web wrapper. Update timings are only recorded if the host calls metrics.renderEnd() after each update paints.

@intuit-svc

intuit-svc commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results

Comparison against baseline from main. ⚠️ = regression (>10% slower), ✅ = improvement (>5% faster)

core/player ⚠️

Benchmark Current Baseline Change
core/player/src/binding/__tests__/parser.bench.ts > parser benchmarks > Resolving binding: foo.bar 870.42K ops/s 722.92K ops/s +20.4% ✅
core/player/src/binding/__tests__/parser.bench.ts > parser benchmarks > Resolving binding: foo.pets.1.name 539.70K ops/s 541.65K ops/s -0.4%
core/player/src/binding/__tests__/parser.bench.ts > parser benchmarks > Resolving binding: foo.pets.01.name 514.08K ops/s 490.79K ops/s +4.7%
core/player/src/binding/__tests__/parser.bench.ts > parser benchmarks > Resolving binding: foo.pets['01'].name 468.88K ops/s 477.60K ops/s -1.8%
core/player/src/binding/__tests__/parser.bench.ts > parser benchmarks > Resolving binding: foo.pets[01].name 523.99K ops/s 519.91K ops/s +0.8%
core/player/src/binding/__tests__/parser.bench.ts > parser benchmarks > Resolving binding: foo.pets[name = "frodo"].type 286.04K ops/s 284.75K ops/s +0.5%
core/player/src/binding/__tests__/parser.bench.ts > parser benchmarks > Resolving binding: foo.pets["name" = "sprinkles"].type 225.72K ops/s 220.52K ops/s +2.4%
core/player/src/binding/__tests__/parser.bench.ts > parser benchmarks > Resolving binding: foo.pets["isDog" = false].type 291.73K ops/s 290.37K ops/s +0.5%
core/player/src/binding/__tests__/parser.bench.ts > parser benchmarks > Resolving binding: foo.pets["isDog" = true].type 302.53K ops/s 309.01K ops/s -2.1%
core/player/src/binding/__tests__/parser.bench.ts > binding creation benchmarks > Resolving binding: foo.bar 602.99K ops/s 624.16K ops/s -3.4%
core/player/src/binding/__tests__/parser.bench.ts > binding creation benchmarks > Resolving binding: foo.pets.1.name 367.82K ops/s 388.57K ops/s -5.3%
core/player/src/binding/__tests__/parser.bench.ts > binding creation benchmarks > Resolving binding: foo.pets.01.name 389.17K ops/s 371.06K ops/s +4.9%
core/player/src/binding/__tests__/parser.bench.ts > binding creation benchmarks > Resolving binding: foo.pets['01'].name 327.21K ops/s 290.01K ops/s +12.8% ✅
core/player/src/binding/__tests__/parser.bench.ts > binding creation benchmarks > Resolving binding: foo.pets[01].name 377.04K ops/s 365.25K ops/s +3.2%
core/player/src/binding/__tests__/parser.bench.ts > binding creation benchmarks > Resolving binding: foo.pets[name = "frodo"].type 232.05K ops/s 232.45K ops/s -0.2%
core/player/src/binding/__tests__/parser.bench.ts > binding creation benchmarks > Resolving binding: foo.pets["name" = "sprinkles"].type 189.88K ops/s 182.88K ops/s +3.8%
core/player/src/binding/__tests__/parser.bench.ts > binding creation benchmarks > Resolving binding: foo.pets["isDog" = false].type 234.95K ops/s 223.78K ops/s +5.0%
core/player/src/binding/__tests__/parser.bench.ts > binding creation benchmarks > Resolving binding: foo.pets["isDog" = true].type 230.72K ops/s 237.78K ops/s -3.0%
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = 1 + 3 (sync) 437.24K ops/s 479.93K ops/s -8.9%
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = 1 + 3 (async) 348.49K ops/s 383.55K ops/s -9.1%
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: conditional(true, true, false) (sync) 485.77K ops/s 491.89K ops/s -1.2%
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: conditional(true, true, false) (async) 357.84K ops/s 448.21K ops/s -20.2% ⚠️
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = conditional({{bar}} > 0, true, false) (sync) 208.66K ops/s 225.48K ops/s -7.5%
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = conditional({{bar}} > 0, true, false) (async) 187.47K ops/s 201.47K ops/s -6.9%
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = conditional(conditional(true = false, false, true), conditional(false = false, true, false), conditional(true = true, false, true)) (sync) 149.66K ops/s 170.13K ops/s -12.0% ⚠️
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = conditional(conditional(true = false, false, true), conditional(false = false, true, false), conditional(true = true, false, true)) (async) 141.79K ops/s 161.73K ops/s -12.3% ⚠️
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = await(asyncTestFunction(1)) (sync) N/A N/A N/A
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = await(asyncTestFunction(1)) (async) 260.79K ops/s 301.22K ops/s -13.4% ⚠️
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = asyncTestFunction(1) (sync) 310.50K ops/s 345.46K ops/s -10.1% ⚠️
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = asyncTestFunction(1) (async) 268.59K ops/s 306.73K ops/s -12.4% ⚠️
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: asyncTestFunction(1) (sync) 897.12K ops/s 840.98K ops/s +6.7% ✅
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: asyncTestFunction(1) (async) 697.19K ops/s 645.43K ops/s +8.0% ✅
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = conditional(!{{bar}} == false, await(asyncTestFunction(1)), false) (sync) 177.39K ops/s 169.06K ops/s +4.9%
core/player/src/expressions/__tests__/performance.bench.ts > Expression Parsing/Execution Benchmark > Parsing: {{foo}} = conditional(!{{bar}} == false, await(asyncTestFunction(1)), false) (async) 161.77K ops/s 143.18K ops/s +13.0% ✅
core/player/src/view/resolver/__tests__/index.bench.ts > resolver benchmarks > initial resolve 650.60 ops/s 653.82 ops/s -0.5%
core/player/src/view/resolver/__tests__/index.bench.ts > resolver benchmarks > Resolving from cache 15.97K ops/s 21.18K ops/s -24.6% ⚠️
core/player/src/view/resolver/__tests__/index.bench.ts > resolver benchmarks > data changes 2.91K ops/s 3.04K ops/s -4.2%
core/player/src/view/resolver/__tests__/index.bench.ts > resolver benchmarks > data changes slow 603.33 ops/s 655.63 ops/s -8.0%

plugins/async-node/core ⚠️

Benchmark Current Baseline Change
plugins/async-node/core/src/__tests__/index.bench.ts > async node benchmarks > Resolve Async Node 1 times 14.49K ops/s 14.39K ops/s +0.7%
plugins/async-node/core/src/__tests__/index.bench.ts > async node benchmarks > Resolve Async Node 5 times 13.83K ops/s 14.03K ops/s -1.4%
plugins/async-node/core/src/__tests__/index.bench.ts > async node benchmarks > Resolve Async Node 10 times 10.36K ops/s 10.25K ops/s +1.1%
plugins/async-node/core/src/__tests__/index.bench.ts > async node benchmarks > Resolve Async Node 50 times 3.41K ops/s 3.47K ops/s -1.8%
plugins/async-node/core/src/__tests__/index.bench.ts > async node benchmarks > Resolve Async Node 100 times 1.69K ops/s 1.85K ops/s -8.3%
plugins/async-node/core/src/__tests__/transform.bench.ts > async transform benchmarks > Resolve Async Node 1 times 6.74K ops/s 7.71K ops/s -12.6% ⚠️
plugins/async-node/core/src/__tests__/transform.bench.ts > async transform benchmarks > Resolve Async Node 5 times 7.47K ops/s 8.80K ops/s -15.1% ⚠️
plugins/async-node/core/src/__tests__/transform.bench.ts > async transform benchmarks > Resolve Async Node 10 times 7.16K ops/s 7.61K ops/s -5.9%
plugins/async-node/core/src/__tests__/transform.bench.ts > async transform benchmarks > Resolve Async Node 50 times 2.69K ops/s 2.85K ops/s -5.7%
plugins/async-node/core/src/__tests__/transform.bench.ts > async transform benchmarks > Resolve Async Node 100 times 1.63K ops/s 1.66K ops/s -2.0%

react/player

Benchmark Current Baseline Change
react/player/src/asset/__tests__/index.bench.tsx > ReactAsset benchmarks > Render asset nested in 1 ReactAssets 574.78 ops/s 628.93 ops/s -8.6%
react/player/src/asset/__tests__/index.bench.tsx > ReactAsset benchmarks > Bubble errors nested in 1 ReactAssets 1.09K ops/s 1.13K ops/s -2.8%
react/player/src/asset/__tests__/index.bench.tsx > ReactAsset benchmarks > Render asset nested in 5 ReactAssets 596.86 ops/s 629.37 ops/s -5.2%
react/player/src/asset/__tests__/index.bench.tsx > ReactAsset benchmarks > Bubble errors nested in 5 ReactAssets 1.09K ops/s 1.13K ops/s -3.9%
react/player/src/asset/__tests__/index.bench.tsx > ReactAsset benchmarks > Render asset nested in 10 ReactAssets 636.47 ops/s 653.51 ops/s -2.6%
react/player/src/asset/__tests__/index.bench.tsx > ReactAsset benchmarks > Bubble errors nested in 10 ReactAssets 924.74 ops/s 936.46 ops/s -1.3%
react/player/src/asset/__tests__/index.bench.tsx > ReactAsset benchmarks > Render asset nested in 50 ReactAssets 509.64 ops/s 531.93 ops/s -4.2%
react/player/src/asset/__tests__/index.bench.tsx > ReactAsset benchmarks > Bubble errors nested in 50 ReactAssets 248.95 ops/s 268.09 ops/s -7.1%
react/player/src/asset/__tests__/index.bench.tsx > ReactAsset benchmarks > Render asset nested in 100 ReactAssets 412.62 ops/s 403.76 ops/s +2.2%
react/player/src/asset/__tests__/index.bench.tsx > ReactAsset benchmarks > Bubble errors nested in 100 ReactAssets 119.55 ops/s 126.76 ops/s -5.7%

@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (0261225) to head (1e88836).

Additional details and impacted files
@@     Coverage Diff     @@
##   main   #928   +/-   ##
===========================
===========================

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread jvm/testutils/src/main/kotlin/com/intuit/playerui/utils/test/TestMocks.kt Outdated
)
},
"trackRenderTime" to true,
"trackUpdateTime" to true,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are either of these configs we want to actually make configurable? I see the web wrapper doesn't, yeah?

@cehan-Chloe cehan-Chloe Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah. there is an imparity.
Web: configurable and default to true for both render and update
iOS: configurable and default to true for render only. Default false from core for update
Android: hardcoded true. not configurable.
I can change Android to be configurable and default to true for both just like Web. What do you think?
updated in ddb4f37

<Fragment slot='android'>

The `JVM` Metrics plugin can track render time for views in a flow.
The `JVM` Metrics plugin tracks render time and update time for views in a flow (both always enabled, like the web wrapper). Note that on JVM `renderEnd()` is called by the host rather than automatically, so an update timing only completes when the host calls `renderEnd()` after that update paints — which streaming integrations do; non-streaming hosts that don't will simply produce no update data (render tracking is unaffected).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Shouldn't we just call renderEnd in PlayerFragment here?

That'd be invoked for every asset render update.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

updated in commit
I tested it with Android demoapp action-counter by adding renderEnd logs for metricsPlugin. Not sure is there are other good ways to add tests for PlayerFragment onHydrationComplete hook to call the renderEnd 🤔 Any suggestions?

@cehan-Chloe
cehan-Chloe requested a review from sugarmanz August 6, 2026 15:47
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