一、Dart
1.Dart当中的「..」表示什么意思?
Dart当中的「..」意思是级联操作符,为了方便配置而使用。链式编程,返回的是this
「..」和「.」不同,「..」返回的相当于是this,「.」返回的则是该方法的返回值
2.Dart的作用域
Dart没有public,private等关键字,默认都是公开的,私有变量使用下划线_开头
3.Dart是不是单线程模型?是如何运行的?
Dart是单线程模型
Dart在单线程中是以消息循环机制来运行的,包含两个任务队列,一个是“微任务队列”microtask queue,一个是事件队列 event queue
当flutter应用启动后,消息循环机制便启动了,
首先会按照先进先出的顺序逐个执行微任务队列中的任务,
当微任务队列执行完后,便开始执行事件队列中的任务,事件任务执行完后在去执行微任务,
如此循环,生生不息
4. Dart是如何实现多任务并行的
Dart是单线程,不存在多线程
Flutter的多线程主要依赖Dart得并发编程、异步和事件驱动机制
在Dart中、一个isolate对象其实就是一个isolate执行环境的引用,我们都是通过当前的isolate,去控制其他的isolate完成彼此之间的交互,而当我们想要创建一个新的isolate可以使用isolate.spawn方法获取返回一个新的isolate对象,两个isolate直接使用sendPort互相发消息,而一个isolate中也存在一个与之对应的receivePort接受消息来处理
sendPort和receivePort在每个isolate都有一对,只有同一个isolate中的receivePort才能接受到当前来的sendPort发送的消息并处理
5.说一下Dart异步编程中的Future关键字
声明一个 Future 时,Dart 会将异步任务的函数执行体放入event queue,然后立即返回,后续的代码继续同步执行。
当同步执行的代码执行完毕后,event queue会按照加入event queue的顺序(即声明顺序),依次取出事件,最后同步执行 Future 的函数体及后续的操作。
默认的Future是异步运行的。如果想要我们的Future同步执行,可以通过async和await关键字:
async:用来表示函数是异步的,定义的函数会返回一个Future对象。
await:后面跟着一个Future,表示等待该异步任务完成,异步任务完成后才会继续往下执行。await只能出现在异步函数内部。能够让我们可以像写同步代码那样来执行异步任务而不使用回调的方式。
6.说一下Dart异步编程中的Stream数据流?
在Dart中,Stream和Future一样,都是用来处理异步编程的工具。
它们的区别在于,Stream可以接收多个异步结果,而Future只有一个
Stream的创建可以使用Stream.FromFutre,也可以使用StreamController来创建和控制。
普通的Stream只可以有一个订阅者,如果想要多个订阅的话,要使用asBroadcastStream()
7.Stream有哪两种订阅模式?分别怎么调用?
Stream有两种订阅模式:单订阅(single)和多订阅(broadcast)。单订阅就是只能有一个订阅者,而广播是可以多订阅者。有点累屎与消息服务(Message Service)的处理模式。单订阅类似于点对点,在订阅者出现之前会持有数据,在订阅者出现之后才转交给它。而广播类嘘发布订阅模式,可以同时有多个订阅者,当有数据时就会传递给所有的订阅者,不管当前是否有订阅者存在
Stream默认处于单订阅模式,所以同一个stream上的listen和其他大多数方法一样,只能调用一次,调用第二次就会报错。但stream可以通过transForm()方法( 返回另一个stream)进行连续调用。通过Stream.asBroadcasrStream(),可以将一个单订阅的Stream转换成一个多订阅模式的Stream,isBroadcast属性可以判断当前Stream所处的模式。
8.await for
await for 是不断从stream流中获取的数据,然后执行循环体中的操作。它一般用在stream什么时候完成,并且必须等待传递完成后才能使用,不然就会一张纸阻塞
9.说一下 mixin机制
mixin是Dart2.1加入的特性,以前版本通常使用abstract class代替
为了解决继承方面的问题而引入的机制,Dart为了多重继承,引入了mixin关键字,它最大的特殊性在于:mixin定义的类不能有构造方法,这样可以避免继承多个类而产生的父类构造方法冲突
mixin的对象是类,mixins绝不是继承,也不是接口,而是一种全新的特性,可以mixins多个类,mixins的使用需要满足一定的条件
二、Flutter
1.请简单介绍一下flutter框架,以及它的优缺点
Flutter是Google推出的一套开源跨平台UI框架,可以快速地在android、iOS和web平台上构建高质量的原生用户界面。同时,flutter还是Google新研发的Fuchsia操作系统的默认开发套件
在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的
Flutter采用现代响应式框架构建,其中心思想是使用组件来构建应用的UI。当组件的状态发生改变时,组件才会重构它的描述,Flutter会对比之前的描述,以确定底层渲染树从当前状态切换到下一个状态所需要的最小改变
优点:
热重载,利用AS直接ctl+s就可以保存并重载,模拟器立马就可以看见效果,相比原生长久的编译过程强很多
一切皆为Widget的理念,对于Flutter来说,手机应用里的所有东西都是Widget,通过可组合的空间集合、丰富的动画以及分层课扩展的架构实现了富有感染力的灵活界面设计
借助可移植的GPU加速的渲染引擎及高性能本地化代码运行时,以达到跨平台设备的高质量用户体验
简单来说就是:最终结果就是利用Flutter构建的应用在运行效率上会和原生应用差不多
缺点:
不支持热更新
三方库有限
Dart语言编写,增加学习难度,并且学习了Dart之后无其他用处,相比JS和Java来说
2.介绍下Flutter的理念架构
Flutter自上而下分为Embedder、Engine和Framework三层。其中Embedder是操作系统适配层,实现了渲染Surface设置,线程设置,以及平台插件等平台相关特性的适配;Engine层负责图形绘制,文字排版和提供Dart运行时,Engine层具有独立虚拟机,正是由于它的存在,Flutter程序才能运行在不同的平台上,实现跨平台运行;Framework层则是使用Dart编写的一套基础视图库,包含了动画、图形绘制和手势识别等功能,是使用频率最高的一层。
3.介绍下Flutter的Framework层和Engine层,以及它们的作用
Flutter的Framework层是用Dart编写的框架(SDK),它实现了一套基础库,包含Material(Android)和Cupertino(iOS风格)的UI界面,下面是通用的Widgets(组件),之后是一些动画、绘制、渲染、手势库等。这个纯Dart实现的SDK被封装为了一个叫做dart:ui的Dart库。我们在使用Flutter写App的时候,直接导入这个库即可使用组件等功能。
Flutter的Engine层是Skia 2D的绘图引擎库,其前身是一个向量绘图软件,Chrome和Android均采用Skia作为绘图引擎,Skia提供了非常友好的API,并且在图形转换、文字渲染、位图渲染方面都提供了友好、高效的表现。Skia是跨平台的,所以可以被嵌入到Flutter的iOS SDK中,而不用去研究iOS闭源的Core Graphice/Core Animation。Android自带了Skia,所以Flutter Android SDK要逼iOS SDK小很多。
4.介绍下Widget、State、Context概念
Widget:在Flutter中,几乎所有东西都是Widget。将一个Widget想象一个可视化的组件(或与应用可视化方面交互的组件),当你需要构建与布局直接或间接相关的任何内容时,你正在使用Widget
Widget树:Widget以树结构进行组织。包含其他Widget的widget被称为父Widget(或widget容量)。包含在父widget中的widget被称为子Widget。
Context:仅仅是已创建的所有Widget树结构中的某个Widget的位置引用。简而言之,为context作为widget树的一部分,其中context所对应的widget背添加到此树中。一个context只从属于一个widget,它和widget一样是链接在一个的,并且会形成一个context树。
State:定义StateFulWidget实例的行为,它包含了用于 “交互/干预”Widget信息的行为和布局,应用与State的任何更改都会强制重建Widget。
5.简述Widget的StatelessWidget和StatefulWidget两种状态组件类
StatelessWidget:一旦创建就不关心任何变化,在下次构建之前都不会改变,它们除了依赖于自身的配置信息(在父节点构建时提供)外不再依赖于任何其他信息。比如典型的Text、Row、Column、Container等,都是StatelessWidget。它的生命周期相当简单:初始化、通过build()渲染。
StatefulWidget:在生命周期内,该类Widget所持有的数据可能会发生变化,这样的数据背称为State,这些拥有动态内部数据的Widget被称为StatefulWidget。鼻涕复选框、Button等。State会于Context相关联,并且此关联是永久性的,State对象将永远不会改变其Context,即使可以在树结构周围移动,也仍将与该context相关联。当state与context关联时,state背视为已挂载。StatefulWidget由两部分组成,在初始化时必须要在create State()时初始化一个与之相关的State对象
6.StatefulWidget的生命周期
Flutter的Widget分为Stateless Widget和Stateful Widget两种。其中,StatelessWidget是无状态的,StatefulWidget时有状态的,因此实际使用时,更多的是StatefulWidget。StatefulWidget的生命周期如下
initState():Widget 初始化当前State,在当前方法中是不能获取到Context得,如想获取,可以试试Future.delayed()
didChangeDependencies():在initState()后调用,State对象依赖关系发送变化的时候也会调用。
deactivate():当State被暂时从视图树移除时会调用这个方法,页面切换是也会调用该方法,和Android里的onPause差不多。
dispose():Widget销毁时调用
didUpdateWidget:widget状态发生变化的时候调用
7。简述Widget、RenderObject和Elements的关系
Widget:仅用于存储渲染所需要的信息
RenderObject:负责管理布局、绘制等操作
Element:才是这颗巨大的控件树上的实体
Widget会被inflate(填充)到Element,并由Element管理底层渲染树。Widget并不会直接管理状态及渲染,而是通过State这个对象来管理状态。Flutter创建Element得可见树,相对于Widget来说,是可变的,通常界面开发中,我们不用直接操作Eelement,而是由框架层实现内部逻辑。就如一个UI视图树中,可能包含多个TextWidget(Widget被使用多次),但是放在内部视图树的视角,这些TextWidget都是填充到一个个独立的Element中。Element会持有renderObject和widget实例。记住,Widget只是一个配置,RenderObject负责管理布局、绘制等操作
在第一次创建Widget的时候,会对应创建一个Element,然后将该元素插入树中。如果之后Widget发生了变化,则将其与旧的Widget进行比较,并且相应地更新Element。重要的是,Element不会被重建,只是更新而已。
8.什么是状态管理,你了解哪些状态管理框架
Flutter中的状态和前端React中的状态概念是一致的。React框架的核心思想就是组件化,应用由组件搭建而成,组件最重要的改了就是状态,状态是一个组件的UI数据模型,是组件渲染仕的数据依据。
Flutter的状态可以分为全局状态和局部状态两种。常用的状态管理由ScopedModel、BLoC、Redux/FishRedux和Provider。详细使用情况和差异可以自行了解
9.简述Flutter的绘制流程
Flutter的绘制流程如下
相对于React Native、Week等跨平台框架,Flutter拥有自己的UI绘制体系,避免了React Native、Week等跨平台框架与Native系统的侨接,从而更好的提升性能
在Flutter中,UI都是一帧一帧的绘制,但这绘制的背后都会经过如下阶段
1.动画与微任务阶段,主要是处理动画及执行一系列微任务
2.构建阶段(build),找出标记为“脏”的节点与布局边界直接的所有节点,并做相应的更新
3.布局阶段,计算Widget的大小及位置的确定
4.compositingBits阶段。重绘之前的预处理操作,检查RenderObject是否需要重绘
5.绘制阶段,根据Widget大小及位置来绘制UI
6.compositing阶段,将UI数据发生给GPU处理
7.semantics阶段,与平台的辅助功能相关
8.finalization阶段,主要是从Element树中移除无用的Element对象及处理绘制结束回调
10.简述Flutter的现场管理模型
默认情况下,Flutter Engine层会创建一个Isolate,并且Dart代码默认就运行在这个主Isolate上。必要时可以使用spawnUri和spawn两种方式来创建新的Isolate,在Flutter中,新创建的Isolate由Flutter进行统一的管理。 事实上,Flutter Engine自己不创建和管理线程,Flutter Engine线程的创建和管理是Embeder负责的,Embeder指的是将引擎移植到平台的中间层代码,Flutter Engine层的架构示意图如下图所示。
在Flutter的架构中,Embeder提供四个Task Runner,分别是Platform Task Runner、UI Task Runner Thread、GPU Task Runner和IO Task Runner,每个Task Runner负责不同的任务,Flutter Engine不在乎Task Runner运行在哪个线程,但是它需要线程在整个生命周期里面保持稳定。
11.Flutter 是如何与原生Android、iOS进行通信的?
Flutter 通过 PlatformChannel 与原生进行交互,其中 PlatformChannel 分为三种:
BasicMessageChannel :用于传递字符串和半结构化的信息。
MethodChannel :用于传递方法调用(method invocation)。
EventChannel : 用于数据流(event streams)的通信。
12. 简述Flutter 的热重载
Flutter 的热重载是基于 JIT 编译模式的代码增量同步。由于 JIT 属于动态编译,能够将 Dart 代码编译成生成中间代码,让 Dart VM 在运行时解释执行,因此可以通过动态更新中间代码实现增量同步。
热重载的流程可以分为 5 步,包括:扫描工程改动、增量编译、推送更新、代码合并、Widget 重建。Flutter 在接收到代码变更后,并不会让 App 重新启动执行,而只会触发 Widget 树的重新绘制,因此可以保持改动前的状态,大大缩短了从代码修改到看到修改产生的变化之间所需要的时间。
另一方面,由于涉及到状态的保存与恢复,涉及状态兼容与状态初始化的场景,热重载是无法支持的,如改动前后 Widget 状态无法兼容、全局变量与静态属性的更改、main 方法里的更改、initState 方法里的更改、枚举和泛型的更改等。
可以发现,热重载提高了调试 UI 的效率,非常适合写界面样式这样需要反复查看修改效果的场景。但由于其状态保存的机制所限,热重载本身也有一些无法支持的边界。