Google 自从 Google fit 应用程序更新以来,Fit 数据模式发生变化,实施显然已中断
Google Fit data pattern change since Google fit App update, implementation apparently broken
我们已经在我们的用户群中发现,自上次 google 健身应用程序更新以来,数据出现了急剧下降,并且从它开始我们就试图在我们的代码中找出问题。给出时间,我们认为我们使用的版本(当时是 18.0)是问题所在。
升级到 SDK 20.0 并没有改善结果,但阻止了数据停滞。目前我们可以假设 50-60% 的用户通过 SDK 连接到 google fit 不再根据(以前工作的)实现正确地检索数据。他们没有丢失,他们仍然在这里和那里发送一些比特,但它不再像以前那样了。
此图显示了事件的时间表,这些事件使我们得出结论,认为其中一方一定做错了什么。
为了便于阅读,下面的代码示例已删除了大部分数据处理代码,但它仍然存在。
我们的 Fitness 客户端请求 FitnessOptions.ACCESS_READ
下面提到的所有类型,加上其他取决于应用程序的类型,每次在前台或后台初始化时,确保我们只请求用户接受的类型。
我们可以确认下一个数据类型不再是return请求每日总计或本地设备每日总计时的任何值,而是return非请求时的同期数据块汇总阅读:
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)
}
当前 returns 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) null 或空结果,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, to.time, TimeUnit.MILLISECONDS)
.build()
val account = GoogleSignIn
.getAccountForExtension(context, fitnessOptions!!)
这往往可行,但考虑到数据集、存储桶和整体数据集结构的复杂嵌套性质,手动处理数据很复杂。
我们还注意到在检索数据时出现的问题,这些数据在 fit 应用程序上可以清楚地看到,但不会出现在 SDK 上,例如,华为健康活动出现在应用程序上,而 SDK returns只有其中的一部分,反之亦然,SDK return 向我们发送数据(例如,一整晚的睡眠时间(轻度、快速、深度...),而健身应用程序显示与没有任何会话的单个睡眠块相同的睡眠。
第三方应用程序中显示的睡眠会话,使用 SDK return 提供给我们的相同数据:
Google 健身应用程序中显示的相同睡眠会话:
据文档所述:
For the Android APIs, read by data type and the Fit platform will
return the merged stream by default. This automatically includes all
data available to your app, including data written by other apps. You
won't be able to see a list of which apps or devices the data came
from with the Android APIs.
我们认为合并流的行为不正常,不是实时的(这可以解释为应用程序直接从后端显示数据和 SDK 尚未写入数据之间存在延迟),但是也不是几分钟或几小时的差异,有时永远不会出现。
为了了解我们如何检索这些数据,我们有一个背景 WorkerManager CouroutineJob
每隔一段时间(当系统允许时,给定休眠模式权限,但我们更喜欢什么(并通过以下方式询问) WorkerManager 配置)是每小时或几个小时一次,以使数据与健身应用程序中显示的数据保持同步),我们请求从上次更新到最后一天结束日期的数据 or/and 我们请求今天的每日总数(或截至当前时间,取决于我们走了多远的“不起作用”漏斗,以及上次更新的日期)。
- 我们的实现有什么问题吗?
- google fit 是否改变了它向连接的应用程序报告数据的方式?
- 我们能否以某种方式获得更真实的数据?
- 有没有什么方法可以更有效地以不同方式请求相同的数据?我们最感兴趣的是获取每日摘要、总计和平均值,而不是时间段/会话。我们同时请求两者,但它们会进入涵盖不同用例的不同数据渠道。
还没有答案。
我们的解决方案最终对数据进行了一系列粗暴的检查,并且在每次失败时我们都会尝试不同的方法。
我们已经在我们的用户群中发现,自上次 google 健身应用程序更新以来,数据出现了急剧下降,并且从它开始我们就试图在我们的代码中找出问题。给出时间,我们认为我们使用的版本(当时是 18.0)是问题所在。 升级到 SDK 20.0 并没有改善结果,但阻止了数据停滞。目前我们可以假设 50-60% 的用户通过 SDK 连接到 google fit 不再根据(以前工作的)实现正确地检索数据。他们没有丢失,他们仍然在这里和那里发送一些比特,但它不再像以前那样了。
此图显示了事件的时间表,这些事件使我们得出结论,认为其中一方一定做错了什么。
为了便于阅读,下面的代码示例已删除了大部分数据处理代码,但它仍然存在。
我们的 Fitness 客户端请求 FitnessOptions.ACCESS_READ
下面提到的所有类型,加上其他取决于应用程序的类型,每次在前台或后台初始化时,确保我们只请求用户接受的类型。
我们可以确认下一个数据类型不再是return请求每日总计或本地设备每日总计时的任何值,而是return非请求时的同期数据块汇总阅读:
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)
}
当前 returns 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) null 或空结果,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, to.time, TimeUnit.MILLISECONDS)
.build()
val account = GoogleSignIn
.getAccountForExtension(context, fitnessOptions!!)
这往往可行,但考虑到数据集、存储桶和整体数据集结构的复杂嵌套性质,手动处理数据很复杂。
我们还注意到在检索数据时出现的问题,这些数据在 fit 应用程序上可以清楚地看到,但不会出现在 SDK 上,例如,华为健康活动出现在应用程序上,而 SDK returns只有其中的一部分,反之亦然,SDK return 向我们发送数据(例如,一整晚的睡眠时间(轻度、快速、深度...),而健身应用程序显示与没有任何会话的单个睡眠块相同的睡眠。
第三方应用程序中显示的睡眠会话,使用 SDK return 提供给我们的相同数据:
Google 健身应用程序中显示的相同睡眠会话:
据文档所述:
For the Android APIs, read by data type and the Fit platform will return the merged stream by default. This automatically includes all data available to your app, including data written by other apps. You won't be able to see a list of which apps or devices the data came from with the Android APIs.
我们认为合并流的行为不正常,不是实时的(这可以解释为应用程序直接从后端显示数据和 SDK 尚未写入数据之间存在延迟),但是也不是几分钟或几小时的差异,有时永远不会出现。
为了了解我们如何检索这些数据,我们有一个背景 WorkerManager CouroutineJob
每隔一段时间(当系统允许时,给定休眠模式权限,但我们更喜欢什么(并通过以下方式询问) WorkerManager 配置)是每小时或几个小时一次,以使数据与健身应用程序中显示的数据保持同步),我们请求从上次更新到最后一天结束日期的数据 or/and 我们请求今天的每日总数(或截至当前时间,取决于我们走了多远的“不起作用”漏斗,以及上次更新的日期)。
- 我们的实现有什么问题吗?
- google fit 是否改变了它向连接的应用程序报告数据的方式?
- 我们能否以某种方式获得更真实的数据?
- 有没有什么方法可以更有效地以不同方式请求相同的数据?我们最感兴趣的是获取每日摘要、总计和平均值,而不是时间段/会话。我们同时请求两者,但它们会进入涵盖不同用例的不同数据渠道。
还没有答案。
我们的解决方案最终对数据进行了一系列粗暴的检查,并且在每次失败时我们都会尝试不同的方法。