public final class AggregateQuery
- Object
- AggregateQuery
Describes a bucketed summary read against HealthStore.
AggregateQuery q = new AggregateQuery()
.addType(HealthDataType.STEPS)
.addMetric(AggregateMetric.TOTAL)
.setTimeRange(HealthTimeRange.calendarDays(7, ZoneId.systemDefault()))
.setBucket(HealthInterval.calendarDays(1, ZoneId.systemDefault()));
Overlapping sources are counted twice, on every platform
When a phone and a watch both record steps for the same walk, the store holds two overlapping sets of samples, and a total over them counts the walk twice.
This includes iOS. HealthKit’s statistics engine does de-duplicate overlapping sources – but no port uses it in this release. Every metric here is computed by shared code from raw samples read back through an ordinary query, so that the bucket arithmetic has one implementation rather than one per platform that can drift. iOS therefore double-counts exactly as Android does. A port that grows a native aggregate path would change that, and this note with it.
This API does not paper over the overlap with a heuristic
de-duplicator – guessing which of two overlapping sources is
authoritative is exactly the kind of silent wrongness health data
cannot afford. Use addSource(String) to pin the query to the source
you trust, and tell the user which device a figure came from.
Constructors
public AggregateQuery() |
Methods
Inherited methods
Constructor details
AggregateQuery
public AggregateQuery()Method details
addType
public AggregateQuery addType(HealthDataType type)getTypes
public List<HealthDataType> getTypes()addMetric
public AggregateQuery addMetric(AggregateMetric metric)getMetrics
public List<AggregateMetric> getMetrics()setDeduplicateSources
public AggregateQuery setDeduplicateSources(boolean deduplicate)Restricts the summary to one writing app – see the double-counting warning on this class. Asks the platform to de-duplicate overlapping sources.
Off by default, because the shared rollup cannot do it and most
stores therefore cannot honour it. Check
HealthStore.isSourceDeduplicationSupported() first: asking a store
that cannot fails the aggregate with HealthError.NOT_SUPPORTED
rather than silently returning the double-counted answer.
Where it is honoured – HealthKit, whose statistics engine does this natively – a walk recorded by both a phone and a watch is counted once instead of twice.
isDeduplicateSources
public boolean isDeduplicateSources()addSource
public AggregateQuery addSource(String bundleId)getSources
public List<String> getSources()setTimeRange
public AggregateQuery setTimeRange(HealthTimeRange timeRange)getTimeRange
public HealthTimeRange getTimeRange()setBucket
public AggregateQuery setBucket(HealthInterval bucket)Splits the range into buckets of this width. Leave unset for a single bucket covering the whole range.
Prefer HealthInterval.calendarDays(int,java.time.ZoneId) over a
fixed 24-hour width whenever the buckets are labelled with dates in
your UI – see HealthInterval.
getBucket
public HealthInterval getBucket()setUnit
public AggregateQuery setUnit(HealthUnit unit)unit rather than each type’s
canonical unit.getUnit
public HealthUnit getUnit()validate
public void validate()
throws HealthExceptionThrows
HealthExceptionHealthError.INVALID_ARGUMENTfor a missing type, metric or range, or for a metric that is meaningless for the requested type;HealthError.UNIT_MISMATCHfor an incompatible unit.
isMeaningful
public static boolean isMeaningful(HealthDataType type, AggregateMetric metric)Whether metric says anything true about type.
AggregateMetric.COUNT and AggregateMetric.DURATION apply to
everything. Summing a discrete series – the total of every body
mass ever recorded – and averaging a cumulative one – the mean of
arbitrarily-chunked step totals – are both meaningless, so they
are rejected rather than answered.