Stream Health Connect reads page-by-page + optional native downsampling (fixes HR OOM)#18
Merged
Merged
Conversation
…ownsampling Reading a dense heart-rate history previously materialized every raw record plus one map per sample in memory at once, OOM-crashing the app for heavy users. Non-workout, non-sleep types are now converted one page at a time so raw records are dropped as soon as each page is processed, and an optional samplingIntervalMillis argument keeps at most one sample per interval as the data is read. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ssFfNA35kFNFqSP8uCzyx
Interval queries for heart rate previously aggregated MEASUREMENTS_COUNT, which returns sample counts rather than heart rates, making the interval API unusable for HR. Mapping it to BPM_AVG lets callers read dense HR histories as bucketed averages computed inside Health Connect, so raw samples never cross IPC into the app — this is what makes heavy HR reads fast, not just memory-safe. Also in getAggregateHCData: skip buckets with no data instead of fabricating zero values (a 0 BPM sample is not a real data point), and add the same try/catch getHCData already has so provider errors return an empty result instead of crashing the coroutine. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ssFfNA35kFNFqSP8uCzyx
2 tasks
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.
Fixes the production OOM crash and the slowness of reading dense heart-rate histories on Android.
getHCDatapaged all raw records into one list, then converted every HR sample into its ownLinkedHashMap. For a wearable writing a sample every few seconds, that's millions of maps at once on a 256 MB heap — and even without the crash, paging millions of records across IPC is very slow.Changes (Android / Health Connect path only)
1. Page-by-page conversion in
getHCData— non-workout, non-sleep types are converted as each page arrives, so peak memory is bounded by one ~1,000-record page regardless of history size. A new optionalsamplingIntervalMillisargument (Dart:getHealthDataFromTypes(samplingInterval: ...)) keeps at most one converted sample per interval as data is read. Omitted → behavior unchanged.2. Make interval (aggregate) queries usable for heart rate — this is what makes dense HR reads fast, not just safe:
MapToHCAggregateMetric:HEART_RATEnow aggregatesBPM_AVGinstead ofMEASUREMENTS_COUNT(which returned sample counts, not heart rates, so the interval API was unusable for HR).getAggregateHCDataskips empty buckets instead of fabricatingvalue: 0data points (a 0 BPM sample is not real data).getAggregateHCDatagets the same try/catchgetHCDataalready has (better error handeling if hc request comes when app is in background #17), so provider errors return an empty result instead of crashing the coroutine.With this,
getHealthIntervalDataFromTypesreturns one average BPM per bucket computed inside Health Connect — raw samples never cross IPC. A 30-day window at 5-min buckets is ~8.6k values instead of potentially millions of records.Behavior notes
samplingIntervalis optional; raw-read callers are unaffected.getIntervalDataonly supports cumulative types via.cumulativeSum).Companion app change: Arcascope/shiftapp_flutter#1411
🤖 Generated with Claude Code
https://claude.ai/code/session_016ssFfNA35kFNFqSP8uCzyx