互操作性增强、渠道特定的网络组件、优化类型揣度,以及空安全言语里程碑的近期更新

文/ Michael Thomsen, Google Flutter & Dart 产品司理

Dart 2.18 稳定版也跟着 Flutter 3.3 稳定版一同发布,本次更新带来了 Dart 与 Objective-C & Swift 互操作特性的预览版,以及依据这个特性构建的 iOS/macOS 网络组件的 package。新的 Dart 还包含泛型办法的类型揣度优化、异步代码的功用提高、pub.dev 新的功用,以及对咱们工具和核心库的一些调整。

文章最终咱们也给出了最新的空安全搬迁状态状况数据,以及终究彻底完成 Dart 空安全特性路线图的一个重要更新,请务必读到最终。

Dart 2.18 正式发布

Dart 与 Objective-C 和 Swift 互调

早在 2020 年的时分,咱们发布了外部功用接口 (FFI) 用于调用原生 C 言语接口的预览,并在 2021 年的 Dart 2.12 中正式发布。自那时起,很多的 package 借助于 FFI 的优势与现有的原生 C 言语接口 API 集成,举一些比如,比如 file_pickerprintingwin32objectboxrealmisartflite_flutter 以及 dbus 这些 package。

Dart 团队期望主流编程言语之间的互操作能够在所有 Dart 能够运行的渠道上都支撑,2.18 正式版达到了这个方针的下一个里程碑,现在,Dart 代码能够直接调用 Objective-C 和 Swift 代码了,首要用于在 macOS 和 iOS 渠道调用 API。Dart 支撑“全端调用”——从后端的指令行代码,再到前端的 Flutter 界面,你能够在任何运用中运用这种互操作机制。

这种全新机制源自于 Objective-C 和 Swift 代码能够经过 API 绑定机制用 C 言语代码来调用。Dart 的 ffigen工具能够经过 API 头文件来创立这些绑定,接下来看一个比如。

在 Objective-C 中操作时区的比如

macOS 上有一个查询时区信息的 API,能够经过 NSTimeZone类来调用,开发者们能够经过这个 API 来查询用户为设备设定的时区和 UTC 时区偏移。

下面的示例 Objective-C 运用就调用了这个时区 API 来取得系统时区设定和 GMT 偏移。

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSTimeZone *timezone = [NSTimeZone systemTimeZone]; // Get current time zone.
        NSLog(@"Timezone name: %@", timezone.name);
        NSLog(@"Timezone offset GMT: %ld hours", timezone.secondsFromGMT/60/60);
    }
    return 0;
}

这个示例运用首要导入了 Foundation.h 头文件,它包含了 Apple 的根底库的 API 头文件。在接下来的办法体中,它调用了 NSTimeZone 的 systemTimeZone 办法,这个办法会返回一个实例化之后的 NSTimeZone 并包含了设备所设定的时区信息。

最终,这个运用会向操控台输出两行内容,包含时区称号和 UTC 的小时偏移量:

Timezone name: Europe/Copenhagen
Timezone offset GMT: 2 hours

在 Dart 中操作时区的比如

让咱们用 Dart 和 Objective-C 的互操作来重复一遍刚刚的完成。

首要经过 Dart 指令行创立一个运用:

$ dart create timezones

接着,在你的 pubspec 文件里参加 ffigen 的装备参数,这些装备会在 headers 里设定头文件路径,而且罗列出要生成的包装类 (wrapper) 的 Objective-C 接口:

ffigen:
  name: TimeZoneLibrary
  language: objc
  output: "foundation_bindings.dart"
  exclude-all-by-default: true
  objc-interfaces:
    include:
      - "NSTimeZone"
  headers:
    entry-points:
      - "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/
         Headers/NSTimeZone.h"

这会为 NSTimeZone.h 这个头文件装备 Objective-C 绑定,而且仅包含 NSTimeZone 接口中的 API,然后运行下面代码生成包装类:

$ dart run ffigen

这个指令会创立一个包含了各种 API 绑定的新 dart 文件 foundation_bindings.dart,调用这个文件之后,咱们就能够来写 Dart 主办法 (main) 了,这个办法「镜像」了 Objective-C 的代码,如下:

void main(List<String> args) async {
  const dylibPath =
      '/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation';
  final lib = TimeZoneLibrary(DynamicLibrary.open(dylibPath));
  final timeZone = NSTimeZone.getLocalTimeZone(lib);
  if (timeZone != null) {
    print('Timezone name: ${timeZone.name}');
    print('Offset from GMT: ${timeZone.secondsFromGMT / 60 / 60} hours');
  }
}

这样就能够啦,这个新特性从 Dart 2.18 开端以实验性的支撑开端供给,它增强了 Dart 的根底互操作特性,能够直接在 Dart 代码里或许经过 Flutter 插件来调用 macOS 和 iOS API 了。

咱们十分欢迎开发者们的反应,你能够经过咱们的 GitHub Issue 提出反应主张,让咱们知道哪些已然做的很好了、哪些地方尚有待改善,以及任何你遇到的问题。了解互操作性的更多信息,能够参阅 Dart 文档: 运用 package:ffigen 来进行与 Objective-C 和 Swift 的互操作。

渠道特定的 http 库

Dart 自带一个通用的、可适用于多个渠道的 http 库,运用这个库进行网络恳求可免于考虑各个渠道的不同状况。但有些时分,开发者们可能会想在某个渠道运用这个渠道的网络恳求 API 来进行构建。

比如,Apple 的网络恳求库 NSURLSession 能够限定仅在 Wi-Fi 下拜访或需求 VPN 才能衔接。为了支撑这些用例,咱们创立了一个新的网络恳求的 package: cupertino_http,它根据上一节说到的新的 Objective-C 互操作,并从 Apple Foundation 库中网络恳求库中「提取」了很多的 API。

cupertino_http 示例

这个比如里,Flutter 运用的 HTTP 客户端在 macOS 和 iOS 上运用了 cupertino_http,在其他渠道中仍运用普通的 dart:io库:

late Client client;
if (Platform.isIOS || Platform.isMacOS) {
  final config = URLSessionConfiguration.ephemeralSessionConfiguration()
    ..allowsCellularAccess = false
    ..allowsExpensiveNetworkAccess = false;
  client = CupertinoClient.fromSessionConfiguration(config);
} else {
  client = Client(); // 运用根据 dart:io 的 HTTP 客户端
}

像这样的初始装备完成之后,运用就会在不同渠道上履行特定的网络恳求,比如现在的 get() 恳求类似于下面这样:

final response = await get(
  Uri.https(
    'www.googleapis.com',
    '/books/v1/volumes',
    {'q': 'HTTP', 'maxResults': '40', 'printType': 'books'},
  ),
);

当无法运用通用的接口时,你能够经过 cupertino_http 来调用 Apple 的网络恳求 API:

final session = URLSession.sessionWithConfiguration(
    URLSessionConfiguration.backgroundSession('com.example.bgdownload'),
    onFinishedDownloading: (s, t, fileUri) {
      actualContent = File.fromUri(fileUri).readAsStringSync();
    });
final task = session.downloadTaskWithRequest(
    URLRequest.fromUrl(Uri.https(...))
    ..resume();

多渠道运用中运用特定渠道的网络

咱们的规划方针仍旧是尽可能坚持运用的多渠道通用性,因此咱们为 http API 保存了多渠道通用的根底网络恳求的等操作,而且能够经过装备文件在不同渠道装备网络恳求库。开发者们能够运用 package:http 的 Client API来减少编写渠道特定的代码,它能够按照渠道进行装备并以独立于渠道的办法运用。

Dart 2.18 对 package:http Client API 供给了特定渠道 http 库的实验性支撑:

  • 在 macOS / iOS 运用根据 NSURLSession
  • 在 Android 上运用根据 Cronet,Cronet 是一个在 Android 上十分盛行的网络恳求库

将一个通用的 Client API 与几个不同的网络恳求完成结合在一同能够让你取得两方面的好处,既能够运用渠道特定的行为,一起也仍然在保护同一组共享的网络恳求资源。咱们期望 在 GitHub 上收到咱们的反应。

增强类型揣度

Dart 运用了许多通用办法,试想这个能够将调集元素转换为一个单一值的 fold办法。下面是一个对调集中的数字进行求和的比如:

List<int> numbers = [1, 2, 3];
final sum = numbers.fold(0, (x, y) => x + y);
print(‘The sum of $numbers is $sum’);

在 Dart 2.17 之前这个办法会返回一个类型过错:

line 2 • The operator ‘+’ can’t be unconditionally invoked because the receiver can be ‘null’.

Dart 无法结合多个参数之间的信息进行类型揣度。这导致了 x 类型具有不确定性。要纠正这个潜在的过错,你需求指定类型:

final sum = numbers.fold(0, (int x, int y) => x + y)

Dart 2.18 增强了类型揣度。在前面样例中,Dart 将会进行静态剖析,并揣度出 x 和 y 都是非空的整型。这个改动能够让你在保存强类型揣度带来的稳健性的一起编写出更加简洁的 Dart 代码。

异步函数功用增强

这个版别的 Dart 优化了 Dart VM 履行 async 以及 async*/sync* 的办法。这会减缩代码体积:在 Google 的两个大型运用上,咱们看到 AOT snapshot 产物巨细减少大约了 10% 左右。一起在咱们的微基准测验上也反映出了功用的提高。

VM 中还包含了一些额定的小的行为改变,了解更多请检查 发行注记。

Pub.dev 网站的改善

结合 2.18 版别发布的改动,咱们在 pub.dev 这个 package 生态网站上也带来了两个新的改动。

通常状况下,个人的 package 开发者会运用业余时刻保护并发布新的 package,这可能会消耗他们很多的时刻和资源。为方便其他运用者进行资助,咱们在 pubspec 中支撑了全新的 funding 标签,package 开发者能够用它列出一个或多个资助其继续开发的链接,这些链接会展示在 pub.dev 网站的侧栏中。

Dart 2.18 正式发布

了解更多请拜访 pubspec 文档。

此外,咱们也期望促进开源 package 的丰厚生态,为了突出这一点,pub.dev 上的自动评分系统会为运用了 OSI 同意的许可证 的 package 额定奖赏 10 分。

一些破坏性改动

Dart 特别重视简略性和可学习性,因此在添加新功用时,咱们也一直小心谨慎。坚持简略的一种做法是移除很少被运用或现已有更好的替代品的旧功用和 API。Dart 2.18 清理了这类条目,并包含少数的破坏性改动:

  • 咱们在 2020 年 10 月添加了统一的 dart CLI 开发者工具。在 2.18 中,咱们完成了此过渡。此版别移除了最终两个已弃用的指令行工具: dart2js (更换为运用 dart compile js) 和 dartanalyzer (更换为运用 dart analyze)。
  • 跟着言语版别操控的引进,pub 指令会生成一个新的解析文件: .dart_tool/package_config.json (之前运用的 .packages 格局的文件不能包含版别),现在咱们现已中止运用 .packages 文件了,如果你有任何 .packages 文件,你能够删除它们。
  • 非承继自 Object 的类不能再作为 Mixin 被运用 (破坏性改动 #48167),这种行为从未有意发起。
  • dart:ioRedirectExceptionuri 特点已更改为可为空 (nullable) (破坏性改动 #49045)。
  • dart:io 网络恳求 API 中遵循 SCREAMING_SNAKE 约好的常量已被移除 (破坏性改动 #34218),请改用相应的 lowerCamelCase 常量。
  • Dart VM 在退出时不再恢复初始终端设置,更改标准输入设置 lineModeechoMode 的程序现在负责在程序退出时恢复设置 (破坏性改动 #45630)。

空安全更新

空安全自 2020 年 11 月 Beta 版发布、2021 年 3 月跟着 Dart 2.12正式推出以来,咱们很快乐看到空安全已被广泛运用。

首要,pub.dev 上大部分盛行 package 的开发者都已搬迁到了空安全。咱们的剖析表明,最常用的 package 前 250 已悉数支撑空安全,前 1,000 中也有 98% 现已支撑空安全。

其次,大部分开发者现已在具有彻底空安全性的代码库中开发。这一点至关重要,因为在将所有代码和所有依靠项 (包含传递性) 搬迁之前,Dart 健全的空安全性 并不会发挥作用,咱们正在经过 flutter run 指令的遥测来盯梢这一点。

下图展示了 flutter run 指令履行中非健全 (Unsound) 和健全 (Sound) 的空安全的比照状况。在引进空安全之前,两者都为零。随后非健全的空安全快速增长,此时运用开端逐步搬迁到空安全,开发者先进行了部分搬迁,但有些部分仍然需求搬迁。一段时刻过后,咱们能够看到健全的空安全曲线稳定增长,到上月底,与非健全的空安全相比,健全的空安全履行量多出了四倍。咱们期望在接下来的几个季度中,咱们将看到健全空安全抵达 100%!

Dart 2.18 正式发布

重要的空安全路线图更新

一起支撑非健全和健全的空安全性不可避免地会添加开支和复杂性。

首要,Dart 开发者需求学习和了解这两种形式。每当阅览一段 Dart 代码时,都需求 检查言语版别。

其次,在编译器和运行时一起支撑这两种形式也会减慢 Dart SDK 支撑新功用的发展。

根据非健全空安全的开支和上一节中说到的十分可观的统计数据,咱们的方针是过渡到仅支撑健全的空安全,并中止支撑非空安全和非健全的空安全形式,咱们暂时将其定于 2023 年年中发布。

这将意味着中止对 Dart 2.11 及更早版别的支撑。具有 SDK 束缚且下限小于 2.12 的 pubspec 将不再在 Dart 3 及更高版别中解析。在包含言语标记的源代码中,如果设置为小于 2.12 (例如 //@dart=2.9) 则会失利。

如果你已搬迁到健全的空安全,你的代码将在 Dart 3 中以彻底的空安全作业。如果你还没有,咱们的主张是请当即着手开端搬迁。了解有关这些更改的更多信息,请参阅 这个议题。

总结

与 Objective-C 和 Swift 等互操作、网络恳求库、Dart 编程言语的类型揣度以及 pub.dev 的更新等现已正式可用。开端体会,请下载最新的 Dart 2.18 正式版,或许直接在 Flutter 3.3 中体会,也能够直接在 DartPad 中体会 Dart 编程言语。

最终就是空安全的搬迁,请即刻着手搬迁,与咱们共同构建和体会拥有健全空安全特性的 Dart 编程言语!

原文链接:

medium.com/dartlang/da…

本地化: CFUG 团队: @chenglu、@Vadaski、@MeandNi、@Realank

中文链接:

flutter.cn/posts/dart-…