Fuchsia 平台 API 演变指南

本部分包含面向对 Fuchsia 平台 API 进行更改的 Fuchsia 贡献者的指南。在开始之前,您应该先熟悉以下概念:

平台 API 的生命周期

Fuchsia 平台 API 应遵循以下生命周期:添加 → 弃用 → 移除 → 删除,如下图所示:

此图片显示了 Fuchsia 平台上的 API 生命周期,从添加 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;

NEXTHEAD 类似,但 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 的特殊批准才能提交更改。

如需移除在不稳定级别(例如 NEXTHEAD)添加的 API,只需从 FIDL 文件中将其删除即可。

设计可顺利演变的 API

此评分标准重点关注提升与各种平台版本的兼容性。这些属性可让您尽可能轻松地维护兼容性,是 FIDL API Rubric 的一部分。

遵循 FIDL 样式指南

FIDL 样式准则用于使 FIDL 可读,并体现最佳实践。这些通常是最佳实践,无论 sdk_category 为何,都应遵循。

使用 FIDL 版本控制注解

借助 FIDL 版本注解,您可以将库、协议和其他元素与特定 API 级别相关联。所有兼容性推理均基于 API 版本。这是表示 API 演变过程中的一个点的方式。

  • 请仅在 NEXTHEAD 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 enumbits 类型。strict enumbits 类型无法扩展,并且将它们迁移到 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 可以重复使用这些类型和协议,但很容易出错。