flutter 面試

一、flutter啟動(dòng)流程
1.實(shí)例化WidgetsFlutterBinding類,
2.創(chuàng)建組件樹attachRootWidget(app),
3.啟動(dòng)預(yù)熱幀scheduleWarnUpFrame()。

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..attachRootWidget(app)
    ..scheduleWarmUpFrame();
}

1.WidgetsFlutterBinding類繼承自BindingBase基類和其它一些特定功能特性類,該類是將基于組件框架的應(yīng)用程序綁定到Flutter引擎的膠水類,并返回一個(gè)WidgetsBinding實(shí)例。

BindingBase: 該類是一個(gè)抽象類基類,初始化拓展服務(wù),其申明的接口由其子類實(shí)現(xiàn)。
-GestureBinding: 該類綁定了手勢(shì)事件,用于檢測(cè)應(yīng)用程序各類手勢(shì)。它以mixin的方式加入到WidgetsFlutterBinding中,并實(shí)現(xiàn)了BindingBase類中的部分方法。
-ServicesBinding:該類監(jiān)聽了平臺(tái)消息(platform messages),注冊(cè)了Flutter層與Native層的消息傳輸服務(wù),最終將消息定向到BinaryMessages類中。
-SchedulerBinding: 該類注冊(cè)了頁面幀繪制有關(guān)的回調(diào)函數(shù),同時(shí)也處理Widget生命周期相關(guān)事件,處理的事件類型包括:paused、resumed、inactive和suspending。
-PaintingBinding:綁定繪制庫(kù),同時(shí)還創(chuàng)建了圖片緩存。
-SemanticsBinding:將組件語義樹與Flutter引擎綁定
-RendererBinding:將組件渲染樹與Flutter引擎綁定
-WidgetsBinding:將組件樹與Flutter引擎綁定

2.attachRootWidget(Widget rootWidget)函數(shù)

void attachRootWidget(Widget rootWidget) {
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView,
      debugShortDescription: '[root]',
      child: rootWidget,
    ).attachToRenderTree(buildOwner, renderViewElement);
  }

該函數(shù)調(diào)用RenderObjectToWidgetAdapter函數(shù)創(chuàng)建了一個(gè)RenderBox,緊接著將改組件作為根節(jié)點(diǎn)并調(diào)用attachToRenderTree把組件綁定到Widget樹中。

3.scheduleForcedFrame()函數(shù)
該函數(shù)作用是在App啟動(dòng)是盡快運(yùn)行預(yù)熱幀,而不是等待引擎請(qǐng)求幀以響應(yīng)系統(tǒng)“Vsync”信號(hào)。

二、異步
可以使我們?cè)诰帉慏art程序時(shí)可以異步的來執(zhí)行耗時(shí)操作。從而可以在等待一個(gè)操作完成的同時(shí)進(jìn)行別的操作以下是一些常見的異步操作:

通過網(wǎng)絡(luò)獲取數(shù)據(jù)。

寫入數(shù)據(jù)庫(kù)。

從文件讀取數(shù)據(jù)。

要在Dart中執(zhí)行異步操作,可以使用Future類和async和await關(guān)鍵字。
1.dart 事件隊(duì)列

Dart的事件循環(huán)(event loop)

在Dart中,實(shí)際上有兩種隊(duì)列:

a.用來處理外部的事件,如果IO、點(diǎn)擊、繪制、計(jì)時(shí)器(timer)和不同 isolate 之間的消息事件等。。

將任務(wù)添加到MicroTask隊(duì)列有兩種方法

import  'dart:async';
 
void  myTask(){
    print("this is my task");
}
 
void  main() {
    # 1. 使用 scheduleMicrotask 方法添加
    scheduleMicrotask(myTask);
 
    # 2. 使用Future對(duì)象添加
    new  Future.microtask(myTask);
}

b.微任務(wù)隊(duì)列(microtask queue),處理來自于Dart內(nèi)部的任務(wù),適合用來不會(huì)特別耗時(shí)或緊急的任務(wù)。表示一個(gè)短時(shí)間內(nèi)就會(huì)完成的異步任務(wù)。它的優(yōu)先級(jí)最高,高于event queue,只要隊(duì)列中還有任務(wù),就可以一直霸占著事件循環(huán)。microtask queue添加的任務(wù)主要是由 Dart內(nèi)部產(chǎn)生。

在每一次事件循環(huán)中,Dart總是先去第一個(gè)microtask queue中查詢是否有可執(zhí)行的任務(wù),如果沒有,才會(huì)處理后續(xù)的event queue的流程。異步任務(wù)我們用的最多的還是優(yōu)先級(jí)更低的 event queue。Dart為 event queue 的任務(wù)建立提供了一層封裝,就是我們?cè)贒art中經(jīng)常用到的Future。

事件循環(huán)的運(yùn)行機(jī)制:當(dāng)應(yīng)用啟動(dòng)后,它會(huì)創(chuàng)建一個(gè)isolate,啟動(dòng)事件循環(huán),按照FIFO的順序,優(yōu)先處理微任務(wù)隊(duì)列,然后再處理事件隊(duì)列,如此反復(fù)。

三、多線程

注:以下當(dāng)我們提到isolate的時(shí)候,你可以把它等同于線程,但我們知道它不僅僅是一個(gè)線程。
1.Isolate
2.compute

1.Isolate
得益于異步 IO + 事件循環(huán),盡管Dart是單線程,一般的IO密集型App應(yīng)用通常也能獲得出色的性能表現(xiàn)。但對(duì)于一些計(jì)算量巨大的場(chǎng)景,比如圖片處理、反序列化、文件壓縮這些計(jì)算密集型的操作,只單靠一個(gè)線程就不夠用了。

在Dart中,你可以通過Isolate.spawn 來創(chuàng)建一個(gè)新的isolate:

void newIsolate(String mainMessage){
  sleep(Duration(seconds: 3));
  print(mainMessage);
}

void main() {
  // 創(chuàng)建一個(gè)新的isolate,newIoslate
  Isolate.spawn(newIsolate, 'Hello, Im from new isolate!'); 
  sleep(Duration(seconds: 10)); //主線程阻塞等待
}

輸出:

Hello, Im from new isolate!

spawn 有兩個(gè)必傳參數(shù),第一個(gè)是新isolate入口函數(shù)(entrypoint),第二個(gè)是這個(gè)入口函數(shù)的參數(shù)值(message)。

如果主isolate想接收子isolate的消息,可以在主isolate創(chuàng)建一個(gè)ReceivePort對(duì)象,并把對(duì)應(yīng)的receivePort.sendPort作為新isolate入口函數(shù)參數(shù)傳入,然后通過ReceivePort綁定SendPort對(duì)象給主isolate發(fā)送消息:

//新isolate入口函數(shù)
void newIsolate(SendPort sendPort){
  sendPort.send("hello, Im from new isolate!");
}

void main() async{
  ReceivePort receivePort= ReceivePort();
  Isolate isolate = await Isolate.spawn(newIsolate, receivePort.sendPort);
  receivePort.listen((message){ //監(jiān)聽從新isolate發(fā)送過來的消息
   
    print(message);
     
    // 不再使用時(shí),關(guān)閉管道
     receivePort.close();
     
    // 關(guān)閉isolate線程
     isolate?.kill(priority: Isolate.immediate);
  });
}
輸出:

hello, Im from new isolate!

上面我們了解了主isolate是如何監(jiān)聽來自子isolate的消息的,如果同時(shí)子isolate也想知道主isolate的一些狀態(tài),那該如何處理呢?下面的代碼將提供一種雙向通信的方式:

Future<SendPort> initIsolate() async {
  Completer completer = new Completer<SendPort>();
  ReceivePort isolateToMainStream = ReceivePort();

  //監(jiān)聽來自子線程的消息
  isolateToMainStream.listen((data) {
    if (data is SendPort) {
      SendPort mainToIsolateStream = data;
      completer.complete(mainToIsolateStream);
    } else {
      print('[isolateToMainStream] $data');
    }
  });

  Isolate myIsolateInstance = await Isolate.spawn(newIsolate, isolateToMainStream.sendPort);
  //返回來自子isolate的sendPort
  return completer.future; 
}

void newIsolate(SendPort isolateToMainStream) {
  ReceivePort mainToIsolateStream = ReceivePort();
  //關(guān)鍵實(shí)現(xiàn):把SendPort對(duì)象傳回給主isolate
  isolateToMainStream.send(mainToIsolateStream.sendPort);

  //監(jiān)聽來自主isolate的消息
  mainToIsolateStream.listen((data) {
    print('[mainToIsolateStream] $data');
  });

  isolateToMainStream.send('This is from new isolate');
}

void main() async{
  SendPort mainToIsolate = await initIsolate();
  mainToIsolate.send('This is from main isolate');
}
輸出:

[mainToIsolateStream] This is from main isolatemain end
[isolateToMainStream] This is from new isolate

2.compute
你還可以通過一個(gè)簡(jiǎn)化版的compute函數(shù)啟動(dòng)一個(gè)新的isolate。

比如在反序列化的場(chǎng)景中,直接在主isolate進(jìn)行序列化:

List<Photo> parsePhotos(String responseBody) {
  final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();

  return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}

Future<List<Photo>> fetchPhotos(http.Client client) async {
  final response =
      await client.get('https://jsonplaceholder.typicode.com/photos');
  //直接在主isolate轉(zhuǎn)換
  return parsePhotos(response.body); 
}

啟動(dòng)一個(gè)新的isolate:

Future<List<Photo>> fetchPhotos(http.Client client) async {
  final response =
      await client.get('https://jsonplaceholder.typicode.com/photos');
  // 使用compute函數(shù),啟動(dòng)一個(gè)新的isolate
  return compute(parsePhotos, response.body);
}

總結(jié)一下,當(dāng)遇到計(jì)算密集型的耗時(shí)操作,你可以開啟一個(gè)新的isolate來并發(fā)執(zhí)行任務(wù)。不像我們常規(guī)認(rèn)識(shí)的多線程,不同的isolate之間不能共享內(nèi)存,但通過ReceivePort和SendPort可以構(gòu)建不同isolate之間的消息通道,另外從別的isolate傳來的消息也是要經(jīng)過事件循環(huán)的。

四、Future 說一下 Future?

Future,字面意思「未來」,是用來處理異步的工具。

剛才也說過:

Dart 在單線程中是以消息循環(huán)機(jī)制來運(yùn)行的,其中包含兩個(gè)任務(wù)隊(duì)列,一個(gè)是“微任務(wù)隊(duì)列” microtask queue,另一個(gè)叫做“事件隊(duì)列” event queue。

Future 默認(rèn)情況下其實(shí)就是往「事件隊(duì)列」里插入一個(gè)事件,當(dāng)有空余時(shí)間的時(shí)候就去執(zhí)行,當(dāng)執(zhí)行完畢后會(huì)回調(diào) Future.then(v) 方法。

而我們也可以通過使用 Future.microtask 方法來向 「微任務(wù)隊(duì)列」中插入一個(gè)任務(wù),這樣就會(huì)提高他執(zhí)行的效率。

因?yàn)樵?Dart 每一個(gè) isolate 當(dāng)中,執(zhí)行優(yōu)先級(jí)為 : Main > MicroTask > EventQueue

五、steam 說一下 Stream?
應(yīng)用場(chǎng)景:網(wǎng)絡(luò)狀態(tài)的監(jiān)控
Stream 和 Feature 一樣,都是用來處理異步的工具。

但是 Stream 和 Feature 不同的地方是 Stream 可以接收多個(gè)異步結(jié)果,而Feature 只有一個(gè)。

Stream 的創(chuàng)建可以使用 Stream.fromFuture,也可以使用 StreamController 來創(chuàng)建和控制。

還有一個(gè)注意點(diǎn)是:普通的 Stream 只可以有一個(gè)訂閱者,如果想要多訂閱的話,要使用 asBroadcastStream()。

六、說一下 mixin?
首先mixin是一個(gè)定義類的關(guān)鍵字。直譯出來是混入,混合的意思 Dart為了支持多重繼承,引入了mixin關(guān)鍵字,它最大的特殊處在于: mixin定義的類不能有構(gòu)造方法,這樣可以避免繼承多個(gè)類而產(chǎn)生的父類構(gòu)造方法沖突
mixin extends implement 之間的關(guān)系?
繼承(關(guān)鍵字 extends)、混入 mixins (關(guān)鍵字 with)、接口實(shí)現(xiàn)(關(guān)鍵字 implements)。這三者可以同時(shí)存在,前后順序是extends -> mixins -> implements。

Flutter中的繼承是單繼承,子類重寫超類的方法要用@Override,子類調(diào)用超類的方法要用super。

在Flutter中,Mixins是一種在多個(gè)類層次結(jié)構(gòu)中復(fù)用類代碼的方法。mixins的對(duì)象是類,mixins絕不是繼承,也不是接口,而是一種全新的特性,可以mixins多個(gè)類,mixins的使用需要滿足一定條件。

(1) 使用mixins的條件是什么?
因?yàn)閙ixins使用的條件,隨著Dart版本一直在變,這里講的是Dart2.1中使用mixins的條件:

mixins類只能繼承自object
mixins類不能有構(gòu)造函數(shù)
一個(gè)類可以mixins多個(gè)mixins類
可以mixins多個(gè)類,不破壞Flutter的單繼承

(2) mixin 怎么指定異常類型?
on關(guān)鍵字可用于指定異常類型。 on只能用于被mixins標(biāo)記的類,例如mixins X on A,意思是要mixins X的話,得先接口實(shí)現(xiàn)或者繼承A。這里A可以是類,也可以是接口,但是在mixins的時(shí)候用法有區(qū)別.

七、StatefulWidget 的生命周期
initState():Widget 初始化當(dāng)前 State,在當(dāng)前方法中是不能獲取到 Context 的,如想獲取,可以試試 Future.delayed()
didChangeDependencies():在 initState() 后調(diào)用,State對(duì)象依賴關(guān)系發(fā)生變化的時(shí)候也會(huì)調(diào)用。
deactivate():當(dāng) State 被暫時(shí)從視圖樹中移除時(shí)會(huì)調(diào)用這個(gè)方法,頁面切換時(shí)也會(huì)調(diào)用該方法,和Android里的 onPause 差不多。
dispose():Widget 銷毀時(shí)調(diào)用。
didUpdateWidget:Widget 狀態(tài)發(fā)生變化的時(shí)候調(diào)用。

八、Flutter 如何與 Android iOS 通信?
Flutter 通過 PlatformChannel 與原生進(jìn)行交互,其中 PlatformChannel 分為三種:

BasicMessageChannel:用于傳遞字符串和半結(jié)構(gòu)化的信息。
MethodChannel:用于傳遞方法調(diào)用。Flutter主動(dòng)調(diào)用Native的方法,并獲取相應(yīng)的返回值。
EventChannel:用于數(shù)據(jù)流(event streams)的通信。
具體可以查看 閑魚技術(shù):深入理解 Flutter Platform Channel。

九、什么是 Widgets、RenderObjects 和 Elements?

Widget 僅用于存儲(chǔ)渲染所需要的信息。
RenderObject 負(fù)責(zé)管理布局、繪制等操作。
Element 才是這顆巨大的控件樹上的實(shí)體。
具體可以查看[譯] Flutter,什么是 Widgets、RenderObjects 和 Elements?

Widget實(shí)際上就是Element的配置數(shù)據(jù),Widget樹實(shí)際上是一個(gè)配置樹,而真正的UI渲染樹是由Element構(gòu)成;不過,由于Element是通過Widget生成,所以它們之間有對(duì)應(yīng)關(guān)系,所以在大多數(shù)場(chǎng)景,我們可以寬泛地認(rèn)為Widget樹就是指UI控件樹或UI渲染樹。
一個(gè)Widget對(duì)象可以對(duì)應(yīng)多個(gè)Element對(duì)象。這很好理解,根據(jù)同一份配置(Widget),可以創(chuàng)建多個(gè)實(shí)例(Element)。

十、說一下什么是狀態(tài)管理,為什么需要它?

首先狀態(tài)其實(shí)是一個(gè)概念上的東西,區(qū)分全局狀態(tài)和局部狀態(tài)。

局部狀態(tài)比如說一個(gè)控件中輸入的信息,全局狀態(tài)比如是登陸后從后臺(tái)請(qǐng)求回來的 userId。

當(dāng)全局狀態(tài)越來越多,多個(gè)頁面共享一個(gè)狀態(tài)時(shí),我們就需要管理它。

常用的狀態(tài)管理有:

ScopedModel
BLoC
Redux / FishRedux
Provider

十一、說一下 BLoC 模式?
具體可以查看: Vadaski - Flutter | 狀態(tài)管理探索篇——BLoC(三)

BLoC是一種利用reactive programming方式構(gòu)建應(yīng)用的方法,這是一個(gè)由流構(gòu)成的完全異步的世界。

Bloc

十一、如何統(tǒng)一管理錯(cuò)誤頁面?

我們都知道,如果在 Flutter 當(dāng)中出錯(cuò)的話,那就是一片紅。
可以使用 ErrorWidget.builder 來自定義一個(gè) Widget 就 ok 了。

十二、Stateless Widget和Stateful Widget區(qū)別
Stateless Widget和Stateful Widget區(qū)別
StatelessWidget用于不需要維護(hù)狀態(tài)的場(chǎng)景.,StatefulWidget用于狀態(tài)會(huì)發(fā)生變化的場(chǎng)景,它們都繼承自Widget

StatelessWidget,重寫了createElement()方法,StatelessElement 間接繼承自Element類,

@override
StatelessElement createElement() => new StatelessElement(this);

StatefulWidget也是重寫了createElement()方法,不同的是返回的Element 對(duì)象并不相同;另StatefulWidget類中添加了一個(gè)新的接口createState()。

StatefulElement 間接繼承自Element類,與StatefulWidget相對(duì)應(yīng)(作為其配置數(shù)據(jù))。StatefulElement中可能會(huì)多次調(diào)用createState()來創(chuàng)建狀態(tài)(State)對(duì)象。
createState() 用于創(chuàng)建和Stateful widget相關(guān)的狀態(tài),它在Stateful widget的生命周期中可能會(huì)被多次調(diào)用。例如,當(dāng)一個(gè)Stateful widget同時(shí)插入到widget樹的多個(gè)位置時(shí),F(xiàn)lutter framework就會(huì)調(diào)用該方法為每一個(gè)位置生成一個(gè)獨(dú)立的State實(shí)例,其實(shí),本質(zhì)上就是一個(gè)StatefulElement對(duì)應(yīng)一個(gè)State實(shí)例。

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);

  @override
  StatefulElement createElement() => new StatefulElement(this);

  @protected
  State createState();
}

State生命周期
一個(gè)StatefulWidget類會(huì)對(duì)應(yīng)一個(gè)State類,State表示與其對(duì)應(yīng)的StatefulWidget要維護(hù)的狀態(tài),

initState()
界面初始化狀態(tài)時(shí)調(diào)用

didChangeDependencies()
當(dāng)state狀態(tài)對(duì)象發(fā)生變化時(shí)調(diào)用(典型的場(chǎng)景是當(dāng)系統(tǒng)語言Locale或應(yīng)用主題改變時(shí),F(xiàn)lutter framework會(huì)通知widget調(diào)用此回調(diào)。)

build()
主要是用于構(gòu)建Widget子樹,調(diào)用時(shí)機(jī)如下:
在調(diào)用initState()之后。
在調(diào)用didUpdateWidget()之后。
在調(diào)用setState()之后。
在調(diào)用didChangeDependencies()之后。
在State對(duì)象從樹中一個(gè)位置移除后(會(huì)調(diào)用deactivate)又重新插入到樹的其它位置之后。

reassemble()
主要用于調(diào)試,熱重載時(shí)調(diào)用,release環(huán)境下不會(huì)調(diào)用

didUpdateWidget()
用于更新widget ,Widget.canUpdate返回true則會(huì)調(diào)用此回調(diào)

deactivate()
從widget樹中移除State對(duì)象t時(shí)調(diào)用(位置交換)

dispose()
從widget樹中移除State對(duì)象,并不再插入此State對(duì)象時(shí)調(diào)用(一般用于釋放資源)

十三、為什么第一個(gè)Flutter應(yīng)用構(gòu)建需要這么長(zhǎng)時(shí)間?
首次構(gòu)建Flutter應(yīng)用時(shí), 將花費(fèi)更長(zhǎng)的時(shí)間。這是因?yàn)镕lutter構(gòu)建了設(shè)備專用的APK或IPA文件。因此, Gradle和Xcode用于構(gòu)建文件, 需要很長(zhǎng)時(shí)間。

對(duì)象鎖和類鎖

dart是值傳遞還是引用傳遞

await for使用

十四、使用mixins的條件是什么?

因?yàn)閙ixins使用的條件,隨著Dart版本一直在變,這里講的是Dart2.1中使用mixins的條件:

mixins類只能繼承自object

mixins類不能有構(gòu)造函數(shù)

一個(gè)類可以mixins多個(gè)mixins類

可以mixins多個(gè)類,不破壞Flutter的單繼承

十五、Future和Isolate有什么區(qū)別?

future是異步編程,調(diào)用本身立即返回,并在稍后的某個(gè)時(shí)候執(zhí)行完成時(shí)再獲得返回結(jié)果。在普通代碼中可以使用await 等待一個(gè)異步調(diào)用結(jié)束。

isolate是并發(fā)編程,Dartm有并發(fā)時(shí)的共享狀態(tài),所有Dart代碼都在isolate中運(yùn)行,包括最初的main()。每個(gè)isolate都有它自己的堆內(nèi)存,意味著其中所有內(nèi)存數(shù)據(jù),包括全局?jǐn)?shù)據(jù),都僅對(duì)該isolate可見,它們之間的通信只能通過傳遞消息的機(jī)制完成,消息則通過端口(port)收發(fā)。isolate只是一個(gè)概念,具體取決于如何實(shí)現(xiàn),比如在Dart VM中一個(gè)isolate可能會(huì)是一個(gè)線程,在Web中可能會(huì)是一個(gè)Web Worker。

十六、Flutter中的Widget、State、Context 的核心概念?是為了解決什么問題?

Widget: 在Flutter中,幾乎所有東西都是Widget。將一個(gè)Widget想象為一個(gè)可視化的組件(或與應(yīng)用可視化方面交互的組件),當(dāng)你需要構(gòu)建與布局直接或間接相關(guān)的任何內(nèi)容時(shí),你正在使用Widget。

Widget樹: Widget以樹結(jié)構(gòu)進(jìn)行組織。包含其他Widget的widget被稱為父Widget(或widget容器)。包含在父widget中的widget被稱為子Widget。

Context: 僅僅是已創(chuàng)建的所有Widget樹結(jié)構(gòu)中的某個(gè)Widget的位置引用。簡(jiǎn)而言之,將context作為widget樹的一部分,其中context所對(duì)應(yīng)的widget被添加到此樹中。一個(gè)context只從屬于一個(gè)widget,它和widget一樣是鏈接在一起的,并且會(huì)形成一個(gè)context樹。

State: 定義了StatefulWidget實(shí)例的行為,它包含了用于”交互/干預(yù)“Widget信息的行為和布局。應(yīng)用于State的任何更改都會(huì)強(qiáng)制重建Widget。

這些狀態(tài)的引入,主要是為了解決多個(gè)部件之間的交互和部件自身狀態(tài)的維護(hù)。

十七、什么是Navigator? MaterialApp做了什么?

Navigator是在Flutter中負(fù)責(zé)管理維護(hù)頁面堆棧的導(dǎo)航器。MaterialApp在需要的時(shí)候,會(huì)自動(dòng)為我們創(chuàng)建Navigator。Navigator.of(context),會(huì)使用context來向上遍歷Element樹,找到MaterialApp提供的_NavigatorState再調(diào)用其push/pop方法完成導(dǎo)航操作。

十八、簡(jiǎn)述Flutter的線程管理模型

默認(rèn)情況下,F(xiàn)lutter Engine層會(huì)創(chuàng)建一個(gè)Isolate,并且Dart代碼默認(rèn)就運(yùn)行在這個(gè)主Isolate上。必要時(shí)可以使用spawnUri和spawn兩種方式來創(chuàng)建新的Isolate,在Flutter中,新創(chuàng)建的Isolate由Flutter進(jìn)行統(tǒng)一的管理。

事實(shí)上,F(xiàn)lutter Engine自己不創(chuàng)建和管理線程,F(xiàn)lutter Engine線程的創(chuàng)建和管理是Embeder負(fù)責(zé)的,Embeder指的是將引擎移植到平臺(tái)的中間層代碼,F(xiàn)lutter Engine層的架構(gòu)示意圖如下圖所示。


截屏2021-11-18 上午10.22.00.png

Flutter的架構(gòu)中,Embeder提供四個(gè)Task Runner,分別是Platform Task Runner、UI Task Runner Thread、GPU Task Runner和IO Task Runner,每個(gè)Task Runner負(fù)責(zé)不同的任務(wù),F(xiàn)lutter Engine不在乎Task Runner運(yùn)行在哪個(gè)線程,但是它需要線程在整個(gè)生命周期里面保持穩(wěn)定。

十九、請(qǐng)簡(jiǎn)單介紹下Flutter框架,以及它的優(yōu)缺點(diǎn)?

Flutter是Google推出的一套開源跨平臺(tái)UI框架,可以快速地在Android、iOS和Web平臺(tái)上構(gòu)建高質(zhì)量的原生用戶界面。同時(shí),F(xiàn)lutter還是Google新研發(fā)的Fuchsia操作系統(tǒng)的默認(rèn)開發(fā)套件。在全世界,F(xiàn)lutter正在被越來越多的開發(fā)者和組織使用,并且Flutter是完全免費(fèi)、開源的。Flutter采用現(xiàn)代響應(yīng)式框架構(gòu)建,其中心思想是使用組件來構(gòu)建應(yīng)用的UI。當(dāng)組件的狀態(tài)發(fā)生改變時(shí),組件會(huì)重構(gòu)它的描述,F(xiàn)lutter會(huì)對(duì)比之前的描述,以確定底層渲染樹從當(dāng)前狀態(tài)轉(zhuǎn)換到下一個(gè)狀態(tài)所需要的最小更改。

優(yōu)點(diǎn)

熱重載(Hot Reload),利用Android Studio直接一個(gè)ctrl+s就可以保存并重載,模擬器立馬就可以看見效果,相比原生冗長(zhǎng)的編譯過程強(qiáng)很多;

一切皆為Widget的理念,對(duì)于Flutter來說,手機(jī)應(yīng)用里的所有東西都是Widget,通過可組合的空間集合、豐富的動(dòng)畫庫(kù)以及分層課擴(kuò)展的架構(gòu)實(shí)現(xiàn)了富有感染力的靈活界面設(shè)計(jì);

借助可移植的GPU加速的渲染引擎以及高性能本地代碼運(yùn)行時(shí)以達(dá)到跨平臺(tái)設(shè)備的高質(zhì)量用戶體驗(yàn)。簡(jiǎn)單來說就是:最終結(jié)果就是利用Flutter構(gòu)建的應(yīng)用在運(yùn)行效率上會(huì)和原生應(yīng)用差不多。

缺點(diǎn)

不支持熱更新;

三方庫(kù)有限,需要自己造輪子;

Dart語言編寫,增加了學(xué)習(xí)難度,并且學(xué)習(xí)了Dart之后無其他用處,相比JS和Java來說。

二十、 介紹下Flutter的理念架構(gòu)

截屏2021-11-18 上午10.23.27.png

由上圖可知,F(xiàn)lutter框架自下而上分為Embedder、Engine和Framework三層。其中,Embedder是操作系統(tǒng)適配層,實(shí)現(xiàn)了渲染

Surface設(shè)置,線程設(shè)置,以及平臺(tái)插件等平臺(tái)相關(guān)特性的適配;Engine層負(fù)責(zé)圖形繪制、文字排版和提供Dart運(yùn)行時(shí),Engine層具有獨(dú)立虛擬機(jī),正是由于它的存在,F(xiàn)lutter程序才能運(yùn)行在不同的平臺(tái)上,實(shí)現(xiàn)跨平臺(tái)運(yùn)行;Framework層則是使用Dart編寫的一套基礎(chǔ)視圖庫(kù),包含了動(dòng)畫、圖形繪制和手勢(shì)識(shí)別等功能,是使用頻率最高的一層。

二十一、介紹下FFlutter的FrameWork層和Engine層,以及它們的作用

Flutter的FrameWork層是用Drat編寫的框架(SDK),它實(shí)現(xiàn)了一套基礎(chǔ)庫(kù),包含Material(Android風(fēng)格UI)和Cupertino(iOS風(fēng)格)的UI界面,下面是通用的Widgets(組件),之后是一些動(dòng)畫、繪制、渲染、手勢(shì)庫(kù)等。這個(gè)純

Dart實(shí)現(xiàn)的 SDK被封裝為了一個(gè)叫作 dart:ui的 Dart庫(kù)。我們?cè)谑褂?Flutter寫

App的時(shí)候,直接導(dǎo)入這個(gè)庫(kù)即可使用組件等功能。

Flutter的Engine層是Skia

2D的繪圖引擎庫(kù),其前身是一個(gè)向量繪圖軟件,Chrome和 Android均采用 Skia作為繪圖引擎。Skia提供了非常友好的

API,并且在圖形轉(zhuǎn)換、文字渲染、位圖渲染方面都提供了友好、高效的表現(xiàn)。Skia是跨平臺(tái)的,所以可以被嵌入到 Flutter的 iOS

SDK中,而不用去研究 iOS閉源的 Core Graphics / Core Animation。Android自帶了 Skia,所以

Flutter Android SDK要比 iOS SDK小很多。

二十二、 簡(jiǎn)述Flutter 的熱重載

Flutter 的熱重載是基于 JIT 編譯模式的代碼增量同步。由于 JIT 屬于動(dòng)態(tài)編譯,能夠?qū)?Dart 代碼編譯成生成中間代碼,讓 Dart VM 在運(yùn)行時(shí)解釋執(zhí)行,因此可以通過動(dòng)態(tài)更新中間代碼實(shí)現(xiàn)增量同步。

熱重載的流程可以分為

5 步,包括:掃描工程改動(dòng)、增量編譯、推送更新、代碼合并、Widget 重建。Flutter 在接收到代碼變更后,并不會(huì)讓 App

重新啟動(dòng)執(zhí)行,而只會(huì)觸發(fā) Widget 樹的重新繪制,因此可以保持改動(dòng)前的狀態(tài),大大縮短了從代碼修改到看到修改產(chǎn)生的變化之間所需要的時(shí)間。

另一方面,由于涉及到狀態(tài)的保存與恢復(fù),涉及狀態(tài)兼容與狀態(tài)初始化的場(chǎng)景,熱重載是無法支持的,如改動(dòng)前后 Widget 狀態(tài)無法兼容、全局變量與靜態(tài)屬性的更改、main 方法里的更改、initState 方法里的更改、枚舉和泛型的更改等。

可以發(fā)現(xiàn),熱重載提高了調(diào)試 UI 的效率,非常適合寫界面樣式這樣需要反復(fù)查看修改效果的場(chǎng)景。但由于其狀態(tài)保存的機(jī)制所限,熱重載本身也有一些無法支持的邊界。

二十三、簡(jiǎn)述Flutter的繪制流程

截屏2021-11-18 上午10.26.15.png

Flutter只關(guān)心向

GPU提供視圖數(shù)據(jù),GPU的 VSync信號(hào)同步到 UI線程,UI線程使用 Dart來構(gòu)建抽象的視圖結(jié)構(gòu),這份數(shù)據(jù)結(jié)構(gòu)在

GPU線程進(jìn)行圖層合成,視圖數(shù)據(jù)提供給 Skia引擎渲染為 GPU數(shù)據(jù),這些數(shù)據(jù)通過 OpenGL或者 Vulkan提供給 GPU。

二十四、dart是值傳遞還是引用傳遞?

首先給個(gè)結(jié)論,dart是值傳遞。我們每次調(diào)用函數(shù),傳遞過去的都是對(duì)象的內(nèi)存地址,而不是這個(gè)對(duì)象的復(fù)制。
我們只要記住一點(diǎn),參數(shù)是把內(nèi)存地址傳過去了,如果對(duì)這個(gè)內(nèi)存地址上的對(duì)象修改,那么其他位置的引用該內(nèi)存地址的變量值也會(huì)修改。千萬要記住dart中一切都是對(duì)象。

二十五、flutter特性
Flutter 是 Google 推出并開源的移動(dòng)應(yīng)用開發(fā)框架,主打跨平臺(tái)、高保真、高性能。開發(fā)者可以通過 Dart 語言開發(fā) App,一套代碼同時(shí)運(yùn)行在 iOS 和 Android平臺(tái)。 Flutter 提供了豐富的組件、接口,開發(fā)者可以很快地為 Flutter 添加 Native 擴(kuò)展。

》高性能

Flutter 高性能主要靠?jī)牲c(diǎn)來保證:

第一:Flutter APP 采用 Dart 語言開發(fā)。Dart 在 JIT(即時(shí)編譯)模式下,執(zhí)行速度與 JavaScript 基本持平。但是 Dart 支持 AOT,當(dāng)以 AOT模式運(yùn)行時(shí),JavaScript 便遠(yuǎn)遠(yuǎn)追不上了。執(zhí)行速度的提升對(duì)高幀率下的視圖數(shù)據(jù)計(jì)算很有幫助。

第二:Flutter 使用自己的渲染引擎來繪制 UI ,布局?jǐn)?shù)據(jù)等由 Dart 語言直接控制,所以在布局過程中不需要像 RN 那樣要在 JavaScript 和 Native 之間通信,這在一些滑動(dòng)和拖動(dòng)的場(chǎng)景下具有明顯優(yōu)勢(shì),因?yàn)樵诨瑒?dòng)和拖動(dòng)過程往往都會(huì)引起布局發(fā)生變化,所以 JavaScript 需要和 Native 之間不停的同步布局信息,這和在瀏覽器中JavaScript 頻繁操作 DOM 所帶來的問題是類似的,都會(huì)導(dǎo)致比較可觀的性能開銷。

》效率高

Dart 運(yùn)行時(shí)和編譯器支持 Flutter 的兩個(gè)關(guān)鍵特性的組合:

基于 JIT 的快速開發(fā)周期:Flutter 在開發(fā)階段采用,采用 JIT 模式,這樣就避免了每次改動(dòng)都要進(jìn)行編譯,極大的節(jié)省了開發(fā)時(shí)間;

基于 AOT 的發(fā)布包: Flutter 在發(fā)布時(shí)可以通過 AOT 生成高效的機(jī)器碼以保證應(yīng)用性能。而 JavaScript 則不具有這個(gè)能力。

二十六、PlatformView
platform view 就是 AndroidView 和 UIKitView 的總稱,允許將 native view 嵌入到了 flutter widget 體系中,完成 Datr 代碼對(duì) native view 的控制。
https://www.cnblogs.com/ClientInfra/articles/15248565.html(原理)
二十七、flutter從加載到顯示
https://mp.weixin.qq.com/s/ncViI0KGikPUIZ7BlEHGOA

main是的作用及調(diào)用時(shí)機(jī)
flutter為什么既要支持運(yùn)行時(shí)編譯又要支持運(yùn)行前編譯
dart傳參的基本方式
都了解過哪些跨平臺(tái)技術(shù)并聊一下其優(yōu)缺點(diǎn)
Flutter生命周期介紹
說一下你了解的一些dart語法規(guī)范
js了解多少
final與const區(qū)別,
with關(guān)鍵字的作用
?、??、??=三者的區(qū)別
如何捕捉異常,同步異步
Map如何轉(zhuǎn)Modal
stateless和stateful的區(qū)別,為什么根Widget要使用stateless,
如何在啟動(dòng)圖消失之前初始化信息
Provider的使用,
使用GestureDetector碰到的坑
flutter的兩種路由方式
如何使用Controller調(diào)用子節(jié)點(diǎn)的方法
動(dòng)畫及自定義動(dòng)畫
讓你影響深刻的一些坑及填坑經(jīng)驗(yàn)
如何做全局路由
flutter項(xiàng)目如何抓包
聊一聊flutterboost的使用場(chǎng)景
如何將Flutter 模塊嵌入純Native項(xiàng)目,及都需要注意哪些事項(xiàng)
如何維護(hù)一個(gè)路由棧
說下Widgets、RenderObjects 和 Elements的關(guān)系
Flutter 是如何與原生Android、iOS進(jìn)行通信的?
設(shè)計(jì)一個(gè)日志系統(tǒng),
flutter項(xiàng)目的性能優(yōu)化
Flutter里的各種key
了解過持續(xù)化集成不
設(shè)計(jì)一個(gè)性能監(jiān)控系統(tǒng)
flutter的通信原理
flutter渲染原理
事件循環(huán)
widget的root節(jié)點(diǎn)
mixin extends implement之間的關(guān)系(除了extends其他的沒怎么用過。。)
jvm內(nèi)存模型(感覺這個(gè)是面試官可憐我,看我什么都不會(huì)才問的=。=)
介紹下inheritwidget
flutter中都有哪幾種線程?
Platform Task Runner
UI Task Runner
GPU Task Runner
IO Task Runner
什么是狀態(tài)管理,了解哪些狀態(tài)管理框架
如何在flutter中使用rootBundle加載圖像?
如何取消Future.delayed函數(shù)調(diào)用
Provider.of(context,listen:false)是否等同于context.read()?
如何制作一個(gè)ListView.builder從一個(gè)特定的索引開始
初始化Dart VM時(shí)Flutter應(yīng)用程序出現(xiàn)錯(cuò)誤:錯(cuò)誤的完整快照版本,預(yù)期為“ 8343…”發(fā)現(xiàn)為“ 46b2…”
從Firebase Datasnapshot獲取JSON密鑰值
Flutter:如何將變量從StatelessWidget傳遞到StatefulWidget

什么是flutter里的key? 有什么用?
key是Widgets,Elements和SemanticsNodes的標(biāo)識(shí)符。

key有LocalKey 和 GlobalKey兩種。

LocalKey 如果要修改集合中的控件的順序或數(shù)量。GlobalKey允許 Widget 在應(yīng)用中的任何位置更改父級(jí)而不會(huì)丟失 State。

在什么場(chǎng)景下使用profile mode?
profile model 是用來評(píng)估app性能的,profile model 和release mode是相似的,只有保留了一些需要評(píng)估app性能的debug功能。在模擬器上profile model是不可用的。

怎么做到只在debug mode運(yùn)行代碼?
foundation有一個(gè)靜態(tài)的變量kReleaseMode來表示是否是release mode

Dart的一些重要概念?
在Dart中,一切都是對(duì)象,所有的對(duì)象都是繼承自O(shè)bject
Dart是強(qiáng)類型語言,但可以用var或 dynamic來聲明一個(gè)變量,Dart會(huì)自動(dòng)推斷其數(shù)據(jù)類型,dynamic類似c#
沒有賦初值的變量都會(huì)有默認(rèn)值null
Dart支持頂層方法,如main方法,可以在方法內(nèi)部創(chuàng)建方法
Dart支持頂層變量,也支持類變量或?qū)ο笞兞?br> Dart沒有public protected private等關(guān)鍵字,如果某個(gè)變量以下劃線(_)開頭,代表這個(gè)變量在庫(kù)中是私有的

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容