Repository navigation
[location] Gate Android puck pulsing on layer visibility and lifecycle - #2739
Open
ajtrasatti wants to merge 2 commits into
Open
ajtrasatti wants to merge 2 commits into
ajtrasatti wants to merge 2 commits into
Conversation
ajtrasatti
marked this pull request as ready for review
October 31, 2026 14:00
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
I noticed high CPU usage with location pulsing enabled, including after turning device location off. A native-only reproduction isolates a lifecycle defect: enabling
pulsingEnabledbefore the first location update starts the pulse animator even though the native puck layer is hidden. The animator continues changing the hidden layer's emphasis-circle radius and color, sustaining map rendering without a visible puck.Turning device location off does not necessarily clear the last location or hide the native layer. The reproduction below removes that ambiguity by using a custom provider that emits no locations.
Resulting behavior
Run the pulse only when the native puck layer is visible and the location component is started. Preserve the caller's requested pulse setting so pulsing resumes when those conditions are met. A visible, active puck keeps its existing pulse appearance and cadence.
In our app, disabling pulsing is the immediate mitigation. This PR contributes the lifecycle fix so hidden pucks do not need that workaround.
Implementation
Scope
This change handles native hidden/stopped states, including before the first location update. Native layer visibility is distinct from viewport culling: a puck outside the camera viewport can still have a visible native layer. Offscreen-culling behavior is outside this fix.
Visible pulsing continues to generate render work and can prevent map idle. This PR preserves that behavior and makes no public API or pulse-design changes.
Companion iOS wrapper fix: rnmapbox/maps#4431 addresses rnmapbox/maps#3787 by removing the hidden puck renderer while preserving camera following. The two PRs address the hidden-rendering paths at their respective ownership layers.
Reproduction and validation
Reproduced on unmodified public Android SDK 11.34.1 using a native app, blank style, fixed camera, and a custom location provider that emits no locations. The reproduction requires no React Native code, tile sources, markers, or incoming location updates.
The patched native probe uses the location module compiled from this branch with the public 11.34.1 engine on an API 34 ARM64 emulator:
Automated checks: 95 tests pass across the five affected test classes in an isolated Android library build of the same modified source/test files against public SDK 11.34.1, with Robolectric 4.14 and MockK 1.13.7. Removing the lifecycle/renderer/listener fixes causes regression failures; restoring them passes. The stopped-settings test also fails when the outer plugin's lifecycle guard is removed. Upstream
:plugin-locationcomponent:ktlintpasses.Validation limit: the full upstream
:plugin-locationcomponent:testDebugUnitTesttask cannot resolve private 11.35 snapshot core/common dependencies without registry credentials (HTTP 401). The isolated public-release test run is not a full upstream build or test pass. Physical-device energy measurements remain outside this validation.Native recordings and logs are retained locally. The CLI refused video attachment because it requires upstream repository write access; no recording is attached to this PR.
Pull request checklist