Skip to content

fix: improve Android marker viewport performance - #28

Merged
piotr-graczyk-dev merged 3 commits into
mainfrom
codex/android-map-performance
Jun 28, 2026
Merged

fix: improve Android marker viewport performance#28
piotr-graczyk-dev merged 3 commits into
mainfrom
codex/android-map-performance

Conversation

@piotr-graczyk-dev

Copy link
Copy Markdown
Contributor

Summary

  • reduce Android Google Maps marker churn during camera movement
  • add render-version diffing for retained markers and clusters
  • keep lightweight live viewport refreshes while deferring heavier animated updates

Validation

  • git diff --check
  • bun run typecheck
  • ./gradlew :app:assembleDebug
  • agent-device Android Pixel 9 Pro XL: Advanced features / 10k markers, ping-pong pan frame sample

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved marker entering animations for large sets by limiting how many markers animate per update, revealing the remainder immediately.
    • Refined camera-driven marker refresh behavior with debounced idle updates and throttled live updates for smoother performance.
    • Improved consistency of retained markers across refreshes to reduce jitter and unnecessary marker churn.
  • Documentation
    • Updated guidance to be provider-agnostic for Google Maps SDKs on marker entering animations and reiterated recommended mitigation for large marker sets (e.g., clustering/shorter durations/disable animations).

Walkthrough

Adds render-version tracking for markers and clusters, uses it in Android viewport diffing, caps entering animations per refresh, refactors camera-triggered refresh scheduling, and updates docs wording to remove iOS-specific framing.

Changes

Android marker diff and animation budget

Layer / File(s) Summary
ClusterElement renderVersion contract and implementations
package/android/src/main/java/com/margelo/nitro/nitromaps/MarkerClusterEngine.kt
ClusterElement gains renderVersion; Single and Cluster compute stable hash-based versions from marker or cluster state.
Version-based viewport diffing and animation-capped applyDiff
package/android/src/main/java/com/margelo/nitro/nitromaps/MapOverlayController.kt
refreshViewportMarkers snapshots versions and classifies retained vs added markers by renderVersion; applyDiff updates version state and limits entering animations per diff.
Camera-driven refresh debounce/throttle refactor
package/android/src/main/java/com/margelo/nitro/nitromaps/MapOverlayController.kt
clear() cancels pending refresh runnables and clears version state; camera idle and move handlers now schedule debounced or throttled viewport refreshes through runnable helpers.

Docs generalization

Layer / File(s) Summary
README and architecture doc wording
README.md, docs/architecture.md
Both docs replace iOS-specific marker animation guidance with Google Maps provider wording.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • gmi-software/react-native-nitro-maps#26: Introduces the native marker entering-animation behavior that this PR extends with render-version tracking, animation capping, and refresh scheduling changes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, includes a fix prefix, and accurately summarizes the Android marker performance work.
Description check ✅ Passed The description matches the changes by describing marker churn reduction, render-version diffing, and live refresh behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@package/android/src/main/java/com/margelo/nitro/nitromaps/MapOverlayController.kt`:
- Around line 385-388: The applyMarkersSync() function clears markerVersions and
clusterByKey but fails to invalidate in-flight viewport computations, allowing
stale diffs to be applied after sync reconciliation. After the existing clear()
calls in applyMarkersSync(), bump the refreshGeneration counter and/or cancel
any scheduled viewport refreshes to ensure that previously queued viewport
computations cannot pass their generation check and apply obsolete diffs after
the sync operation completes.
- Around line 174-181: The retained-marker update path in MapOverlayController’s
retained-element handling is missing styling refreshes, so non-positional marker
option changes are ignored. Update the retained update logic to reapply all
marker styling/options, not just position/title/snippet/draggable, and make sure
the marker rebuild path in toMarkerOptions() does not hard-code the default red
icon when a marker’s icon or other visual options have changed. Use the existing
renderVersion and displayedVersions flow to decide when a full rebuild is needed
versus when retained markers can be updated in place.

In
`@package/android/src/main/java/com/margelo/nitro/nitromaps/MarkerClusterEngine.kt`:
- Line 13: The equality gate in MarkerClusterEngine is too weak because
renderVersion is an Int and can collide, causing stale overlays to be retained.
Update the render signature contract in MarkerClusterEngine to use a
collision-resistant structural signature object, or at minimum switch
renderVersion and the related markerVersions storage and comparisons to Long.
Make the same change consistently across the affected MarkerClusterEngine
methods and any implementations that read, compute, or compare renderVersion so
equality checks remain reliable.
- Around line 38-45: The renderVersion getter in MarkerClusterEngine.Cluster is
doing work on every read by sorting memberIds, which is causing avoidable
UI-thread churn when applyDiff stores versions. Move the version/hash
computation out of the getter and compute it once when the Cluster instance is
created, then have renderVersion return the cached value; use Cluster,
renderVersion, and applyDiff to locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ab9b0bb-01a6-4d4d-843c-02445a098a28

📥 Commits

Reviewing files that changed from the base of the PR and between f056c1e and 2bd9eed.

📒 Files selected for processing (4)
  • README.md
  • docs/architecture.md
  • package/android/src/main/java/com/margelo/nitro/nitromaps/MapOverlayController.kt
  • package/android/src/main/java/com/margelo/nitro/nitromaps/MarkerClusterEngine.kt

Comment thread package/android/src/main/java/com/margelo/nitro/nitromaps/MarkerClusterEngine.kt Outdated
Comment thread package/android/src/main/java/com/margelo/nitro/nitromaps/MarkerClusterEngine.kt Outdated

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
package/android/src/main/java/com/margelo/nitro/nitromaps/MapOverlayController.kt (1)

489-498: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Stop resetting the live-refresh throttle in cancellation
cancelLiveRefresh() should only cancel the pending runnable. Clearing lastLiveRefreshMs there makes every idle→move turnaround look like a fresh session, so the next onCameraMove() fires immediately and defeats LIVE_REFRESH_THROTTLE_MS. Reset the baseline only on true full resets like clear() or applyMarkersSync().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@package/android/src/main/java/com/margelo/nitro/nitromaps/MapOverlayController.kt`
around lines 489 - 498, `cancelLiveRefresh()` is resetting the live-refresh
throttle state in addition to canceling the pending runnable, which makes the
next `onCameraMove()` bypass `LIVE_REFRESH_THROTTLE_MS`. Update
`MapOverlayController.cancelLiveRefresh()` so it only removes
`liveRefreshRunnable` and clears the reference, and move any `lastLiveRefreshMs`
reset to full reset paths like `clear()` or `applyMarkersSync()` where a fresh
session should actually start.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@package/android/src/main/java/com/margelo/nitro/nitromaps/MapOverlayController.kt`:
- Around line 489-498: `cancelLiveRefresh()` is resetting the live-refresh
throttle state in addition to canceling the pending runnable, which makes the
next `onCameraMove()` bypass `LIVE_REFRESH_THROTTLE_MS`. Update
`MapOverlayController.cancelLiveRefresh()` so it only removes
`liveRefreshRunnable` and clears the reference, and move any `lastLiveRefreshMs`
reset to full reset paths like `clear()` or `applyMarkersSync()` where a fresh
session should actually start.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 211c620f-42c9-43bc-8d48-a66d2f0eaf3a

📥 Commits

Reviewing files that changed from the base of the PR and between 6042079 and 9e289d6.

📒 Files selected for processing (1)
  • package/android/src/main/java/com/margelo/nitro/nitromaps/MapOverlayController.kt

@piotr-graczyk-dev
piotr-graczyk-dev merged commit c6ac7f8 into main Jun 28, 2026
3 checks passed
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.

1 participant