API标签化管控

说明:

当前为Beta阶段。

概述

通常,开发者需要对一些 API 进行标签化,使得这些 API 能够根据标签被限定在源码的不同位置使用。全局标签会从 Deveco Studio 创建的仓颉工程中获取,例如:创建的工程为 6.0.2,那么它的全局 API level 为 22。 标签化的 API 需要满足全局的标签配置才能够被合法使用。

为了做到 API 标签化管控,仓颉语言引入了 @IfAvailable 宏表达式。它可以基于全局的标签设置,提供更精细的标签化 API 的使用管控。@IfAvailable 需要与自定义注解 APILevel 配合使用,本章将介绍 @IfAvailable 的功能及使用方法。

@IfAvailable 的语法

@IfAvailable(<label>: <value>, <lambda1>, <lambda2>)

其中:

  • <label> 为自定义注解 APILevel 的参数名称;
  • <value> 为该注解类型的参数,仅支持字面量表达式;
  • <label>: <value> 构成了 @IfAvailable 的条件;
  • <lambda1><lambda2> 需满足 Lambda 表达式的语法,且被限定为无形参的形式,<lambda1><lambda2> 的返回类型由编译器推断为 Unit

@IfAvailable 的语义

@IfAvailable 的条件在运行态是成立的,则 <lambda1> 的函数体会被执行,否则 <lambda2> 的函数体会被执行。<lambda1> 中所有调用的符号将被标记为弱符号,不要求编译链接时一定能够找到。

在一个仓颉工程中,使用 @IfAvailable 表达式时需要根据检查类型手动导入依赖包:level 检查依赖 ohos.device_infosyscap 检查依赖 ohos.base

level 检查

@IfAvailable 宏表达式中,指定 <label>level 时,该项检查生效。

在任何作用域中,不允许调用比当前作用域更高 Level 的 API。即 <lambda1> 中不得调用高于 level: <value><value> 指定的值,在 <lambda2> 中不得调用高于当前工程的 Level 等级。

syscap 检查

@IfAvailable 宏表达式中,指定 <label>syscap 时,该项检查生效。

说明:

  • 交集:所有设备同时拥有的能力集合;
  • 并集:至少有一个设备拥有的能力集合。

检查规则:

  • 当调用的 API 满足交集时,不报错;
  • 当调用 API 不满足交集,但满足并集时,编译告警;
  • 当调用 API 既不满足交集,也不满足并集时,编译报错。

在任何作用域中,不允许调用任何设备中都不支持的 API,允许调用 SysCap 在交集并集中的 API。@IfAvailable 可以为交集并集增加能力,即 <lambda1> 中可以调用 syscap: <value><value> 指定的 SysCap API,在 <lambda2> 中则不允许。

@IfAvailable 使用示例

使用 IfAvailable 控制 APILevel

前置依赖,提供不同标签的 API:

package ohos.sample

import kit.PerformanceAnalysisKit.Hilog
import ohos.labels.APILevel

@!APILevel[since: "23"]
public func f23() {
    Hilog.info(0, "", "level-23")
}

@!APILevel[since: "24"]
public func f24() {
    Hilog.info(0, "", "level-24")
}

@!APILevel[since: "26.0.0"]
public func f26() {
    Hilog.info(0, "", "level-26")
}

假设 ohos.sample 为 sdk 提供的包,用户使用 Deveco Studio 仓颉项目工程时可以选择所需的 APILevel 等级,以 compatibleSDKVersion 为 23 的工程为例:

image-Create-Project-With-Level

使用 @IfAvailable 时,<label>: <value>level: xxxx 可以是整数字面量(如 24)或三段字符串字面量(如 "26.0.0")。

import ohos.sample.*
import ohos.device_info.*

func demo() {
    @IfAvailable(level: 24, { =>
        // 编译期:此作用域允许调用 level 为 24 或 24 以下的 API。即该分支能够使用 f23, f24,调用更高等级接口(如 f26)会编译报错。
        // 运行期:当执行设备支持 level 24,那么执行该分支。
        f23()
        f24()
        f26() // compile error
    }, { =>
        // 编译期:此作用域使用工程提供的能力,允许调用 level 为 23 或 23 以下的 API。即该分支能够使用 f23,调用更高等级接口会编译报错。
        // 运行期:当执行设备支持 level 23,那么执行该分支。
        f23()
        f24() // compile error
        f26() // compile error
    })
}

也可以使用三段字符串形式指定版本:

import ohos.sample.*
import ohos.device_info.*

func demo() {
    @IfAvailable(level: "26.0.0", { =>
        // 编译期:此作用域允许调用 since 为 "26.0.0" 或更低版本的 API。
        // 运行期:当执行设备支持版本 "26.0.0",那么执行该分支。
        f23()
        f24()
        f26()
    }, { =>
        // 编译期:此作用域使用工程提供的能力,允许调用 level 为 23 或 23 以下的 API。
        f23()
        f24() // compile error
        f26() // compile error
    })
}

使用 IfAvailable 控制 syscap

前置依赖,提供不同标签 syscap 的 API:

package ohos.sample

import kit.PerformanceAnalysisKit.Hilog
import ohos.labels.APILevel

@!APILevel[since: "22", syscap: "SystemCapability.A"]
public func f1() {
    Hilog.info(0, "", "SystemCapability.A")
}

@!APILevel[since: "22", syscap: "SystemCapability.B"]
public func f2() {
    Hilog.info(0, "", "SystemCapability.B")
}

@!APILevel[since: "22", syscap: "SystemCapability.C"]
public func f3() {
    Hilog.info(0, "", "SystemCapability.C")
}

@!APILevel[since: "22", syscap: "SystemCapability.D"]
public func f4() {
    Hilog.info(0, "", "SystemCapability.D")
}

Deveco Studio 默认读取所有设备支持的 SystemCapability,用于检查作用域中是否允许使用带有标签的 API。

假设 ohos.sample 为 sdk,此时设备1的 syscap 为 ["SystemCapability.A", "SystemCapability.B"],设备2的 syscap 为 ["SystemCapability.B", "SystemCapability.C"]

使用 @IfAvailable 时,<label>: <value>syscap: "SystemCapability.xx""SystemCapability.xx" 为字符串字面量。

import ohos.sample.*
import ohos.base.*

func demo() {
    @IfAvailable(syscap: "SystemCapability.D", { =>
        // 此作用域最高允许使用 ["SystemCapability.A", "SystemCapability.B", "SystemCapability.C", "SystemCapability.D"],其中:
        // ["SystemCapability.B", "SystemCapability.D"] 不告警;
        // ["SystemCapability.A", "SystemCapability.C"] 告警;
        // 非 ["SystemCapability.A", "SystemCapability.B", "SystemCapability.C", "SystemCapability.D"] 报错
        f1() // warning
        f2() // ok
        f3() // warning
        f4() // ok
    }, { =>
        // 此作用域最高允许使用 ["A", "B", "C"],其中:
        // ["B"] 不告警;
        // ["A", "C"] 告警;
        // 非 ["A", "B", "C"] 报错
        f1() // warning
        f2() // ok
        f3() // warning
        f4() // error
    })
}