8.7 发布—WinterCG 合规性第 1 部分
了解详情

通过元数据,NativeScript 赋予 JavaScript 平台 iOS 和 Android API。平台元数据包含有关支持的每个本机类、接口、协议、结构、枚举、函数、变量等所有必需的信息,并且是在构建时通过检查 iOS/Android SDK 和本机应用程序使用的任何第三方库和框架中的本机库来生成的。

注意

您不能使用元数据中不存在的 API。默认情况下,如果在构建时未提供 --compileSdk 参数,将针对工作站上安装的最新 Android 平台 SDK 构建元数据。请参阅元数据限制

元数据筛选

默认情况下,NativeScript 会将所有支持的实体包含在元数据中。这允许应用程序和插件作者从 JavaScript 任意调用任何本机 API。尽管在开发期间这十分有利,但在某些情况下,为所有 API 提供元数据是不可取且不必要的。例如,可能涉及安全影响(例如在元数据二进制文件中提及不应该出现实体的名称);在运行时性能可能降低(由于在遇到未知入口或在启动时必须咨询更大的元数据);或者由于不必要的元数据从未使用而使应用程序大小增加。

为了让开发人员能够控制所生成的元数据,因此支持通过其本机名称将符号列入黑名单或白名单。

插件中的元数据筛选规则

插件可以使用一个名为 native-api-usage.json 的文件声明从 JavaScript 调用的 API 列表,该文件位于每个平台目录(platforms/androidplatforms/ios)中。其格式类似于

json
{
  "uses": ["java.util:List"]
}

应用程序中的元数据筛选规则

应用程序具有将哪些筛选应用到元数据的最终决定权。它们提供了类似的 native-api-usage.json 文件,位于 App_Resources/AndroidApp_Resources/iOS 中,采用以下格式

json
{
  "whitelist-plugins-usages": true,
  "whitelist": ["java.util:Base64*"],
  "blacklist": ["java.util:Locale*"]
}

规则语法

每个列表都包含以下特征的模式条目

  • 条目的格式为 <pattern1>[:pattern2]
  • 在 Android 上,将 pattern1 与 Java 包名称进行匹配,而将可选的 pattern2 与类、接口和枚举进行匹配。
  • 在 iOS 上,将 pattern1 与 Clang 模块/子模块名称进行匹配,而将可选的 pattern2 与结构体、全局函数、枚举、Objective-C 接口、协议、类别、常量等进行匹配。
  • 模式支持通配符("*" -任意数量的字符和 "?" - 任何单个字符)。
  • 未指定或空模式等效于设置为 "*"(匹配所有内容)

规则语义

在分析平台的筛选规则后,{N} CLI 构建最终白名单和黑名单文件,并将其输出到要由相应的元数据生成器使用的本机项目中。黑名单始终等于应用指定的黑名单。而白名单则取决于 whitelist-plugins-usages 标志

  • 如果为 true,则最终白名单是将所有插件使用列表与应用的白名单连接起来
  • 否则,则等于应用的白名单

这两个列表明确地确定筛选的执行方式

  1. 如果白名单为空,则默认情况下所有内容都被视为白名单
  2. 如果它包含至少一个规则,则仅匹配规则的实体被视为白名单
  3. 所有未列入白名单或与黑名单中的规则匹配的实体都将从元数据中删除
  4. 所有其他实体都包含在元数据中

示例

可以在 @nativescript/core 插件存储库中找到示例筛选规范

故障排除

缺少元数据实体可能导致运行时的错误。例如,如果本机类已被意外筛选掉,则其构造函数将为 undefined,并且在尝试调用它时会导致异常。弄清楚导致某些内容为 undefined 的原因可能相当困难,因为原因各不相同。要检查元数据筛选是否要归咎,您应该在成功构建后检查元数据生成器的详细信息日志

  • 在 iOS 上,它们位于 platforms/ios/build/<configuration>-<platform>/metadata-generation-stderr-<arch>.txt 中(例如 platforms/ios/build/Debug-iphonesimulator/metadata-generation-stderr-x86_64.txt);
  • 在 Android 上,它们位于 platforms/android/build-tools/buildMetadata.log

对于生成器发现的每个全局符号,都应有一行提供有关是否将其包括在元数据中以及哪条规则或什么异常导致了此情况的信息。示例

  • verbose: Blacklisted kCFBuddhistCalendar from CoreFoundation.CFLocale (disabled by 'CoreFoundation*:*') - 当没有白名单规则时,黑名单符号将仅显示禁用它的规则
  • verbose: Blacklisted NSString from Foundation.NSString - 当至少有一个白名单规则时,一些黑名单符号不会指定规则。这意味着该符号不匹配任何白名单规则。
  • verbose: Blacklisted PHImageContentModeDefault from Photos.PhotosTypes (enabled by 'Photos.PhotosTypes:*', disabled by 'Photos.PhotosTypes:PHImageContentMode*')verbose: Blacklisted String from java.lang (enabled by java.lang:*, disabled by java.lang:String) - 匹配白名单规则和黑名单规则的黑名单条目将指定两者。
  • verbose: Included NSObject from ObjectiveC.NSObject - 当没有白名单规则时,包含的符号不会指定导致其包含的规则
  • verbose:包括 PHCollectionListType 来自 Photos.PhotosTypes(由 'Photos.PhotosTypes:*' 启用)verbose:包括 StrictMath 来自 java.lang(由 java.lang:* 启用) - 当某个符号包含在内是因为它匹配白名单中的规则(并且不匹配黑名单中的任何规则)时,它将打印该规则
  • verbose:异常 [名称:'vfwprintf',js 名称:'vfwprintf',模块:'Darwin.C.wchar',文件:'/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.2.sdk/usr/include/wchar.h']:无法创建类型依赖关系。 --> [类型衰减]:无法创建类型依赖关系。 --> [类型 typedef]:不支持 VaList 类型。 - 如果某个符号由于某些原因不被包括在内,它将被记录在日志异常中。在这种情况中,该符号无法由 JavaScript 使用,因为 NativeScript 不支持调用具有可变参数列表的函数。
  • verbose:异常 [名称:'GLKVector3Make',js 名称:'GLKVector3Make',模块:'GLKit.GLKVector3',文件:'/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.2.sdk/System/Library/Frameworks/GLKit.framework/Headers/GLKVector3.h']:无法创建类型依赖关系。 --> [类型 typedef]:无法创建类型依赖关系。 --> [类型详细阐述]:无法创建类型依赖关系。 --> [类型记录]:该记录是一个联合。 - 另一个不支持符号的示例,这次的原因是 union 不受支持

Android 元数据 JNI 签名。它以二进制格式预先生成,并嵌入在应用程序包 (apk) 中,存储所需的最小信息,从而提供小尺寸和高效的读访问。生成过程使用字节码读取来解析 NativeScript 项目中提供的 Android 库中的所有公开可用类型。生成器作为 Android 构建过程的一部分工作,这意味着无需用户交互即可使其工作。

元数据 API 级别 元数据限制

我们来看一下 Android TextView。在 API 级别 21 中,添加了一个名为 getLetterSpacing 的方法,这意味着应用程序开发人员只能在两个条件下使用 getLetterSpacing 方法

  • 已构建元数据 >= 21
  • 应用程序将运行在的设备 >= 21

使用 Android API 时可能的含义

含义 A:针对较低 API 级别进行构建。

如果使用指向较低 Android API 级别(例如 19)的 --compileSdk 标志来构建应用程序,那么生成的元数据也将针对 API 级别 19。在这种情况下,将无法调用 API 级别 21 及更高版本的 API,因为元数据包含关于 API 级别 <= 19 的元信息。

这个问题可以通过不指定 --compileSdk 标志并使用默认行为来轻松解决。

含义 B:针对较高 API 级别进行构建。

当使用较高的 API 级别(例如 23)构建应用程序,但在 API 级别较低的设备(例如 20)上运行时,会发生什么情况?首先,元数据针对 API 级别 23 构建。如果 JavaScript 代码调用 API 级别 20 之后引入的某个方法,则 Android 运行时将尝试调用此方法,因为它会在元数据中识别此方法,但当在较低级别设备上进行实际原生调用时,将抛出异常,因为此方法不会出现在设备上。

这个问题可以通过在运行时检测 API 级别并使用可用 API 来解决。

在 JavaScript 中检测 API 等級

js
if (android.os.Build.VERSION.SDK_INT >= 21) {
  // your api level-specific code
}

iOS 元数据

这是我们自定义的数据格式,用于列出我们了解并且能够使用 iOS API。它存储最少所需的信息,并且尺寸较小,读取访问效率极高。iOS 支持一定程度的类型内省,但同时嵌入在 native API 中的所有 C API,所以我们必须存储大量额外信息。元数据在编译时由 SDK 头文件预先生成,并嵌入在 app 包 (ipa) 中。

下一个
封送