问题描述
我们在用户群中发现,自上次 google fit 应用更新以来,数据急剧下降,自开始以来,我们一直试图找出代码中的问题。给出时间,我们认为我们使用的版本(当时是 18.0)是问题所在。 升级到 SDK 20.0 并没有改善结果,但阻止了数据停滞。目前我们可以假设 50-60% 通过 SDK 连接到 google fit 的用户不再根据(以前工作的)实现正确检索数据。它们并没有丢失,它们仍然到处发送一些比特,但不再是以前的样子。
这张图表展示了事件的时间线,这些事件让我们得出结论,一方一定做错了什么。
为了可读性,下面的代码示例已经去除了大部分数据处理代码,但它仍然存在。
我们的 fitness 客户端每次在前台或后台初始化时,都会为下面提到的所有类型以及其他取决于应用的类型请求 fitnessOptions.ACCESS_READ
,确保我们只请求用户接受的那些类型。
我们可以确认下一个数据类型在请求每日总计或本地设备每日总计时不再返回任何值,但在非聚合读取中请求时会返回同一时期的数据块:
DataType.TYPE_STEP_COUNT_DELTA
DataType.TYPE_CALORIES_EXPENDED
DataType.TYPE_HEART_RATE_BPM
我们还尝试将那些可能的更改为它们的聚合对应项,但无济于事:
DataType.AGGREGATE_CALORIES_EXPENDED
DataType.AGGREGATE_STEP_COUNT_DELTA
这是我们当前的 getDailyTotal 实现,在更新之前工作,and is written straight out as the examples on the developer site show:
fitness.getHistoryClient(context,account)
.readDailyTotal(type)
.addOnSuccessListener {
Logger.i("${type.name}::DailyTotal::Success")
onResponse(it)
}
无论在一天中的哪个时间被询问,当前都返回 0。
然后我们有我们的补充代码,它模拟了 getDailyTotal 在内部所做的事情,也根据开发者网站的例子: 来自:天开始于 00:00:00,UTC+1 至:一天结束时间为 23:59:59,UTC+1 类型:任何数据类型。
val readRequest = DataReadRequest.Builder()
.enableServerQueries()
.aggregate(type)
.bucketByTime(1,TimeUnit.DAYS)
.setTimeRange(from.time,to.time,TimeUnit.MILLISECONDS)
.build()
val account = GoogleSignIn
.getAccountForExtension(context,fitnessOptions!!)
GFitClient.request(context,account,readRequest) {
if (it == null) {
aggregatedRequestError(type)
} else {
Logger.i(TAG,"Aggregated ${type.name} received.")
}
}
这里的常见结果是 1) 空结果或空结果,2) 实际得到结果(在 DataType.TYPE_STEP_COUNT_DELTA
有时 的情况下,它会发生) 或 3) APIException code 5012,this datatype can't be aggregated.
我们一直在使用单个聚合,因为可以由 (type,type.aggregate)
调用的双聚合已被弃用,因为一些版本已经过时了,尽管一些开发者站点示例仍在使用它。
使用(或不使用).enableServerQueries()
不会修改最终结果。
最后,我们假设最坏的情况,无论如何我们都会要求当天的任何内容,然后我们手动聚合。这通常会报告结果,而其他人则没有。遗憾的是,这些结果从来都不足以让人感到舒服。
val readRequest = DataReadRequest.Builder()
.enableServerQueries()
.read(type)
.bucketByTime(1,TimeUnit.DAYS)
.setTimeRange(from.time,TimeUnit.MILLISECONDS)
.build()
val account = GoogleSignIn
.getAccountForExtension(context,fitnessOptions!!)
这往往可行,但鉴于数据集、存储桶和整体数据集结构的复杂嵌套性质,数据的手动处理很复杂。
我们也注意到了在获取 Fit 应用程序上清晰可见但 SDK 上没有出现的数据时存在的问题,例如,华为健康活动出现在应用程序上,而 SDK 仅返回其中的一个子集,以及反过来,SDK 会向我们返回数据(例如,一整晚的睡眠会话(轻度、快速、深度...),而 Fit 应用程序显示与没有任何会话的单个 Sleep 块相同的睡眠。
在第三方应用中显示的睡眠会话,使用 SDK 返回给我们的相同数据:
Google 健身应用中显示的相同睡眠时段:
就文档而言:
对于 Android API,按数据类型读取,Fit 平台将 默认返回合并的流。这会自动包括所有 您的应用程序可用的数据,包括其他应用程序写入的数据。你 将无法看到数据来自哪些应用程序或设备的列表 来自 Android API。
我们认为合并的流行为不正常,不是实时的(这可以通过应用程序直接从后端显示数据与 SDK 尚未写入数据之间的延迟来解释),但也不是在几分钟或几小时的差异内,有时从未出现。
为了理解我们如何检索这些数据,我们有一个背景 WorkerManager CouroutineJob
每隔一段时间(当系统允许时,给予打盹模式权限,但我们更喜欢什么(并通过 WorkerManager 配置询问) ) 是每小时或几个小时一次,为了使数据与健身应用程序中显示的数据保持同步),我们请求从上次更新到最后一天结束的数据或/和我们请求今天的每日总数(或最多当前时间,取决于我们“不工作”漏斗的距离,以及上次更新的日期)。
- 我们的实施有什么问题吗?
- google fit 是否改变了向关联应用报告数据的方式?
- 我们能否以某种方式获得更真实的数据?
- 有什么方法可以更有效地以不同方式请求相同的数据?我们最感兴趣的是获取每日摘要、总数和平均值,而不是时间段/会话。我们同时请求两者,但它们会进入涵盖不同用例的不同数据渠道。
解决方法
没有答案,只有沉默。 Firebase 团队的出色支持。
我们的解决方案最终会进行一系列粗暴的数据检查,并且在每次失败时我们都会尝试不同的方法。