Flutter 大厂面试真题(含答案) 您所在的位置:网站首页 厦门大学嘉庚学院的学校代码 Flutter 大厂面试真题(含答案)

Flutter 大厂面试真题(含答案)

2023-12-19 08:53| 来源: 网络整理| 查看: 265

1. 为什么使用Flutter?(Flutter的特点) 性能:实现了自绘引擎,保持不同端UI一致性,动画流畅性 生态:从Github上来看,目前Flutter活跃用户正在高速增长。从Stackoverflow上提问来看,Flutter社区现在已经很庞大。 技术支持:现在Google正在大力推广Flutter,Flutter的作者中很多人都是来自Chromium团队,并且github上活跃度很高。 开发效率:Flutter的热重载可帮助开发者快速地进行测试、构建UI、添加功能并更快地修复错误。在iOS和Android模拟器或真机上可以实现毫秒级热重载,并且不会丢失状态。 2. Flutter和RN的对比。

1. 环境搭建:

React Native:

需要 npm 、node 、react-native-cli 等配置

Flutter:

需要 flutter sdk 和 Android Studio / VSCode 上的 Dart 与 Flutter 插件。

2. 实现原理:

React Native:

通过写 JS 代码配置页面布局,然后 React Native 最终会解析渲染成原生控件,如 标签对应 ViewGroup/UIView , 标签对应 ScrollView/UIScrollView , 标签对应 ImageView/UIImageView 等。

Flutter:

Flutter中绝大部分的 Widget 都与平台无关, 开发者基于 Framework 开发 App ,而 Framework 运行在 Engine 之上,由 Engine 进行适配和跨平台支持。这个跨平台的支持过程,其实就是将 Flutter UI 中的 Widget “数据化” ,然后通过 Engine 上的 Skia 直接绘制到屏幕上

3. 语言:

都支持通过 var 定义变量,支持 async/await 语法糖,支持 Promise(Future) 等链式异步处理

JS 是动态语言,而 Dart 是伪动态语言的强类型语言。动态语言和非动态语言都有各种的优缺点,比如 JS 开发便捷度明显会高于 Dart ,而 Dart 在类型安全和重构代码等方面又会比 JS 更稳健。

4. 图示 image.png

3. 说一下Hot Reload,Hot Restart,热更新三者的区别和原理。

区别

修改了状态相关的代码则需要hot restart,否则只需要 hot reload即可

Hot Reload原理

Flutter 提供的两种编译模式中,AOT 是静态编译,即编译成设备可直接执行的二进制码;而 JIT 则是动态编译,即将 Dart 代码编译成中间代码(Script Snapshot),在运行时设备需要 Dart VM 解释执行。

而热重载之所以只能在 Debug 模式下使用,是因为 Debug 模式下,Flutter 采用的是 JIT 动态编译(而 Release 模式下采用的是 AOT 静态编译)。JIT 编译器将 Dart 代码编译成可以运行在 Dart VM 上的 Dart Kernel,而 Dart Kernel 是可以动态更新的,这就实现了代码的实时更新功能,

总体来说,完成热重载的可以分为扫描工程改动、增量编译、推送更新、代码合并、Widget 重建 5 个步骤。

工程改动。热重载模块会逐一扫描工程中的文件,检查是否有新增、删除或者改动,直到找到在上次编译之后,发生变化的 Dart 代码。 增量编译。热重载模块会将发生变化的 Dart 代码,通过编译转化为增量的 Dart Kernel 文件。 推送更新。热重载模块将增量的 Dart Kernel 文件通过 HTTP 端口,发送给正在移动设备上运行的 Dart VM。 代码合并。Dart VM 会将收到的增量 Dart Kernel 文件,与原有的 Dart Kernel 文件进行合并,然后重新加载新的Dart Kernel 文件。 Widget 重建。在确认 Dart VM 资源加载成功后,Flutter 会将其 UI 线程重置,通知 Flutter Framework 重建 Widget。

不支持 Hot Reload 的场景

代码出现编译错误; Widget 状态无法兼容; 全局变量和静态属性的更改; main 方法里的更改; initState 方法里的更改; 枚举和泛类型更改 4. Flutter是如何做到一套Dart代码可以编译运行在Android和iOS平台的?所以说具体的原理。 Skia绘制,实现跨平台应用层渲染一致性 Method Channel 机制

为了解决调用原生系统底层能力以及相关代码库复用问题,Flutter 为开发者提供了一个轻量级的解决方案,即逻辑层的方法通道(Method Channel)机制。基于方法通道,我们可以将原生代码所拥有的能力,以接口形式暴露给 Dart,从而实现 Dart 代码与原生代码的交互,就像调用了一个普通的 Dart API 一样。

image.png

Flutter定义了三种不同类型的Channel

BasicMessageChannel:用于传递字符串和半结构化的信息。 MethodChannel:用于传递方法调用(method invocation)。 EventChannel: 用于数据流(event streams)的通信。

注意

Method Channel 是非线程安全的。原生代码在处理方法调用请求时,如果涉及到异步或非主线程切换,需要确保回调过程是在原生系统的 UI 线程(也就是 Android 和 iOS 的主线程)中执行的,否则应用可能会出现奇怪的 Bug,甚至是 Crash。

5. Flutter不具备反射,如果要使用反射,你应该如何使用?说一下大概的思路。

使用 Mirror

Mirror 的主要类型如下:

ClassMirror:Dart 类的反射类型 InstanceMirror:Dart 实例的反射类型 ClosureMirror: 闭包的反射类型 DeclarationMirror:类属性的反射类型 IsolateMirror:Isolate 的反射类型 MethodMirror:Dart 方法(包括函数、构造函数、getter/setter 函数)的反射类型 6. Flutter在不使用WebView和JS方案的情况下。如何做到热更新?说一下大概思路。 iOS 目前不支持,不能过审 安卓,可以替换so文件来实现

可以接入Tinker进行热更新,而且有Bugly做为补丁版本控制台,来上传下发补丁,统计数量。

Android端Flutter热更新

7. 如何让Flutter 编译出来的APP的包大小尽可能的变小?

1. 移除无用代码和无用资源,压缩图片, 安卓里拆 App Bundle,

2. Dart 编译产物做针对性优化

动态下发:剥离 Data 段及一切非必要产物,打包后动态下发。 内置压缩:以二进制形态内置动态下发包。

3. Flutter 引擎编译产物优化

主要优化思路有升级 Bulild Tools 统一双编译参数, 定制化编译裁剪引擎内部部分特定无用功能。

4. 机器码指令优化

精简机器码指令,Google 也回复称未来 Dart 与 OC 基本持平。

7. 我们这个项目时一个综合系统的老项目,里面有Android,iOS,还有Web代码,是一个混合开发的项目,现在需要迁移到Flutter,加入你加入团队做这个项目的迁移工作,你觉得这个项目如何工程化、容器化以及架构演变应该从哪些维度思考?

使用闲鱼团队开源的 flutter_boost

可复用通用型混合方案 支持更加复杂的混合模式,比如支持主页Tab这种情况 无侵入性方案:不再依赖修改Flutter的方案 支持通用页面生命周期 统一明确的设计概念

当一个Native的页面容器存在的时候,FlutteBoost保证一定会有一个Widget作为容器的内容。所以我们在理解和进行路由操作的时候都应该以Native的容器为准,Flutter Widget依赖于Native页面容器的状态。

在FlutterBoost的概念里说到页面的时候,我们指的是Native容器和它所附属的Widget。所有页面路由操作,打开或者关闭页面,实际上都是对Native页面容器的直接操作。无论路由请求来自何方,最终都会转发给Native去实现路由操作。这也是接入FlutterBoost的时候需要实现Platform协议的原因。

用它开始Flutter混合开发—FlutterBoost

8. APP启动速度以及页面加载速度一直是我们比较关心的一个问题,特别是混合开发项目,谈谈你对Flutter渲染优化有哪些见解?

1. 让 Flutter 中重建组件的个数尽量少

在实际开发过程中,如果将整个页面写在一个单独的 StatefulWidget 中,那么每次状态更新时都会导致很多不必要的 UI 重建。因此, 我们要学会拆解组件,使用良好设计模式和状态管理方案,当需要更新状态时将影响范围降到最小。

2. 构建组件时使用 const 关键词,可以抑制 widget 的重建

合理利用 const 关键词,可以在很大程度上优化应用的性能

使用 const 也需要注意如下几点:

当 const 修饰类的构造函数时,它要求该类的所有成员都必须是final的。

const 变量只能在定义的时候初始化。

3. Flutter实现的一些效果背后可能会使用 saveLayer() 这个代价很大的方法

如下这几个组件,底层都会触发 saveLayer() 的调用,同样也都会导致性能的损耗:

ShaderMask ColorFilter Chip,当 disabledColorAlpha != 0xff 的时候,会调用 saveLayer()。 Text,如果有 overflowShader,可能调用 saveLayer() ,

4. 官方也给了我们一些非常需要注意的优化点:

由于 Opacity 会使用屏幕外缓冲区直接使目标组件中不透明,因此能不用 Opacity Widget,就尽量不要用。有关将透明度直接应用于图像的示例,请参见 Transparent image,比使用 Opacity widget 更快,性能更好。

要在图像中实现淡入淡出,请考虑使用 FadeInImage 小部件,该小部件使用 GPU 的片段着色器应用渐变不透明度。

很多场景下,我们确实没必要直接使用 Opacity 改变透明度,如要作用于一个图片的时候可以直接使用透明的图片,或者直接使用 Container:Container(color: Color.fromRGBO(255, 0, 0, 0.5))

Clipping 不会调用 saveLayer()(除非明确使用 Clip.antiAliasWithSaveLayer),因此这些操作没有 Opacity 那么耗时,但仍然很耗时,所以请谨慎使用。

5. 管理着色器编译垃圾

有时候,应用中的动画首次运行时会看起来非常卡顿,但是运行多次之后便可以正常运行,这可能就是由于着色器编译混乱导致的。

在不同平台上,可以执行以下命令,使用 SkSL 预热功能构建应用程序:

安卓

flutter build apk — bundle-sksl-path flutter_01.sksl.json

iOS

flutter build ios --bundle-sksl-path flutter_01.sksl.json 9. 谈谈Flutter的内存回收管理机制,以及你平时是怎么处理内存的?内存泄漏和内存溢出你是怎么解决的?

Dart的垃圾回收是分代的:年轻代和老年代

GC与Flutter engine建立联系,当engine检测到应用程序处于空闲状态且没有用户交互时,它会发出通知。这样就使得GC有了收集的窗口从而不影响性能。

年轻代 这个阶段旨在清除寿命较短的短暂对象,例如stateless widgets。虽然它是阻塞的,但它比老年代mark-sweep快得多,并且当与调度结合使用时,几乎不会影响程序的运行。

要确定哪些对象是否可被回收,收集器将以root对象(例如堆栈变量)开始,并检查它们引用的对象。然后把引用的对象移动到另一半空间。在那里它检查这些移动的对象指向的内容,并移动这些引用的对象。如此反复,直到移动所有活动对象到另一半空间。始终没有被引用的对象将被回收。

老年代(并行标记和并发扫描)

当对象经历过一定次数的GC仍然存在,或者其生命周期较长(个人猜测类似于element和RenderObject这种需要多次复用,可变且创建比较耗费性能),将其放入老年代区域中。老年代采用标记整理的方法来回收对象。

这种GC技术有两个阶段:首先遍历对象图,并标记仍在使用的对象。在第二阶段期间,扫描整个存储器,并且回收未标记的任何对象。然后清除所有标志。

详情见Dart内存机制

10. 再问一个简单一点的,你是如何把控混合项目开发时的生命周期(比如类似安卓的onCreate、onResume这种)和路由管理的?

生命周期

使用系统提供的回调

SystemChannels.lifecycle.setMessageHandler((msg) async { if (msg == AppLifecycleState.resumed.toString()) { } else if (msg == AppLifecycleState.inactive.toString()) { } else if (msg == AppLifecycleState.paused.toString()) { } else if (msg == AppLifecycleState.detached.toString()) { } return ''; });

或者自己在安卓端使用 MethodChannel,通知Flutter生命周期的变化

路由管理:

在原生页面切换到 Flutter 页面时,我们通常会将 Flutter 容器封装成一个独立的 ViewController(iOS 端)或 Activity(Android 端),在为其设置好 Flutter 容器的页面初始化路由(即根视图)后,原生的代码就可以按照打开一个普通的原生页面的方式来打开 Flutter 页面了。

而如果我们想在 Flutter 页面跳转到原生页面,则需要同时处理好打开新的原生页面,以及关闭自身回退到老的原生页面两种场景。在这两种场景下,我们都需要利用方法通道来注册相应的处理方法,从而在原生代码宿主实现新页面的打开和 Flutter 容器的关闭。

需要注意的是,与纯 Flutter 应用不同,原生应用混编 Flutter 由于涉及到原生页面与 Flutter 页面之间切换,因此导航栈内可能会出现多个 Flutter 容器的情况,即多个 Flutter 实例。Flutter 实例的初始化成本非常高昂,每启动一个 Flutter 实例,就会创建一套新的渲染机制,即 Flutter Engine,以及底层的 Isolate。而这些实例之间的内存是不互相共享的,会带来较大的系统资源消耗。

为了解决混编工程中 Flutter 多实例的问题,业界有两种解决方案:

以今日头条为代表的修改 Flutter Engine 源码,使多 FlutterView 实例对应的多 Flutter Engine 能够在底层共享 Isolate;

以闲鱼为代表的共享 FlutterView,即由原生层驱动 Flutter 层渲染内容的方案。

不过,目前这两种解决方案都不够完美。所以,在 Flutter 官方支持多实例单引擎之前,应该尽量使用Flutter去开发一些闭环业务,减少原生页面与Flutter页面之间的交互,尽量避免Flutter页面跳转到原生页面,原生页面又启动一个新的Flutter实例的情况,并且保证应用内不要出现多个 Flutter 容器实例的情况。

11. Flutter for web 和Flutter1.9推出的Flutter Web有何本质上的区别?

Web的生产质量支持。

特别关注三种应用程序场景:

渐进式Web应用程序(PWA),将Web的访问范围与桌面应用程序的功能结合在一起。 单页应用程序(SPA),一次加载并与Internet服务之间进行数据传输。 将现有的Flutter移动应用程序带到Web上,从而为两种体验启用共享代码。

在过去的几个月中,在为稳定发布Web支持做准备的同时,我们在性能优化方面取得了许多进展,添加了一个新的由WebAssembly构建的由CanvasKit驱动的渲染引擎。Flutter Plasma是由社区成员Felix Blaschke构建的演示,展示了使用Dart和Flutter构建复杂的Web图形体验的简便性,这些体验也可以在桌面或移动设备上本地运行。

12. 谈谈你认为的Flutter Web应该如何改进?哪些内容可以改造之后可以用于平时的Web开发。谈谈你的改造方案。 性能和兼容性问题 插件太多不支持,生态没起来 对SEO优化支持不好 13. 谈谈如何打造低延迟的视频直播?为什么这样用?

使用WebRTC

它使用UDP来进行媒体推流,而不需要创建离散的媒体段,这为所有客户端提供了始终如一的低延时

WebRTC还提供了一个实时双向数据通道,可用于发送和接收数据流。这种双向数据技术给在线流现在如何能成为一种交互式的体验提供了很多有趣的可能性。

RTS服务部署于CDN节点

RTS服务进行服务与节点双重升级,同时针对全链路直播指标进行监控和针对性优化,以及通过智能调度系统以及网络拥塞、抗弱网优化、缓冲策略等进行一系列底层核心技术优化,实现RTP over UDP更好地对抗公网的丢包,使得播放器上收到的流质量相对RTMP over TCP更加稳定,这样一来,播放器就可以降低buffer,不用像以前那样设置6s 的buffer来对抗抖动,现在只需要设置1秒左右就OK了,整体延时可以控制在1-1.5S左右。

如何构建低延时的直播体验,让互动更实时?

14. 参考链接: Flutter 与 React Native 的对比分析 Flutter的Hot Reload是如何做到的 如何在Dart层兼容Android-iOS平台特定实现? Dart 反射初识 如何缩减接近 50% 的 Flutter 包体积 Flutter渲染性能优化全攻略(解决应用卡顿) Flutter 2.0 重磅更新 如何实现 Flutter 与原生的混合开发方式?详解Flutter混合开发的路由栈管理


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有