@qinyun
2018-03-09T10:03:37.000000Z
字数 8675
阅读 2237
Flutter
Dart
移动开发
作者|Wm Leler
译者|王纯超
许多语言学家认为,一个人说的自然语言会影响他们的思维方式。这个理论适用于计算机语言吗?使用不同编程语言编程的程序员针对问题想出的解决方案经常完全不同。举一个极端的例子,为了程序结构更加清晰,计算机科学家取消了goto语句(这与小说《1984》中的极权主义领导者从自然语言中删除异端词语以消除思维犯罪不太一样,但道理就是这样)。
这与Flutter和Dart有什么关系?确实有关系。早期的Flutter团队评估了十多种语言,并选择了Dart,因为它符合他们构建用户界面的方式。
Dart是开发人员喜欢Flutter的一大原因。如以下推文:
@flutterio got me to look at @dart, and I’m glad I took it for a spin. #Dart is an awesome language, and #flutterio takes it even further, to mobile devices <3
以下是使Dart成为Flutter不可或缺的一部分的特性:
并非所有这些功能都是Dart独有的,但它们的组合却恰到好处,使Dart在实现Flutter方面独一无二。因此,没有Dart,很难想象Flutter像现在这样强大。
本文接下来将深入探讨使Dart成为实现Flutter的最佳语言的许多特性(包括其标准库)。
[如果你已经了解静态语言与动态语言、AOT与JIT编译以及虚拟机等主题,可以跳过本节。]
历史上,计算机语言分为两组:静态语言(例如,Fortran和C,其中变量类型是在编译时静态指定的)和动态语言(例如,Smalltalk和JavaScript,其中变量的类型可以在运行时改变)。静态语言通常编译成目标机器的本地机器代码(或汇编代码)程序,该程序在运行时直接由硬件执行。动态语言由解释器执行,不产生机器语言代码。
当然,事情后来变得复杂得多。虚拟机(VM)的概念开始流行,它其实只是一个高级的解释器,用软件模拟硬件设备。虚拟机使语言移植到新的硬件平台更容易。因此,VM的输入语言常常是中间语言。例如,一种编程语言(如Java)被编译成中间语言(字节码),然后在VM(JVM)中执行。
另外,现在有即时(JIT)编译器。JIT编译器在程序执行期间运行,即时编译代码。原先在程序创建期间(运行时之前)执行的编译器现在称为AOT编译器。
一般来说,只有静态语言才适合AOT编译为本地机器代码,因为机器语言通常需要知道数据的类型,而动态语言中的类型事先并不确定。因此,动态语言通常被解释或JIT编译。
在开发过程中AOT编译,开发周期(从更改程序到能够执行程序以查看更改结果的时间)总是很慢。但是AOT编译产生的程序可以更可预测地执行,并且运行时不需要停下来分析和编译。AOT编译的程序也更快地开始执行(因为它们已经被编译)。
相反,JIT编译提供了更快的开发周期,但可能导致执行速度较慢或时快时慢。特别是,JIT编译器启动较慢,因为当程序开始运行时,JIT编译器必须在代码执行之前进行分析和编译。研究表明,如果开始执行需要超过几秒钟,许多人将放弃应用。
以上就是背景知识。将AOT和JIT编译的优点结合起来不是很棒吗?请继续阅读。
在创造Dart之前,Dart团队成员在高级编译器和虚拟机上做了开创性的工作,包括动态语言(如JavaScript的V8引擎和Smalltalk的Strongtalk)以及静态语言(如用于Java的Hotspot编译器)。他们利用这些经验使Dart在编译和执行方面非常灵活。
Dart是同时非常适合AOT编译和JIT编译的少数语言之一(也许是唯一的“主流”语言)。支持这两种编译方式为Dart和(特别是)Flutter提供了显著的优势。
JIT编译在开发过程中使用,编译器速度特别快。然后,当一个应用程序准备发布时,它被AOT编译。因此,借助先进的工具和编译器,Dart具有两全其美的优势:极快的开发周期、快速的执行速度和极短启动时间。
Dart在编译和执行方面的灵活性并不止于此。例如,Dart可以编译成JavaScript,所以浏览器可以执行。这允许在移动应用和网络应用之间重复使用代码。开发人员报告他们的移动和网络应用程序之间的代码重用率高达70%。通过将Dart编译为本地代码,或者编译为JavaScript并将其与node.js一起使用,Dart也可以在服务器上使用。
最后,Dart还提供了一个独立的虚拟机(本质上就像解释器一样),虚拟机使用Dart语言本身作为其中间语言。
Dart可以进行高效的AOT编译或JIT编译、解释或转译成其他语言。Dart编译和执行不仅非常灵活,而且速度特别快。
下一节将介绍Dart编译速度的颠覆性的例子。
Flutter最受欢迎的功能之一是其极速热重载。在开发过程中,Flutter使用JIT编译器,通常可以在一秒之内重新加载并继续执行代码。只要有可能,应用程序状态在重新加载时保留下来,以便应用程序可以从停止的地方继续。
除非自己亲身体验过,否则很难理解在开发过程中快速(且可靠)的热重载的重要性。开发人员报告称,它改变了他们创建应用的方式,将其描述为像将应用绘制成生活一样。
以下是一位移动应用程序开发人员对Flutter热重载的评价:
我想测试热重载,所以我改变了颜色,保存修改,结果……就喜欢上它了❤!
这个功能真的很棒。我曾认为Visual Studio中编辑和继续(Edit & Continue)很好用,但这简直令人惊叹。有了这个功能,我认为移动开发者的生产力可以提高两倍。
这对我来说真的是翻天覆地的变化。当我部署代码并花费很长时间时,我分心了,做了其他事情,当我回到模拟器/设备时,我就忘了想测试的内容。有什么比花5分钟将控件移动2px更令人沮丧?有了Flutter,这不再存在。
Flutter的热重载也使得尝试新想法或尝试替代方案变得更加容易,从而为创意提供了巨大的推动力。
到目前为止,我们讨论了Dart给开发人员带来的好处。下一节将介绍Dart如何使创建满足用户需求的顺畅的应用程序更加轻松。
应用程序速度快很不错,但流畅则更加了不起。即使是一个超快的动画,如果它不稳定,也会看起来很糟糕。但是,防止卡顿可能很困难,因为因素太多。Dart有许多功能可以避免许多常见的导致卡顿的因素。
当然,像任何语言一样,Flutter也可能写出来卡顿的应用程序;Dart通过提高可预测性,帮助开发人员更好地控制应用程序的流畅性,从而更轻松地提供最佳的用户体验。
以60fps运行,使用Flutter创建的用户界面的性能远远优于使用其他跨平台开发框架创建的用户界面。
不仅仅比跨平台的应用程序好,而且和最好的原生应用程序一样好:
UI像黄油一样顺滑……我从来没有见过这样流畅的Android应用程序。
我们讨论过一个有助于保持顺畅的特性,那就是Dart能AOT编译为本地机器码。预编译的AOT代码比JIT更具可预测性,因为在运行时不需要暂停执行JIT分析或编译。
然而,AOT编译代码还有一个更大的优势,那就是避免了“JavaScript桥梁”。当动态语言(如JavaScript)需要与平台上的本地代码互操作时,它们必须通过桥进行通信,这会导致上下文切换,从而必须保存特别多的状态(可能会存储到辅助存储)。这些上下文切换具有双重打击,因为它们不仅会减慢速度,还会导致严重的卡顿。
注意:即使编译后的代码也可能需要一个接口来与平台代码进行交互,并且这也可以称为桥,但它通常比动态语言所需的桥快几个数量级。另外,由于Dart允许将小部件等内容移至应用程序中,因此减少了桥接的需求。
大多数支持多个并发执行线程的计算机语言(包括Java、Kotlin、Objective-C和Swift)都使用抢占式来切换线程。每个线程都被分配一个时间分片来执行,如果超过了分配的时间,线程将被上下文切换抢占。但是,如果在线程间共享的资源(如内存)正在更新时发生抢占,则会导致竞态条件。
竞态条件具有双重不利,因为它可能会导致严重的错误,包括应用程序崩溃并导致数据丢失,而且由于它取决于独立线程的时序,所以它特别难以找到并修复。在调试器中运行应用程序时,竞态条件常常消失不见。
解决竞态条件的典型方法是使用锁来保护共享资源,阻止其他线程执行,但锁本身可能导致卡顿,甚至更严重的问题(包括死锁和饥饿)。
Dart采取了不同的方法来解决这个问题。Dart中的线程称为isolate,不共享内存,从而避免了大多数锁。isolate通过在通道上传递消息来通信,这与Erlang中的actor或JavaScript中的Web Worker相似。
Dart与JavaScript一样,是单线程的,这意味着它根本不允许抢占。相反,线程显式让出(使用async/await、Future和Stream)CPU。这使开发人员能够更好地控制执行。单线程有助于开发人员确保关键功能(包括动画和转场)完成而无需抢占。这通常不仅是用户界面的一大优势,而且还是客户端——服务器代码的一大优势。
当然,如果开发人员忘记了让出CPU的控制权,这可能会延迟其他代码的执行。然而我们发现,忘记让出CPU通常比忘记加锁更容易找到和修复(因为竞态条件很难找到)。
另一个严重导致卡顿的原因是垃圾回收。事实上,这只是访问共享资源(内存)的一种特殊情况,在很多语言中都需要使用锁。但在回收可用内存时,锁会阻止整个应用程序运行。但是,Dart几乎可以在没有锁的情况下执行垃圾回收。
Dart使用先进的分代垃圾回收和对象分配方案,该方案对于分配许多短暂的对象(对于Flutter这样的反应式用户界面来说非常完美,Flutter为每帧重建不可变视图树)都特别快速。Dart可以用一个指针凹凸分配一个对象(不需要锁)。这也会带来流畅的滚动和动画效果,而不会出现卡顿。
Dart的另一个好处是,Flutter不会从程序中拆分出额外的模板或布局语言,如JSX或XML,也不需要单独的可视布局工具。以下是一个简单的Flutter视图,用Dart编写:
new Center(child:
new Column(children: [
new Text('Hello, World!'),
new Icon(Icons.star, color: Colors.green),
])
)
Dart编写的视图及其效果
注意,可视化这段代码产生的效果是多么容易(即使你没有使用Dart的经验)。
Dart 2即将发布,这将变得更加简单,因为new
和const
关键字变得可选,所以静态布局看起来像是用声明式布局语言编写的:
Center(child:
Column(children: [
Text('Hello, World!'),
Icon(Icons.star, color: Colors.green),
])
)
然而,我知道你可能在想什么——缺乏专门的布局语言怎么会被称为优势呢?但它确实是颠覆性的。以下是一名开发人员在一篇题为“为什么原生应用程序开发人员应认真看待Flutter”的文章中写的内容。
在Flutter里,界面布局直接通过Dart编码来定义,不需要使用XML或模板语言,也不需要使用可视化设计器之类的工具。
说到这里,大家可能会一脸茫然,就像我当初的反应一样。使用可视化工具不是更容易吗?如果把所有的逻辑都写到代码里不是会让事情变复杂吗?
结果不然。天啊,它简直让我大开眼界。
首先是上面提到的热重载。
这比Android的Instant Run和任何类似解决方案不知道要领先多少年。对于大型的应用同样适用。如此快的速度,正是Dart的优势所在。
实际上,可视化编辑器就变得多余了。我一点都不怀恋XCode的自动重布局。
Dart创建的布局简洁且易于理解,而“超快”的热重载可立即看到结果。这包括布局的非静态部分。
结果,在Flutter中进行布局要比在Android/XCode中快得多。一旦你掌握了它(我花了几个星期),由于很少发生上下文切换,因此会节省大量的开销。不必切换到设计模式,选择鼠标并开始点击,然后想是否有些东西必须通过编程来完成,如何实现等等。因为一切都是程序化的。而且这些API设计得非常好。它很直观,并且比自动布局XML更强大。
例如,下面是一个简单的列表布局,在每个项目之间添加一个分隔线(水平线),以编程方式定义:
return new ListView.builder(itemBuilder: (context, i) {
if (i.isOdd) return new Divider();
// rest of function
});
在Flutter中,无论是静态布局还是编程布局,所有布局都存在于同一个位置。新的Dart工具,包括Flutter Inspector和大纲视图(利用所有的布局定义都在代码里)使复杂而美观的布局更加容易。
不,Dart(如Flutter)是完全开源的,具备清楚的许可证,同时也是ECMA标准的。Dart在Google内外很受欢迎。在谷歌内部,它是增长最快的语言之一,并被Adwords、Flutter、Fuchsia和其他产品使用;在谷歌外部,Dart代码库有超过100个外部提交者。
Dart开放性的更好指标是Google之外的社区的发展。例如,我们看到来自第三方的关于Dart(包括Flutter和AngularDart)的文章和视频源源不断,我在本文中引用了其中的一些内容。
除了Dart本身的外部提交者之外,公共Dart包仓库中还有超过3000个包,其中包括Firebase、Redux、RxDart、国际化、加密、数据库、路由、集合等方面的库。
如果没有很多程序员知道Dart,找到合格的程序员会困难吗?显然不是。Dart是一门难以置信的易学语言。事实上,已经了解Java、JavaScript、Kotlin、C#或Swift等语言的程序员几乎可以立即开始使用Dart进行编程。
一个程序员在名为“为什么Flutter 2018年将起飞”的文章中写到:
Dart是用于开发Flutter应用程序的语言,很易学。谷歌在创建简单、有文档记录的语言方面拥有丰富的经验,如Go。到目前为止,对我来说,Dart让我想起了Ruby,很高兴能够学习它。它不仅适用于移动开发,也适用于Web开发。
另一篇关于Flutter和Dart的文章,题为“为什么是Flutter而不是其他框架?”
Flutter使用由Google创建的Dart语言,老实说,我不喜欢C#或JAVA这样的强类型语言,但我不知道Dart编写代码的方式有什么与众不同。但我觉得写起来很舒服。也许是因为它非常简单易学,而且非常直观。
Dart通过广泛的用户体验研究和测试,专门设计得熟悉并易于学习。例如,在2017年上半年,Flutter团队与八位开发人员一起进行了用户体验研究。我们给他们简短地介绍了Flutter,然后给他们一个小时左右,创建了一个简单的视图。所有参与者都能够立即开始编程,即使他们以前从未使用过Dart。他们专注于写响应式视图,而不是语言。Dart直接就能上手用了。
最后,一位参与者(在任务中进展得特别快)没有提及任何有关该语言的内容,所以我们问他是否知道他正在使用哪种语言。他说不知道。语言不成问题;他在几分钟内就能用Dart编程。
学习新系统的难点通常不是学习语言,而是学习编写好代码的所有库、框架、工具、模式和最佳实践。Dart库和工具格外出色,并且文档详尽。有一篇文章宣称:“意外之喜是,他们还极其爱护代码库,并且他们拥有我见过的最好的文档。”花费在学习Dart上的时间很容易通过学习其他东西节省的时间弥补。
作为直接证据,Google内部的一个大型项目希望将其移动应用程序移植到iOS。他们即将聘请一些iOS程序员,但转而决定尝试Flutter。他们监测了让开发者上手Flutter需要多长时间。结果表明,程序员可以学会Dart和Flutter,并在三周内达到高效率。相比之下,他们之前观察到仅仅让程序员上手Android(更不用说他们必须聘用和培训iOS开发人员)需要五个星期。
最后,一家将三种平台(iOS、Android和Web)上的大型企业应用程序都迁移到Dart的公司,有一篇文章“我们为什么选择Flutter以及它如何改变我们的公司”。他们的结论:
招人变得容易多了。无论他们是来自Web、iOS还是Android,我们现在都希望接受最佳人选。
现在我们拥有3倍的工作效率,因为我们所有的团队都集中在一个代码库上。
知识共享达到前所未有的高度。
使用Dart和Flutter使他们的生产力提高到三倍。考虑到他们以前在做什么,这应该不会令人感到意外。与许多公司一样,它们利用不同的语言、工具和程序员为每个平台(Web、iOS和Android)构建独立的应用程序。切换到Dart意味着他们不再需要雇佣三种不同的程序员。而且他们很容易将现有的程序员转移到使用Dart。
他们和其他人发现,一旦程序员开始使用Flutter,他们就会爱上Dart。他们喜欢Dart的简洁和缺乏仪式。他们喜欢级联、命名参数、async/await和Stream等语言特性。而最重要的是,他们喜欢Dart带来的Flutter功能(如热重载),以及Dart帮助他们构建的美丽、高性能的应用程序。
在本文发表时,Dart 2正在发布。Dart 2专注于改善构建客户端应用程序的体验,包括加快开发人员速度、改进开发人员工具和类型安全。例如,Dart 2具有坚实的类型系统和类型推理。
Dart 2还使new
和const
关键字可选。这意味着可以在不使用任何关键字的情况下描述Flutter视图,从而减少混乱并且易于阅读。例如:
Widget build(BuildContext context) =>
Container(
height: 56.0,
padding: EdgeInsets.symmetric(horizontal: 8.0),
decoration: BoxDecoration(color: Colors.blue[500]),
child: Row(
...
),
);
Dart 2自动计算出所有的构造函数,并且“padding:”的值是一个常量。
Dart 2的改进集中在优化客户端开发。但Dart仍然是构建服务器端、桌面、嵌入式系统和其他程序的绝佳语言。
专注是一件好事。几乎所有持久受欢迎的语言都受益于非常专注。例如:
另一方面,许多语言已经明确地尝试过(并且失败了)成为完全是通用的,例如PL/1和Ada等等。最常见的问题是,如果没有重点,这些语言就成了众所周知的厨房洗碗槽。
许多使Dart成为好的客户端语言的特性也使其成为更好的服务器端语言。例如,Dart避免了抢占式多任务处理,这一点与服务器上的Node具有相同的优点,但是数据类型更好更安全。
编写用于嵌入式系统的软件也是一样的。Dart能够可靠地处理多个并发输入是关键。
最后,Dart在客户端上的成功将不可避免地引起用户对服务器上使用的更多兴趣——就像JavaScript和Node一样。为什么强迫人们使用两种不同的语言来构建客户端——服务器软件呢?
这对于Dart来说是一个激动人心的时刻。使用Dart的人喜欢它,而Dart 2中的新特性使其成为你工具库中更有价值的补充。如果你还没有使用过Dart,我希望这篇文章为你提供了有关Dart的新特性的有价值的信息,并且你会试一试Dart和Flutter。
查看英文原文:https://hackernoon.com/why-flutter-uses-dart-dd635a054ebf