Flutter 陈航 00-课程介绍 环境 Dart 语言概览
目录
Flutter 核心技术与实战
作者:陈航,前美团点评高级技术专家
陈航,前美团点评高级技术专家,美团外卖商家业务大前端技术负责人。近十年来,一直工作于大前端相关领域,曾分别探索并大规模落地了以 React Native 和 Flutter 为代表的跨平台方案,是美团点评最早落地 Flutter 线上大规模应用的发起者和推动者之一。
Flutter 是 Google 推出的全新跨平台移动开发框架。因为出色的性能、高效的开发方式等原因,Flutter 正被越来越多的开发者和组织采用。
Flutter 极有可能成为跨平台开发的终极解决方案。
开篇词 | 大前端都应该学习 Flutter
当下是移动互联网的时代,也是大前端技术紧密整合的时代。而移动系统与终端设备的碎片化,让我们一直头痛于在不同平台上开发和维护同一个产品的成本问题:使用原生方式来开发 App,不仅要分别针对 iOS 和 Android 平台,使用不同的语言
实现同样的产品功能,还要对不同的终端设备和不同的操作系统
进行功能适配。
这对中小型团队而言无疑是非常大的负担,也无形中拖慢了追求“小步快跑”,以快速应对市场变化的互联网产品交付节奏。
为解决这一问题,各类打着“一套代码,多端运行”口号的跨平台开发方案,如雨后春笋般涌现,React Native 就是其中的典型代表。
React Native 希望开发者能够在性能、展示、交互能力和迭代交付效率之间做到平衡。它在 Web 容器方案的基础上,优化了加载、解析和渲染这三大过程,以相对简单的方式支持了构建移动端页面必要的 Web 标准,保证了便捷的前端开发体验;并且在保留基本渲染能力的基础上,用原生自带的 UI 组件承载界面渲染,从而保证了良好的渲染性能。
但是,由于 React Native 的技术方案所限,使用原生控件承载界面渲染,在牺牲了部分 Web 标准灵活性的同时,固然解决了不少性能问题,但也引入了新的问题:除开通过 JavaScript 虚拟机进行原生接口的调用,而带来的通信低效不谈,由于框架本身不负责渲染,而是由原生代理,因此我们还需要面对大量平台相关的逻辑。
而随着系统版本和 API 的变化,我们还需要处理不同平台的原生控件渲染能力上的差异,修复各类怪异的 Bug。
这都使 React Native 的跨平台特性被大打折扣:要用好 React Native,除了掌握这个框架外,开发者还必须同时熟悉 iOS 和 Android 系统。这无疑给开发者提出了更多挑战,也是很多开发者们对 React Native 又爱又恨的原因。在这其中,也有一些团队决定放弃 React Native 回归原生开发,Airbnb 就是一个例子。
而我们本次课程的主角 Flutter,则完全不同于 React Native。
它开辟了全新的思路,提供了一整套从底层渲染逻辑到上层开发语言的完整解决方案:视图渲染完全闭环在其框架内部,不依赖于底层操作系统提供的任何组件,从根本上保证了视图渲染在 Android 和 iOS 上的高度一致性;Flutter 的开发语言 Dart,是 Google 专门为(大)前端开发量身打造的专属语言,借助于先进的工具链和编译器,成为了少数同时支持 JIT 和 AOT 的语言之一,开发期调试效率高,发布期运行速度快、执行性能好,在代码执行效率上可以媲美原生 App。而这与 React Native 所用的只能解释执行的 JavaScript,又拉开了性能差距。
正是因为这些革命性的特点,Flutter 在正式版发布半年多的时间里,在 GitHub 上的 Star 就已经超过了 68,000,与已经发布 4 年多的、拥有 78,000 Star 的行业领头羊 React Native 的差距非常小。同时,在阿里闲鱼、今日头条等知名商用案例的加持下,Flutter 的热度不断攀升。
现在看来,在 Google 的强力带动下,Flutter 极有可能成为跨平台开发领域的终极解决方案。在过去的大半年时间里,我曾面试了 20 多位初、中、高级候选人,包括前端、Android、iOS 的开发者。当问到最近想学习什么新技术时,超过 80% 的候选人告诉我,他会学习或正在学习 Flutter。
不过坦白讲,相比其他跨平台技术,Flutter 的学习成本相对较高。我听过很多(大)前端开发者反馈:Flutter 从语言到开发框架都是全新的,技术栈的积累也要从头开始,学不动了。
学习成本高,这也是目前大多数开发者犹豫是否要跟进这个框架的最重要原因。对此,我感同身受。
但其实,大前端各个方向的工作有很多相似、相通之处。面对业务侧日益增多的需求,作为大前端团队的负责人,我曾在不同时期带领团队分别探索并大规模落地了以 React Native 和 Flutter 为代表的跨平台方案,也是美团最早落地 Flutter 线上大规模应用的发起者和推动者之一。
在探索并大规模落地 Flutter 的过程中,我阅读过大量关于 Flutter 的教程和技术博客,但我发现很多文章的学习门槛都比较高,而且过于重视应用层 API 各个参数的介绍或实现细节,导致很多从其他平台转来的开发者无从下手,只能依葫芦画瓢,却不知道为什么要“画瓢”,无法与自身的经验串联进而形成知识体系。这无疑又增加了学习门槛,加长了学习周期。
那么,Flutter 到底该怎么学?真的要从头开始么?
虽然 Flutter 是全新的跨平台技术,但其背后的框架原理和底层设计思想,无论是底层渲染机制与事件处理方式,还是组件化解耦思路,亦或是工程化整体方法等,与原生 Android/iOS 开发并没有本质区别,甚至还从 React Native 那里吸收了不少优秀的设计理念。就连 Flutter 所采用的 Dart 语言,关于信息表达和处理的方式,也有诸多其他优秀编程语言的影子。
整体来说,专栏主要包括以下五大部分内容:
- Flutter 开发起步。我会从跨平台方案发展历史出发,与你介绍 Flutter 的诞生背景、基本原理,并带你体验一下 Flutter 代码是如何在原生系统上运行的。
- Dart 基础。我会从 Dart 与其他编程语言的设计思想对比出发,与你讲述 Dart 设计的关键思路以及独有特性,并通过一个综合案例带你去实践一下。
- Flutter 基础。我将通过 Flutter 与原生系统对应概念对比,与你讲述 Flutter 独有的概念和框架设计思路。学完这个模块,你就可以开发出一个简单的 App 了。
- Flutter 进阶。我会与你讲述 Flutter 开发中的一些疑难问题、高级特性及其背后原理,帮助你在遇到问题时化被动为主动。
- Flutter 综合应用。我将和你聊聊在企业级应用迭代的生命周期中,如何从效率和质量这两个维度出发,构建自己的 Flutter 开发体系。
01 | 预习篇 · 搭建 Flutter 环境
echo $PATH
echo $PATH | tr ':' '\n'
echo $PATH | tr ':' '\n' | grep flutter
env | grep flutter
flutter devices
flutter emulators
flutter doctor
flutter run
adb shell ip route | awk '{print $9}' # 获取手机的 ip【192.168.100.31】
adb tcpip 5555 # 手机端启用 tcpip,且指定端口
adb connect 192.168.100.31:5555 # 电脑端连接指定 ip:端口
scrcpy -S --always-on-top # 开始投屏,关闭屏幕,窗口最前
PUB_HOSTED_URL=https://pub.flutter-io.cn
FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
02 | 预习篇 · Dart 语言概览
Dart 是什么?
2011
年 10 月,Google 发布了 Dart。Dart 是为了解决 JavaScript 存在的、在语言本质上无法改进的缺陷。
那么,JavaScript 到底有哪些问题和缺陷呢?JavaScript 之父布兰登 · 艾克(Brendan Eich)曾在一次采访中说,JavaScript“几天就设计出来了”。
概括来说,他的设计思路是这样的:
- 借鉴 C 语言的基本语法
- 借鉴 Java 语言的数据类型和内存管理机制
- 借鉴 Scheme 语言,将函数提升到第一等公民的地位
- 借鉴 Self 语言,使用基于原型(prototype)的继承机制
所以,JavaScript 实际上是两类编程语言风格的混合产物:(简化的)函数式编程风格,与(简化的)面向对象编程风格。
由于设计时间太短,一些细节考虑得不够严谨,导致后来很长一段时间,使用 JavaScript 开发的程序混乱不堪。出于对 JavaScript 的不满,Google 的程序员们决定自己写一个新语言来换掉它,所以 Dart 的最初定位也是一种运行在浏览器中的脚本语言。
为了推广 Dart,Google 甚至将自己的 Chrome 浏览器内置了 Dart VM,可以直接高效地运行 Dart 代码。而对于普通浏览器来说,Google 也提供了一套能够将 Dart 代码编译成 JavaScript 代码的转换工具。这样一来,开发者们就可以毫无顾虑地使用 Dart 去开发了,而不必担心兼容问题。再加上出身名门,Dart 在一开始就赢得了部分前端开发者的关注。
但,JavaScript 的生命力似乎比预想的更强大。
原本 JavaScript 只能在浏览器中运行,但 Node.js 的出现让它开始有能力运行在服务端,很快手机应用与桌面应用也成为了 JavaScript 的宿主容器,一些明星项目比如 React、React Native、Vue、Electron、NW(node-webkit)等框架如雨后春笋般崛起,迅速扩展了它的边界。
于是,JavaScript 成为了前后端通吃的全栈语言,前端的开发模式也因此而改变,进入了一个新的世界。就如同 Atwood 定律描述的:凡是能用 JavaScript 写出来的系统,最终都会用 JavaScript 写出来(Any application that can be written in JavaScript, will eventually be written in JavaScript.)。
JavaScript 因为 Node.js 焕发了第二春,而 Dart 就没有那么好的运气了。由于缺少顶级项目的使用,Dart 始终不温不火。2015 年,在听取了大量开发者的反馈后,Google 决定将内置的 Dart VM 引擎从 Chrome 移除,这对 Dart 的发展来说是重大挫折,替代 JavaScript 就更无从谈起了。
但,Dart 也借此机会开始转型:在 Google 内部孵化了移动开发框架 Flutter,弯道超车进入了移动开发的领域;而在 Google 未来的操作系统 Fuchsia 中,Dart 更是被指定为官方的开发语言。
也正是因为使用者不多、历史包袱少,所以在经历了这么多的故事后,Dart 可以彻底转变思路,成为专注大前端与跨平台生态的语言。
接下来,我们就从 Flutter 开发的视角,聊聊 Dart 最重要的核心特性。
Dart 的特性
JIT 与 AOT
借助于先进的工具链和编译器,Dart 是少数同时支持 JIT(Just In Time,即时编译)和 AOT(Ahead of Time,运行前编译)的语言之一。
语言在运行之前通常都需要编译,JIT 和 AOT 则是最常见的两种编译模式。
- JIT 在运行时即时编译,在开发周期中使用,可以动态下发和执行代码,开发测试效率高,但运行速度和执行性能则会因为运行时即时编译受到影响。
- AOT 即提前编译,可以生成被直接执行的二进制代码,运行速度快、执行性能表现好,但每次执行前都需要提前编译,开发测试效率低。
总结来讲,在开发期使用 JIT 编译,可以缩短产品的开发周期。Flutter 最受欢迎的功能之一热重载,正是基于此特性。而在发布期使用 AOT,就不需要像 React Native 那样在跨平台 JavaScript 代码和原生 Android、iOS 代码之间建立低效的方法调用映射关系。所以说,Dart 具有运行速度快、执行性能好的特点。
那么,如何区分一门语言究竟是 AOT 还是 JIT 呢?通常来说,看代码在执行前是否需要编译即可。如果需要编译,通常属于 AOT;如果不需要,则属于 JIT。
AOT 的典型代表是 C/C++
,它们必须在执行前编译成机器码;而 JIT 的代表,则包括了如 JavaScript、Python 等几乎所有的脚本语言。
内存分配与垃圾回收
Dart VM 的内存分配策略比较简单,创建对象时只需要在堆上移动指针,内存增长始终是线性的
,省去了查找可用内存的过程。
在 Dart 中,并发是通过 Isolate 实现的。Isolate 是类似于线程但不共享内存、独立运行的 worker。这样的机制,就可以让 Dart 实现无锁的快速分配
。
Dart 的垃圾回收,则是采用了多生代算法。新生代在回收内存时采用“半空间”机制,触发垃圾回收时,Dart 会将当前半空间中的“活跃”对象拷贝到备用空间,然后整体释放当前空间的所有内存。回收过程中,Dart 只需要操作少量的“活跃”对象,没有引用的大量“死亡”对象则被忽略,这样的回收机制很适合 Flutter 框架中大量 Widget 销毁重建
的场景。
单线程模型
支持并发执行线程的高级语言(比如,C++、Java、Objective-C),大都以抢占式的方式切换线程,即:每个线程都会被分配一个固定的时间片来执行,超过了时间片后线程上下文将被抢占后切换。如果这时正在更新线程间的共享资源,抢占后就可能导致数据不同步的问题。
解决这一问题的典型方法是,使用锁来保护共享资源,但锁本身又可能会带来性能损耗,甚至出现死锁等严重的问题。
这时,Dart 是单线程模型的优势就体现出来了,因为它天然不存在资源竞争和状态同步的问题。这就意味着,一旦某个函数开始执行,就将执行到这个函数结束,而不会被其他 Dart 代码打断。
所以,Dart 中并没有线程,只有 Isolate(隔离区)。Isolates 之间不会共享内存,就像几个运行在不同进程中的 worker,通过事件循环(Event Looper)在事件队列(Event Queue)上传递消息通信。
无需单独的声明式布局语言
在 Flutter 中,界面布局直接通过 Dart 编码来定义。
Dart 声明式编程布局易于阅读和可视化,使得 Flutter 并不需要类似 JSX 或 XML 这样额外的声明式布局语言。所有的布局都使用同一种格式,也使得 Flutter 很容易提供高级工具使布局更简单。
开发过程也不需要可视化界面构建器,因为热重载可以让我们立即在手机上看到运行效果。
Dart 的未来
Dart 是一个优秀而年轻的现代语言,但一种编程语言并不是搞定了引擎和开发者接口就算完成了,而是必须在这个语言得以立足的库、框架、 应用程序等生态都成熟起来之后,其价值才会真正开始体现。而要走到这一步,通常需要花上数年的时间。
目前,基于 Dart 语言的第三方库还很少,并且质量一般,不过值得庆幸的是,因为 Flutter 和 Fuchsia 的推动,Dart SDK 更新迭代的速度快了很多,开发者的热情也急剧增长,Dart 生态增速很快。
相信随着 Google 新系统 Fuchsia 的发布,Flutter 和 Dart 会以更迅猛的速度释放它们的力量,而 Google 统一前后端开发技能栈的愿望也会在一定程度上得以实现。
2022-05-16
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/16275441.html