Fuchsia 平台 API 演变指南

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

平台 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 样式指南

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

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

设计可顺利演变的 API

此评分标准侧重于提升与各种平台版本的兼容性。这些属性可尽可能轻松地保持兼容性,并且是 FIDL API 评分标准的子集。

遵循 FIDL 样式指南

FIDL 样式指南用于确保 FIDL 可读并体现最佳实践。这些通常是最佳实践,无论 sdk_category 是什么,都应遵循这些最佳实践。

使用 FIDL 版本控制注释

借助 FIDL 版本控制注释,库、协议和其他元素可以与特定 API 级别相关联。所有兼容性推理都基于 API 版本。以下是表示 API 发展历程中某个时间点的方式。

  • 只能在 NEXTHEAD API 级别修改 API。

  • 只有当更改准备好在下一个 Fuchsia 里程碑中发布时,才应在 NEXT 中实现这些更改。

  • 不应更改编号的 API 级别。请参阅 version_history.json

为向量和字符串指定边界

更多信息: FIDL API 评分标准 - 为矢量和字符串指定边界

使用枚举与布尔值

由于布尔值是二元值,因此在创建兼容性良好的 API 时,最好使用可以具有多种状态的枚举。这样一来,如果需要添加其他状态,可以扩展枚举,而布尔值则必须替换为其他类型。更多信息: FIDL API 评分标准 - 如果可能,请避免使用布尔值

使用灵活的枚举和位

灵活的枚举具有默认的未知成员,因此可以轻松演变枚举。

只有在您非常确信 strictenumbits 类型永远不会被扩展时,才使用这些类型。strictenumbits 类型无法扩展,将它们迁移到 flexible 需要为具有给定类型的每个字段进行迁移。

如需了解详情,请参阅 FIDL 语言 - 严格与灵活

优先选择表而非结构体

结构体和表都表示具有多个命名字段的对象。不同之处在于,结构体在线格式中具有固定的布局,这意味着在不破坏二进制兼容性的情况下,无法修改结构体。相比之下,表在有线格式中具有灵活的布局,这意味着可以随时向表中添加字段,而不会破坏二进制兼容性。

更多信息: FIDL API 评分标准 - 我应该使用结构还是表?

使用开放协议以及灵活的方法和事件

一般来说,所有协议都应为 open,并且这些协议中的所有方法和事件都应为 flexible

将协议标记为开放,可以更轻松地处理以下情况:不同组件可能是在不同版本中构建的,因此每个组件对存在哪些方法和事件的看法可能不同,此时移除方法或事件会比较麻烦。由于协议的灵活性对于不断发展的协议来说通常是可取的,因此建议选择开放协议,除非有理由选择更封闭的协议。

一个潜在的例外情况是针对分离协议(表示交易)的,其中唯一的双向方法是必须严格执行的提交操作,而交易中的其他操作可能会发生变化。如果某个协议非常小,不太可能发生变化,并且预计会由客户端实现,您可以将其设为 closed,并将其所有方法设为 strict。这样一来,客户就无需费心决定如何处理“未知互动”。不过,代价是永远无法向此类协议添加或从中移除方法或事件。如果您决定要添加方法或事件,则需要定义新的分离协议来替换它。

更多信息:

使用错误语法

错误语法用于指定方法将返回值,或者出错并返回表示错误的 int 或枚举。

使用自定义错误枚举,而不是 zx.Status

在定义和控制网域时,使用专门构建的枚举错误类型。例如,当协议是专门构建的,并且传达错误语义是唯一的设计限制时,可以定义枚举。

如果您遵循的是明确定义的规范(例如 HTTP 错误代码),并且枚举旨在以符合人体工程学的方式表示规范规定的原始值,请使用特定于网域的枚举错误类型。

更多信息:FIDL API 评分标准 - 针对错误首选特定于网域的枚举

请勿使用其他库中的声明

如果公共 API 在语义上等效,则最好重用类型并组合协议,但这样做很容易出错。