本部分包含面向对 Fuchsia 平台 API 进行更改的 Fuchsia 贡献者的指南。在开始之前,您应该先熟悉以下概念:
平台 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 样式指南。
"partner"
SDK 类别中的每个 API 都会选择在 CI/CQ 中进行静态兼容性测试。如果 API 以不向后兼容的方式发生更改,这些测试会失败。新库未指定 SDK 类别,因此不向合作伙伴公开这些库、将其从兼容性测试中排除,并允许 API 自由更改。API 稳定且库已完成 API 校准后,请指定 "partner"
类别。
替换 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
请务必让 Fuchsia 平台为依赖平台 API 的开发者提供平滑的过渡。其中包括提供充足的警告,告知 API 将在未来被移除。废弃是向开发者传达这一信息的一种方式。
您应该始终在早于移除 API 的级别进行弃用。当最终开发者以已废弃的 API 为目标平台时,他们会在构建时看到一条警告,告知该 API 已废弃,并且他们应改用替代 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,请使用 @available
属性的 removed
参数。例如,如需移除在级别 12 中已废弃的级别 18 方法,请执行以下操作:
protocol Example {
@available(added=10, deprecated=12, removed=18, note="Use Go() instead")
Run() -> ();
};
在此示例中,以 10 到 17(包括这两个数值)之间的任意级别为目标平台的最终开发者会看到 Run
方法的客户端绑定,但以 18 或更高级别为目标平台的开发者则不会看到。只要平台支持 10 到 17 之间的任何级别,使用 Fuchsia 平台的开发者便会看到 Run
方法的绑定,因为平台 build 是针对所有受支持的 API 级别集合。
当平台停止支持 10 到 17 之间的所有级别后,您可以根据需要从 FIDL 文件中删除 Run
方法。如果您在完成此操作之前删除该文件,静态兼容性测试将会失败,并且您需要获得 //sdk/history/OWNERS 的特殊批准才能提交更改。
如需移除在不稳定级别(例如 NEXT
或 HEAD
)添加的 API,只需从 FIDL 文件中将其删除即可。
设计可顺利演变的 API
此评分标准重点关注提升与各种平台版本的兼容性。这些属性可让您尽可能轻松地维护兼容性,是 FIDL API Rubric 的一部分。
遵循 FIDL 样式指南
FIDL 样式准则用于使 FIDL 可读,并体现最佳实践。这些通常是最佳实践,无论 sdk_category 为何,都应遵循。
使用 FIDL 版本控制注解
借助 FIDL 版本注解,您可以将库、协议和其他元素与特定 API 级别相关联。所有兼容性推理均基于 API 版本。这是表示 API 演变过程中的一个点的方式。
请仅在
NEXT
或HEAD
API 级别修改 API。只有当更改已准备好在下一个 Fuchsia 里程碑中发布时,才应在
NEXT
中实现更改。编号的 API 级别不应更改。请参阅 version_history.json。
为矢量和字符串指定边界
更多信息:FIDL API Rubric - Specify bounds for vector and string
使用枚举与使用布尔值
由于布尔值是二进制的,因此在使 API 兼容型时,最好使用可具有多个状态的枚举。这样,如果需要其他状态,可以扩展枚举,而布尔值必须替换为其他类型。更多信息:FIDL API Rubric - Avoid booleans if more states are possible。
使用灵活的枚举和位
灵活枚举具有默认的未知成员,因此可以轻松演变枚举。
仅当您非常确信这些类型永远不会扩展时,才应使用 strict
enum
和 bits
类型。strict
enum
和 bits
类型无法扩展,并且将它们迁移到 flexible
需要对具有给定类型的每个字段进行迁移。
更多信息:FIDL 语言 - 严格与灵活
优先使用表,而不是结构体
结构体和表都表示具有多个命名字段的对象。不同之处在于,结构体具有传输格式的固定布局,这意味着结构体无法在不破坏二进制文件兼容性的情况下进行修改。相比之下,表在线格格式中具有灵活的布局,这意味着可以随着时间的推移向表中添加字段,而不会破坏二进制兼容性。
更多信息:FIDL API Rubric - Should I use a struct or table?
使用开放协议搭配灵活的方法和事件
一般来说,所有协议都应为 open
,并且这些协议中的所有方法和事件都应为 flexible
。
将协议标记为开放协议后,当不同组件可能在不同版本中构建时,便可更轻松地处理移除方法或事件的问题,这样每个组件对存在的方法和事件都有不同的看法。由于通常希望协议具有灵活性,因此建议为协议选择开放式,除非有理由选择更封闭的协议。
一个可能的例外情况是代表事务的拆解协议,在这种协议中,唯一的双向方法是提交操作(必须严格执行),而事务上的其他操作可能会不断演变。如果协议非常小、不太可能发生变化,并且预计由客户端实现,您可以将其设为 closed
,并将所有方法设为 strict
。这样,客户端就不必费心决定如何处理“未知互动”。不过,代价是,您永远无法向此类协议添加或从中移除方法或事件。如果您决定要添加方法或事件,则需要定义新的分屏协议来替换它。
更多信息:
使用错误语法
错误语法用于指定方法将返回值,或输出错误并返回表示错误的 int 或枚举。
使用自定义错误枚举,而不是 zx.Status
定义和控制网域时,请使用专用枚举错误类型。例如,如果协议是专门构建的,则定义一个枚举,传达错误的语义是唯一的设计限制。
遵循明确定义的规范(例如 HTTP 错误代码)时,请使用特定于领域的枚举错误类型,并且枚举旨在以符合人体工学的方式表示规范规定的原始值。
更多信息:FIDL API Rubric - Prefer domain specific enum for errors。
不要使用其他库中的声明
如果类型和协议在语义上等效,则公共 API 可以重复使用这些类型和协议,但很容易出错。