fix: address top Crashlytics crashes and non-fatals for build 29320984#5684
Merged
Conversation
Address two Crashlytics crashes affecting build 29320984:
1. ComposeUiClusterRenderer.renderViewToBitmapDescriptor (1810 events, 970 users)
- IllegalStateException: ViewTreeLifecycleOwner not propagated
- Added lifecycle state guard to skip rendering when lifecycle < STARTED,
preventing the async MarkerModifier Handler from racing with lifecycle stops
2. renderComposableToBitmapDescriptor in InlineMap (92 events, 43 users)
- IllegalStateException: ComposeView measured to zero width/height
- Added ViewTreeLifecycleOwner propagation workaround (same pattern as
NodeClusterMarkers) so the internal ComposeView can find the lifecycle
- Added explicit defaultMinSize on marker content as safety net
Both crashes stem from android-maps-compose's internal ComposeView bitmap
rendering pipeline, which requires an active lifecycle owner that may not
be available during async marker rendering or on detached views.
Fixes: googlemaps/android-maps-compose#858, googlemaps/android-maps-compose#875
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…cording Kable logs expected BLE operational events (connection failures, disconnect requests) at error level. Our KermitLogEngine bridges these to Kermit, and the CrashlyticsLogWriter records any Error-level log with a throwable as a non-fatal exception via recordException(). This caused 26K+ spurious non-fatal events per week (NotConnectedException, connection timeouts) that are normal BLE lifecycle events, not app bugs. Fix: downgrade KermitLogEngine.error() and .assert() to Logger.w() so Kable internal logs still appear in the Crashlytics log buffer for debugging but don't trigger recordException(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
❌ 1 Tests Failed:
View the top 1 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
This was referenced Jun 1, 2026
Merged
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.
Motivation
Build 29320984 (beta/alpha) shows two categories of Crashlytics issues:
Both degrade signal-to-noise in Crashlytics, making real issues harder to spot.
Approach
Maps clustering crashes (2 issues, ~1900 events, ~1000 users)
The
android-maps-composelibrary creates internalComposeViewinstances to render markers as bitmaps. These views require aViewTreeLifecycleOwnerbut can race with lifecycle transitions when the cluster renderer's async Handler fires after the activity stops.Fixes:
NodeClusterMarkers.kt-- Added lifecycle state guard (isAtLeast(STARTED)) to prevent theClusteringcomposable from rendering when the lifecycle is inactive, avoiding the async raceInlineMap.kt-- Added the sameViewTreeLifecycleOwner/SavedStateRegistryOwnerpropagation workaround that the main map already had, plusdefaultMinSizeon marker content to prevent zero-size measurementBLE non-fatal noise (1 issue, 26K events, 1494 users)
Kable (BLE library) logs expected operational events like failed connections and disconnect requests at error level. Our
KermitLogEnginebridge routes these toLogger.e(), and theCrashlyticsLogWriterrecords any Error+throwable as a non-fatal viarecordException().Fix:
KermitLogEngine.kt-- Downgradederror()andassert()toLogger.w(). Kable "errors" are normal BLE lifecycle events (connection timeouts, already-disconnected), not app bugs. They still appear in the Crashlytics log buffer for debugging but no longer pollute the non-fatals dashboard.Non-obvious decisions
NodeClusterMarkersuses an early return rather than wrappingClusteringin a conditional block -- this ensures no cluster state is retained across lifecycle transitions that could trigger stale renders.References: googlemaps/android-maps-compose#858, googlemaps/android-maps-compose#875