FlutterIT之家Flutter面试题

Flutter面试宝典2021-持续更新

2021-08-25  本文已影响0人  JianLee

1:Flutter中的生命周期

2:Dart是不是单线程模型?是如何运行的?

Dart 是单线程模型,运行的的流程如下图。


简单来说,Dart 在单线程中是以消息循环机制来运行的,包含两个任务队列,一个是“微任务队列” MicroTask Queue,另一个叫做“事件队列” Event Queue。
当Flutter应用启动后,消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务,当所有微任务队列执行完后便开始执行事件队列中的任务,事件任务执行完毕后再去执行微任务,如此循环往复,生生不息。
在 Dart 每一个 isolate 当中,执行优先级为 : Main > MicroTask > EventQueue

3:Dart 是如何实现多任务并行的?

Dart 是单线程的,不存在多线程,那如何进行多任务并行的呢?其实,Dart的多线程和前端的多线程有很多的相似之处。Flutter的多线程主要依赖Dart的并发编程、异步和事件驱动机制。


简单的说,在Dart中,一个Isolate对象其实就是一个isolate执行环境的引用,一般来说我们都是通过当前的isolate去控制其他的isolate完成彼此之间的交互,而当我们想要创建一个新的Isolate可以使用Isolate.spawn方法获取返回的一个新的isolate对象,两个isolate之间使用SendPort相互发送消息,而isolate中也存在了一个与之对应的ReceivePort接受消息用来处理,但是我们需要注意的是,ReceivePort和SendPort在每个isolate都有一对,只有同一个isolate中的ReceivePort才能接受到当前类的SendPort发送的消息并且处理。

4:Future、async 和 await

Future 是异步编程的解决方案,Future 是基于观察者模式的,它有 3 种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)可以用 then 方法指定成功状态的回调函数 then 方法还可以接受一个可选命名参数,参数的名称是 onError,即失败状态的回调函数 Future 实例的函数中抛出了异常,被 onError 回调函数捕获到,并且可以看出 then 方法返回的还是一个 Future 对象,所以其实我们还可以利用 Future 对象的 cathError 进行链式调用从而捕获异常

在Dart1.9中加入了async和await关键字,有了这两个关键字,我们可以更简洁的编写异步代码,而不需要调用Future相关的API
将 async 关键字作为方法声明的后缀时,具有如下意义

async 不是并行执行,它是遵循Dart 事件循环规则来执行的,它仅仅是一个语法糖,简化Future API的使用。

5:Dart异步编程中的 Stream数据流?

在Dart中,Stream 和 Future 一样,都是用来处理异步编程的工具。它们的区别在于,Stream 可以接收多个异步结果,而Future 只有一个。
Stream 的创建可以使用 Stream.fromFuture,也可以使用 StreamController 来创建和控制。还有一个注意点是:普通的 Stream 只可以有一个订阅者,如果想要多订阅的话,要使用 asBroadcastStream()。

6:Stream 有哪两种订阅模式?分别是怎么调用的?

Stream有两种订阅模式:单订阅(single) 和 多订阅(broadcast)。单订阅就是只能有一个订阅者,而广播是可以有多个订阅者。这就有点类似于消息服务(Message Service)的处理模式。单订阅类似于点对点,在订阅者出现之前会持有数据,在订阅者出现之后就才转交给它。而广播类似于发布订阅模式,可以同时有多个订阅者,当有数据时就会传递给所有的订阅者,而不管当前是否已有订阅者存在。

Stream 默认处于单订阅模式,所以同一个 stream 上的 listen 和其它大多数方法只能调用一次,调用第二次就会报错。但 Stream 可以通过 transform() 方法(返回另一个 Stream)进行连续调用。通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。

7:Flutter如何与Android、iOS通信?

Flutter 通过 PlatformChannel 与原生进行交互,其中 PlatformChannel 分为三种:
BasicMessageChannel :用于传递字符串和半结构化的信息。
MethodChannel :用于传递方法调用(method invocation)。
EventChannel : 用于数据流(event streams)的通信。
同时 Platform Channel 并非是线程安全的 ,更多详细可查阅闲鱼技术的 《深入理解Flutter Platform Channel》

8:Flutter中嵌套原生View的方法?

你的类 extends PlatformView
你的类 extends PlatformViewFactory
之后再通过registerViewFactory注册你的View,并设置一个ID。
我们在Flutter端,创建一个AndroidView,并通过上面的ID就可以获取到我们创建的androidView,并使用。

9:Widgets、RenderObjects 和 Elements的关系

首先看一下这几个对象的含义及作用。

Widget会被inflate(填充)到Element,并由Element管理底层渲染树。Widget并不会直接管理状态及渲染,而是通过State这个对象来管理状态。Flutter创建Element的可见树,相对于Widget来说,是可变的,通常界面开发中,我们不用直接操作Element,而是由框架层实现内部逻辑。就如一个UI视图树中,可能包含有多个TextWidget(Widget被使用多次),但是放在内部视图树的视角,这些TextWidget都是填充到一个个独立的Element中。Element会持有renderObject和widget的实例。记住,Widget 只是一个配置,RenderObject 负责管理布局、绘制等操作。在第一次创建 Widget 的时候,会对应创建一个 Element, 然后将该元素插入树中。如果之后 Widget 发生了变化,则将其与旧的 Widget 进行比较,并且相应地更新 Element。重要的是,Element 不会被重建,只是更新而已。

10:Flutter中如何自定义Widget?

首先自定义Widget有两种方式,分别是组合控件和绘制
这里只讲CustomPainter 大致流程

shouldRepaint()控制绘制的重绘,主要用于动画、刷新等功能

11:如何获取Widget的尺寸?

想要获取widget的尺寸,必须要等widget的layout结束之后才能取到,目前有三种方式

 var size = context?.findRenderObject()?.paintBounds?.size;

过早获取可能为空,需要延迟获取。

GlobalKey _globalKey = GlobalKey();
 var size = _globalKey.currentContext
                  ?.findRenderObject()
                  ?.paintBounds
                  ?.size;

使用SizeChangedLayoutNotifier方式,Widget会在layout结束之后会发出一个LayoutChangedNotification通知,我们只需要接收这个通知,即可获取尺寸信息

12:Flutter中进行性能监控和如何进行优化?

13:Flutter中的手势事件传递、事件分发、事件冲突竞争,滑动流畅等等

14:什么是Key?什么时候需要Key?Key的种类有哪些?

key的作用是:控制weidget树上的widget是否被替换(刷新)


KEY的分类

GlobalKey有两种用途,它允许widget在App的任何位置更改父级而不会丢失状态,或者可以使用它们在Widget Tree完全不同的部分中访问有关另一个widget的信息.
比如要在两个不同的屏幕显示相同的widget,并保持所有相同的状态, 此时可以使用GlobalKey.
而LocalKey又有以下几个子类:

PageStorageKey是用来存储用户滚动位置的专用key,因此APP可以保留它以供后面使用.
我们要注意3点:
1.在具有相同父元素的Elements中,键必须唯一。相比之下,GlobalKeys在整个应用程序中必须唯一。
2.Key的子类应该是LocalKey或GlobalKey的子类.
3.GlobalKey更为昂贵,因此如果没有必要,请使用ValueKey, ObjectKey, 或者 UniqueKey.

15:什么是状态管理,你了解哪些状态管理框架?

首先状态其实是一个概念上的东西,区分全局状态和局部状态。
局部状态比如说一个控件中输入的信息,全局状态比如是登陆后从后台请求回来的 userId。
当全局状态越来越多,多个页面共享一个状态时,我们就需要管理它。
常用的状态管理有BLoC、Provider

16:Flutter中的动画

Animation

在Flutter中,实现动画的核心类是Animation,Widget可以直接将这些动画合并到自己的build方法中来读取他们当前值或者监听它们的状态变化。

AnimationController

Animation是一个抽象类,并不能用来直接创建对象实现动画的使用。
AnimationController是Animation的一个子类,实现动画通常我们需要创建AnimationController对象。
AnimationController会生成一系列的值,默认情况下值是0.0到1.0区间的值,除了上面的监听,获取动画的状态、值之外,AnimationController还提供了对动画的控制:

AnimationController有一个必传的参数vsync,这里是为了监听vsync信号,当Flutter开发的应用程序不再接受同步信号时(比如锁屏或退到后台),那么继续执行动画会消耗性能。

CurvedAnimation

CurvedAnimation也是Animation的一个实现类,它的目的是为了给AnimationController增加动画曲线:

Tween

默认情况下,AnimationController动画生成的值所在区间是0.0到1.0,如果希望使用这个以外的值,或者其他的数据类型,就需要使用Tween。

class Tween<T extends dynamic> extends Animatable<T> {
  Tween({ this.begin, this.end });
}

Tween也有一些子类,比如ColorTween、BorderTween,可以针对动画或者边框来设置动画的值。
要使用Tween对象,需要调用Tween的animate()方法,传入一个Animation对象。

AnimatedWidget

17:mixin机制?

mixin 是Dart 2.1 加入的特性,以前版本通常使用abstract class代替。简单来说,mixin是为了解决继承方面的问题而引入的机制,Dart为了支持多重继承,引入了mixin关键字,它最大的特殊处在于:mixin定义的类不能有构造方法,这样可以避免继承多个类而产生的父类构造方法冲突。mixins的对象是类,mixins绝不是继承,也不是接口,而是一种全新的特性,可以mixins多个类,mixins的使用需要满足一定条件。

18:说一下Widget、State、Context 概念

上一篇下一篇

猜你喜欢

热点阅读