跳到主要内容

屏幕时间

本指南介绍如何集成屏幕时间:报告玩家正在使用您的产品、读取判定结果、处理七个屏幕时间 Webhook 事件,以及让玩家可以向父母申请更多时间。

关于屏幕时间是什么、以及 k-ID 与您的产品之间的职责边界在哪里,请先阅读屏幕时间概念

开始之前

  • 确认您的组织已可使用屏幕时间。 如需为您的组织启用此功能,请联系 k-ID。
  • 为您的产品启用该功能。 屏幕时间默认关闭,需要在 Compliance Studio 中按以下顺序打开两个开关:先打开 Screentime,再打开 Screentime Controls,然后发布更改。Controls 依赖于 Screentime,在 Screentime 打开之前无法操作。在某个端点所需的开关打开之前,该端点会以 FEATURE_DISABLED 失败,而两组端点所需的开关并不相同。请参阅功能开关
  • 配置您的 Webhook 端点。 全部七个屏幕时间信号都以 Webhook 形式到达。请在产品的 Developer Settings 中设置 URL 和密钥,并按照 Webhooks 概述的说明验证签名。
  • 订阅您所处理的事件。 端点只会接收它已订阅的事件类型,其余的会被 k-ID 丢弃,既不尝试投递也不报错。请在 Compliance Studio 中打开产品的 Developer Settings 页面,为您的端点选择Screentime.BreakReminderScreentime.LimitWarningScreentime.LimitReachedScreentime.ScheduleChangedScreentime.QuietHoursWarningScreentime.QuietHoursReachedScreentime.OverrideResult。这些项目只有在您的组织启用屏幕时间之后才会列出。
  • 需要有一个拥有 kuid 的玩家会话。 每个屏幕时间调用都面向一个 k-ID sessionId,但其背后的安排是保存在玩家的 kuid 上,而不是保存在会话上。没有 kuid 的会话永远不可能拥有安排,因此无论父母如何配置,GET /screentime/get-state 对它都会返回 { "enabled": false }。玩家在受信任的成年人完成同意之后才会获得 kuid,所以请先走完那个流程,并在后续会话中重复使用已保存的 kuid。请参阅会话与权限挑战

父母未配置安排的玩家不是错误情形。GET /screentime/get-state 会返回 { "enabled": false },不会有任何判定进行拦截,也不会发出事件。请按这种情况来构建:您的大多数玩家都处于这种状态。

报告使用时间

报告使用时间有两种方式,而这个选择并非只是形式上的差别。它决定了您的产品是否能收到实时信号。

进行中的会话:startend事后报告:push
端点/screentime/start/screentime/end/screentime/push
计入每日总量
Screentime.BreakReminder会发出不会发出
Screentime.LimitWarning会发出不会发出
Screentime.LimitReached会发出不会发出
可报告的范围实时,距事件发生 5 分钟内过去 7 天内的任意时点
适用场景您的产品知道玩家何时开始和停止您的产品只能在会话结束后才知道使用时间
如果您需要实时事件,请选择 startend

这是屏幕时间中无法仅凭端点列表推断出来的关键点。通过 /screentime/push 报告的使用时间会计入每日总量,也会出现在父母的 Family Connect 图表中,但它不会发出休息提醒、限制警告或限制到达事件。

原因在于时机。k-ID 在您调用 /screentime/start 时安排提醒和警告,因此它们会在玩家仍处于会话中时发出。一个 90 分钟的会话若在事后报告,60 分钟处的休息提醒只能迟 30 分钟发出,那已经不是休息提醒了。如果您的产品负有休息提醒义务,/screentime/push 无法满足它。

安静时段事件和 Screentime.ScheduleChanged 由父母的日历驱动,而不是由您的会话上报方式驱动,因此两种方式下都会发出。

会话状态机

每个 sessionId 同时只能有一个进行中的屏幕时间会话,以下规则由此而来:

  • id 由您生成。 调用 /screentime/start 时发送一个新的 UUID,调用 /screentime/end 时发送相同的 id。如果 endid 与进行中的会话不匹配,将返回 NOT_FOUND
  • 重试是安全的。 用相同的 id 重复调用 start 会返回 accepted 且不会重复计入。对已结束的会话重复调用 end 同样返回 accepted
  • 使用不同 id 的第二次 start 会返回 status: "replaced" k-ID 会结束上一个会话、计入其时长,并把新会话设为进行中。当应用被强制关闭或设备重启导致 end 从未送达时,这就是您的恢复路径:直接再次调用 start 并检查是否为 replaced。同一位玩家在第二台设备上开始游玩时也会发生同样的事,而那种情况会产生父母能够看到的后果:第一台设备停止统计,也不再收到提醒。请参阅使用时间如何统计
  • k-ID 会在 4 小时后自动结束停滞的会话。 截至该时点的时长会被计入,待发的提醒会被取消。这限制了完全停止发送 end 的产品所造成的影响。它是兜底机制,而不是调用 end 的替代方案。
  • 时间戳必须是实时的。 /screentime/start 会拒绝早于 5 分钟的 timestamp 以及任何未来的时间。/screentime/end 要求时间戳不早于对应 start 的时间戳,并容忍约一分钟的向前时钟偏差。

startend 的响应中都会返回计数器,因此如果只是想在会话开始或结束时向玩家显示剩余时间,您不需要额外调用 GET /screentime/get-state

{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"id": "3f8c1d24-6b5e-4a07-9c2f-8d1e7a4b6c90",
"status": "accepted",
"screentime": {
"counter": {
"timeUsedTodayMinutes": 45,
"timeLimitTodayMinutes": 120,
"timeRemainingTodayMinutes": 75,
"dayResetsAt": "2026-06-25T00:00:00Z"
}
}
}

事后报告

/screentime/push 一次调用最多接收 100 个已完成的片段。每个片段都带有用于幂等的 id、这段使用开始时的 timestamp 以及 durationSeconds

片段是独立处理的,因此请读取每个事件的状态,而不要假设整批都已成功:

{
"sessionId": "b1a6482d-5242-4b4a-aa88-3fa52595a672",
"accepted": 1,
"rejected": 1,
"events": [
{ "id": "9d4e2f81-7a3b-4c56-8e90-1f2a3b4c5d6e", "status": "accepted" },
{ "id": "c1b2a3d4-5e6f-4708-9a1b-2c3d4e5f6071", "status": "rejected", "reason": "invalid_input" }
]
}

reasoninvalid_input 表示该片段违反了校验规则,原样重试仍会失败:片段必须已经结束,其 timestamp 必须在过去 7 天内,且 durationSeconds 必须在 1 秒到 24 小时之间。reasoninternal_error 属于临时性问题,可以安全重试。重新提交已被接受的 id 会再次记为 accepted 且不会重复计入。

读取状态

GET /screentime/get-state 是规则与当前判定结果的唯一可信来源。请在会话开始时调用它,并在任何改变局面的事件之后再次调用。

{
"enabled": true,
"access": {
"allowed": false,
"details": {
"reason": "quiet_hours",
"resumesAt": "2026-06-25T07:00:00+08:00"
}
},
"state": {
"timeUsedTodayMinutes": 75,
"continuousUsageMinutes": 30,
"timeLimitTodayMinutes": 120,
"timeRemainingTodayMinutes": 45,
"dayResetsAt": "2026-06-25T00:00:00Z"
},
"schedule": {
"timezone": "Asia/Shanghai",
"breakReminderIntervalMinutes": 45,
"dailyLimits": [{ "day": "mon", "limitMinutes": 120 }],
"quietHours": [
{
"name": "Bedtime",
"days": ["mon", "tue", "wed", "thu", "fri"],
"start": "21:00",
"end": "07:00"
}
]
}
}

请按以下顺序读取:

  1. enabled。为 false 时响应中没有其他内容,原因有三种:父母没有配置屏幕时间;父母配置了但关闭了限制;或者会话没有 kuid,因此不存在可以归属安排的玩家。在测试中您最可能遇到的是第三种。这三种情况下都不要施加任何限制。
  2. access.allowed。这就是判定结果,其中已经综合了每日限制和所有安静时段。请不要自行从 schedule 重新计算。
  3. access.details。仅在 allowedfalse 时出现。reasonquiet_hourslimit_reached,而 resumesAt 是限制解除的时刻,这正是值得告知玩家的信息。当两种限制同时适用时,响应会返回结束更晚的那一个。
  4. state。供您界面使用的当天数值。timeUsedTodayMinutes 包含进行中会话的时长,而 continuousUsageMinutes 仅是该会话自身的长度。当天所属星期未设置限制时,timeLimitTodayMinutestimeRemainingTodayMinutes 不会出现。
  5. schedule。用于展示的父母规则。时刻为 schedule.timezone 时区下的 HH:MM

处理事件

只有当产品的 Webhook 订阅了相应的事件类型时,这些事件才会到达。在排查一个从未执行的处理逻辑之前,请先核对开始之前

事件含义应做的处理
Screentime.BreakReminder玩家在一个会话中连续使用达到了设定的间隔提示玩家休息
Screentime.LimitWarning当天还剩 15 分钟或 5 分钟告知玩家,以便其到达存档点
Screentime.LimitReached当天的限制已生效施加您的限制,并提供向父母申请的入口
Screentime.QuietHoursWarning时段将在 15 分钟后开始告知玩家是哪个时段以及何时开始
Screentime.QuietHoursReached时段已经开始endsAt 之前施加您的限制
Screentime.ScheduleChanged父母更改了规则重新获取 get-state 并替换您保存的副本
Screentime.OverrideResult父母答复了豁免请求应用负载中的 state

投递方面有三项特性值得在设计时考虑:

  • 事件面向 sessionId 投递。 请按 data.sessionId 分发,这七个事件都包含该字段。
  • 投递保证为至少一次。 同一事件可能到达两次。请按 Webhooks 概述的说明让处理逻辑保持幂等。
  • 安静时段事件不需要有进行中的会话。 对于在您产品中拥有有效 k-ID 会话的玩家,无论其当时是否在游玩,事件都会按父母的日历发出。请不要假设存在可以介入的界面。

向父母申请更多时间

access.allowedfalse 时,请为玩家提供申请的途径,而不只是一堵墙。

  1. 携带 sessionId 调用 /screentime/request-override。响应中包含 idexpiresAt。请保存该 id
  2. k-ID 通知父母,父母在 Family Connect 中批准或拒绝。
  3. 结果会作为 Screentime.OverrideResult 到达,通过 data.id 关联,其 statusgranteddeniedexpired
  4. 读取 data.state,它是决定生效后计算出的完整 get-state 结构。无需再次请求。

该端点对每位玩家是幂等的:在请求仍处于待处理状态时再次调用,会原样返回该待处理请求而不会创建第二个,并且两种情况下响应结构完全相同,因此您无需为此单独写一条代码路径。无人答复的请求会在 24 小时后到期,并以 status: "expired" 发出 Webhook。玩家随后可以再次申请。

获批的豁免会提高当天的限制。它不会停用安排,也不会延续到第二天。

时区与日期边界

每份安排都带有时区,屏幕时间的每项规则都按该时区计算。请以带偏移量的 RFC 3339 发送时间戳,把转换交给 k-ID。

  • 每日限制在安排所用时区的当地午夜重置。状态响应中的 dayResetsAt 就是该时刻。
  • 跨过当地午夜的会话会在 /screentime/end 时被拆分,因此每一天的总量都反映当天实际发生的使用时间。跨过午夜的 push 片段同样如此。

功能开关

屏幕时间分为两个层级,产品可以只启用第一个层级:

开关启用的能力所控制的端点
Screentime被动的使用时间上报,无需父母进行任何设置/screentime/push
Screentime Controls父母配置的限制、安排、安静时段、休息提醒和豁免/screentime/start/screentime/end/screentime/get-state/screentime/request-override

仅启用 Screentime 时,产品只能上报使用时间:调用 /screentime/push 提交已完成的片段,它们会计入孩子的每日总量。这与活动接入是同一种形态的集成,父母无需进行任何设置。

Screentime Controls 是第二个层级:父母配置的安排以及据此运行的一切。它控制会话状态机(/screentime/start/screentime/end)、当前判定(/screentime/get-state)和请求父母的流程(/screentime/request-override),实时事件由该会话状态机产生。

所属层级未启用的端点会返回 FEATURE_DISABLED。仅启用 Screentime 的产品可以 push 使用时间,但无法开始会话或读取状态;两者都未启用的产品无法调用其中任何一个。

上线前检查清单

  • 在每个会话开始时调用 GET /screentime/get-state,并在 enabled: false 时不对玩家施加任何限制。
  • 您的产品读取 access.allowed,而不是从 schedule 重新计算判定结果。
  • 每次 /screentime/start 都与携带相同 id/screentime/end 配对,并且把 statusreplaced 的情况作为正常情况处理而非错误。
  • 每一对预警中的两个事件都被处理,而不只是第二个。
  • 产品的 Webhook 已订阅您所处理的全部七个 Screentime.* 事件类型。Webhook 未选中的事件类型不会被投递。
  • Webhook 处理逻辑是幂等的,并且验证签名。
  • 收到 Screentime.ScheduleChanged 后会重新获取 get-state
  • 对达到限制或进入安静时段的玩家提供 /screentime/request-override,并处理包括 expired 在内的全部三种豁免状态。
  • 您的产品所施加的限制会使用 resumesAtendsAt 告知玩家限制何时解除。

后续步骤