互操作性加强、平台特定的网络组件、优化类型推断,以及空平安语言里程碑的近期更新
文 / Michael Thomsen, Google Flutter & Dart 产品经理
Dart 2.18 稳定版也随着 Flutter 3.3 稳定版一起公布,本次更新带来了 Dart 与 Objective-C \& Swift 互操作个性的预览版,以及依据这个个性构建的 iOS/macOS 网络组件的 package。新的 Dart 还包含泛型办法的类型推断优化、异步代码的性能晋升、pub.dev 新的性能,以及对咱们工具和外围库的一些调整。
文章最初咱们也给出了最新的空平安迁徙状态状况数据,以及最终齐全实现 Dart 空平安个性路线图的一个重要更新,请务必读到最初。
Dart 与 Objective-C 和 Swift 互调
早在 2020 年的时候,咱们公布了内部性能接口 (FFI) 用于调用原生 C 语言接口的预览,并在 2021 年的 Dart 2.12 中正式公布。自那时起,大量的 package 借助于 FFI 的劣势与现有的原生 C 语言接口 API 集成,举一些例子,比方 file_picker
、printing
、win32
、objectbox
、realm
、isar
、tflite_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
网站的侧栏中。
理解更多请拜访 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:io
中RedirectException
的uri
属性已更改为可为空 (nullable) (破坏性改变 #49045)。dart:io
网络申请 API 中遵循SCREAMING_SNAKE
约定的常量已被移除 (破坏性改变 #34218),请改用相应的 lowerCamelCase 常量。- Dart VM 在退出时不再复原初始终端设置,更改规范输出设置
lineMode
和echoMode
的程序当初负责在程序退出时复原设置 (破坏性改变 #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 开发者须要学习和了解这两种模式。每当浏览一段 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 编程语言!
原文链接 :
https://medium.com/dartlang/dart-2-18-f4b3101f146c
本地化 : CFUG 团队: \@chenglu、\@Vadaski、\@MeandNi、\@Realank
中文链接 :
https://flutter.cn/posts/dart-2-18