本部分包含针对 Fuchsia 贡献者更改 Fuchsia 平台 API 的指南。在开始之前,您应该先熟悉以下概念:
平台 API 的生命周期
Fuchsia 平台 API 应遵循以下生命周期:添加 → 弃用 → 移除 → 删除,如下图所示:
以下部分介绍了作为 API 开发者如何管理此生命周期。
添加 FIDL API
请务必使用 @available
属性为新的 FIDL 库和元素添加注释。不稳定的 API 应添加到 HEAD
API 级别。请注意,使用该 SDK 的合作伙伴可以以 HEAD
API 级别为目标平台,但如果这样做,则无法获得 API/ABI 兼容性保证。
例如:
@available(added=HEAD)
library fuchsia.examples.docs;
当 API 准备好稳定时,您应在 NEXT
将其更新为 added
。例如:
@available(added=NEXT)
library fuchsia.examples.docs;
NEXT
与 HEAD
类似,但 NEXT
中提供的 API 元素会自动添加到下一个已发布的 API 级别。因此,如果您在 F23 的 API 冻结前一周将我们的库添加到 NEXT
,一周后您会看到:
@available(added=23)
library fuchsia.examples.docs;
在此之后,添加到 23 的任何 API 元素都需要支持,直到包含这些 API 元素的所有 API 级别都已停用。
如果 FIDL 库包含多个 .fidl
文件,则该库应包含一个单独的 overview.fidl
文件,并且 @available
属性应写入该文件,同时附上描述该库的文档注释。如需了解详情,请参阅 FIDL 样式指南。
SDK 类别中的每个 FIDL API 都会选择在 CI/CQ 中进行静态兼容性测试。如果 API 以不向后兼容的方式更改,这些测试就会失败。新库未指定 SDK 类别,这会阻止它们向合作伙伴公开,将它们排除在兼容性测试之外,并允许自由更改 API。当 API 稳定且库已完成 API 校准后,请指定 "partner"
类别。如需了解详情,请参阅升级 API。
替换 FIDL API
有时,您需要使用新定义替换 API。如需在 API 级别 N
执行此操作,请使用 @available(replaced=N)
为旧定义添加注释,并使用 @available(added=N)
为新定义添加注释。例如,以下代码展示了如何在 API 级别 5 更改常量的值:
@available(replaced=5)
const MAX_LENGTH uint32 = 16;
@available(added=5)
const MAX_LENGTH uint32 = 32;
弃用 FIDL API
对于依赖平台 API 的开发者来说,Fuchsia 平台必须提供顺畅的过渡。其中一部分是充分警告用户,API 将在未来被移除。弃用是向开发者传达此信息的一种方式。
您应始终在移除 API 之前先将其废弃。当最终开发者以已废弃的 API 为目标平台时,他们会在 build 时看到一条警告,指出该 API 已废弃,他们应迁移到替代方案。您应添加备注,帮助最终开发者找到替代方案。例如:
protocol Example {
// (Description of the method.)
//
// # Deprecation
//
// (Detailed explanation of why the method is deprecated, the timeline for
// removing it, and what should be used instead.)
@available(deprecated=5, removed=6, note="use Replacement")
Deprecated();
@available(added=5)
Replacement();
};
API 的弃用和移除之间必须至少间隔一个 API 级别。 例如:
// These are OK.
@available(deprecated=5, removed=6)
@available(deprecated=5, removed=100)
@available(added=5, deprecated=5)
// These will not compile.
@available(deprecated=5, removed=5)
@available(deprecated=5, removed=3)
移除 FIDL API
请注意,在移除 API 之前,您应始终先弃用该 API。
如需移除在稳定级别中添加的 API,请使用 @available
属性的 removed
实参。例如,如需移除在级别 12 中被弃用的级别 18 中的方法,请执行以下操作:
protocol Example {
@available(added=10, deprecated=12, removed=18, note="Use Go() instead")
Run() -> ();
};
在此示例中,以 10 到 17 之间(含)的任何级别为目标平台的最终开发者都会看到 Run
方法的客户端绑定,但以 18 或更高级别为目标平台的开发者则不会看到。只要 Fuchsia 平台支持 10 到 17 之间的任何级别,在该平台上工作的开发者就会看到 Run
方法的绑定,因为平台 build 目标是所有受支持的 API 级别的集合。
一旦平台不再支持 10 到 17 之间的所有级别,您就可以根据需要从 FIDL 文件中删除 Run
方法。如果您在此之前删除该文件,静态兼容性测试将失败,并且需要获得 //sdk/history/OWNERS 的特殊批准才能提交更改。
如需移除在不稳定级别(例如 NEXT
或 HEAD
)添加的 API,只需从 FIDL 文件中将其删除即可。
设计可顺利演变的 API
此评分标准侧重于提升与各种平台版本的兼容性。这些属性可尽可能轻松地保持兼容性,并且是 FIDL API 评分标准的子集。
遵循 FIDL 样式指南
FIDL 样式指南用于确保 FIDL 可读并体现最佳实践。这些通常是最佳实践,无论 sdk_category 是什么,都应遵循这些最佳实践。
使用 FIDL 版本控制注释
借助 FIDL 版本控制注释,库、协议和其他元素可以与特定 API 级别相关联。所有兼容性推理都基于 API 版本。以下是表示 API 发展历程中某个时间点的方式。
只能在
NEXT
或HEAD
API 级别修改 API。只有当更改准备好在下一个 Fuchsia 里程碑中发布时,才应在
NEXT
中实现这些更改。不应更改编号的 API 级别。请参阅 version_history.json。
为向量和字符串指定边界
更多信息: FIDL API 评分标准 - 为矢量和字符串指定边界
使用枚举与布尔值
由于布尔值是二元值,因此在创建兼容性良好的 API 时,最好使用可以具有多种状态的枚举。这样一来,如果需要添加其他状态,可以扩展枚举,而布尔值则必须替换为其他类型。更多信息: FIDL API 评分标准 - 如果可能,请避免使用布尔值。
使用灵活的枚举和位
灵活的枚举具有默认的未知成员,因此可以轻松演变枚举。
只有在您非常确信 strict
、enum
和 bits
类型永远不会被扩展时,才使用这些类型。strict
、enum
和 bits
类型无法扩展,将它们迁移到 flexible
需要为具有给定类型的每个字段进行迁移。
如需了解详情,请参阅 FIDL 语言 - 严格与灵活
优先选择表而非结构体
结构体和表都表示具有多个命名字段的对象。不同之处在于,结构体在线格式中具有固定的布局,这意味着在不破坏二进制兼容性的情况下,无法修改结构体。相比之下,表在有线格式中具有灵活的布局,这意味着可以随时向表中添加字段,而不会破坏二进制兼容性。
更多信息: FIDL API 评分标准 - 我应该使用结构还是表?
使用开放协议以及灵活的方法和事件
一般来说,所有协议都应为 open
,并且这些协议中的所有方法和事件都应为 flexible
。
将协议标记为开放,可以更轻松地处理以下情况:不同组件可能是在不同版本中构建的,因此每个组件对存在哪些方法和事件的看法可能不同,此时移除方法或事件会比较麻烦。由于协议的灵活性对于不断发展的协议来说通常是可取的,因此建议选择开放协议,除非有理由选择更封闭的协议。
一个潜在的例外情况是针对分离协议(表示交易)的,其中唯一的双向方法是必须严格执行的提交操作,而交易中的其他操作可能会发生变化。如果某个协议非常小,不太可能发生变化,并且预计会由客户端实现,您可以将其设为 closed
,并将其所有方法设为 strict
。这样一来,客户就无需费心决定如何处理“未知互动”。不过,代价是永远无法向此类协议添加或从中移除方法或事件。如果您决定要添加方法或事件,则需要定义新的分离协议来替换它。
更多信息:
使用错误语法
错误语法用于指定方法将返回值,或者出错并返回表示错误的 int 或枚举。
使用自定义错误枚举,而不是 zx.Status
在定义和控制网域时,使用专门构建的枚举错误类型。例如,当协议是专门构建的,并且传达错误语义是唯一的设计限制时,可以定义枚举。
如果您遵循的是明确定义的规范(例如 HTTP 错误代码),并且枚举旨在以符合人体工程学的方式表示规范规定的原始值,请使用特定于网域的枚举错误类型。
更多信息:FIDL API 评分标准 - 针对错误首选特定于网域的枚举。
请勿使用其他库中的声明
如果公共 API 在语义上等效,则最好重用类型并组合协议,但这样做很容易出错。