概述
Flutter是Google推出的一套開源跨平臺UI框架,可以快速地在Android、iOS和Web平臺上構建高質量的原生使用者介面。在過去的一年裡,Flutter的更新頻率是相當的快,也有很多的公司開始使用它來進行跨平臺應用開發,可以說,將Flutter稱為2019年最流行的跨平臺技術也不為過。
在研究了Hybrid APP、React Native和Weex等技術之後,為在今年的早些時候也入了Flutter的坑。總的來說,不管是從社群和社群的活躍來看,還是從技術的水準上來看,Flutter無疑是最優秀的,特別是Google將Flutter列為重點推廣專案之後,全世界掀起了一股學習Flutter的熱潮。
在國內,除了阿里、騰訊、美團等大廠外,國內很多的中小團隊也開始使用Flutter來作為移動應用開發的首選,並且很多公司在移動招聘方面也要求具有Flutter開發的背景。Flutter 的面試題主要分為兩個Dart和Flutter部分,下面是一些常見的面試題。
Dart
1. Dart 當中的 「..」表示什麼意思? Dart 當中的 「..」意思是 「級聯操作符」,為了方便配置而使用。「..」和「.」不同的是 呼叫「..」後返回的相當於是 this,而「.」返回的則是該方法返回的值 。
2. Dart 的作用域 Dart 沒有 「public」「private」等關鍵字,預設就是公開的,私有變數使用 下劃線 _開頭。
3. Dart 是不是單執行緒模型?是如何執行的? Dart 是單執行緒模型,執行的的流程如下圖。
簡單來說,Dart 在單執行緒中是以訊息迴圈機制來執行的,包含兩個任務佇列,一個是“微任務佇列” microtask queue,另一個叫做“事件佇列” event queue。當Flutter應用啟動後,訊息迴圈機制便啟動了。首先會按照先進先出的順序逐個執行微任務佇列中的任務,當所有微任務佇列執行完後便開始執行事件佇列中的任務,事件任務執行完畢後再去執行微任務,如此迴圈往復,生生不息。
4. Dart 是如何實現多工並行的? 前面說過, Dart 是單執行緒的,不存在多執行緒,那如何進行多工並行的呢?其實,Dart的多執行緒和前端的多執行緒有很多的相似之處。Flutter的多執行緒主要依賴Dart的併發程式設計、非同步和事件驅動機制。
簡單的說,在Dart中,一個Isolate物件其實就是一個isolate執行環境的引用,一般來說我們都是通過當前的isolate去控制其他的isolate完成彼此之間的互動,而當我們想要建立一個新的Isolate可以使用Isolate.spawn方法獲取返回的一個新的isolate物件,兩個isolate之間使用SendPort相互傳送訊息,而isolate中也存在了一個與之對應的ReceivePort接受訊息用來處理,但是我們需要注意的是,ReceivePort和SendPort在每個isolate都有一對,只有同一個isolate中的ReceivePort才能接受到當前類的SendPort傳送的訊息並且處理。5. 說一下Dart非同步程式設計中的 Future關鍵字? 前面說過,Dart 在單執行緒中是以訊息迴圈機制來執行的,其中包含兩個任務佇列,一個是“微任務佇列” microtask queue,另一個叫做“事件佇列” event queue。
在Java併發程式設計開發中,經常會使用Future來處理非同步或者延遲處理任務等操作。而在Dart中,執行一個非同步任務同樣也可以使用Future來處理。在 Dart 的每一個 Isolate 當中,執行的優先順序為 : Main > MicroTask > EventQueue。
6. 說一下Dart非同步程式設計中的 Stream資料流? 在Dart中,Stream 和 Future 一樣,都是用來處理非同步程式設計的工具。它們的區別在於,Stream 可以接收多個非同步結果,而Future 只有一個。 Stream 的建立可以使用 Stream.fromFuture,也可以使用 StreamController 來建立和控制。還有一個注意點是:普通的 Stream 只可以有一個訂閱者,如果想要多訂閱的話,要使用 asBroadcastStream()。
7. Stream 有哪兩種訂閱模式?分別是怎麼呼叫的? Stream有兩種訂閱模式:單訂閱(single) 和 多訂閱(broadcast)。單訂閱就是隻能有一個訂閱者,而廣播是可以有多個訂閱者。這就有點類似於訊息服務(Message Service)的處理模式。單訂閱類似於點對點,在訂閱者出現之前會持有資料,在訂閱者出現之後就才轉交給它。而廣播類似於釋出訂閱模式,可以同時有多個訂閱者,當有資料時就會傳遞給所有的訂閱者,而不管當前是否已有訂閱者存在。
Stream 預設處於單訂閱模式,所以同一個 stream 上的 listen 和其它大多數方法只能呼叫一次,呼叫第二次就會報錯。但 Stream 可以通過 transform() 方法(返回另一個 Stream)進行連續呼叫。通過 Stream.asBroadcastStream() 可以將一個單訂閱模式的 Stream 轉換成一個多訂閱模式的 Stream,isBroadcast 屬性可以判斷當前 Stream 所處的模式。
8. await for 如何使用? await for是不斷獲取stream流中的資料,然後執行迴圈體中的操作。它一般用在直到stream什麼時候完成,並且必須等待傳遞完成之後才能使用,不然就會一直阻塞。
Stream<String> stream = new Stream<String>.fromIterable(['不開心', '面試', '沒', '過']);
main() async{
await for(String s in stream){
print(s);
}
}
複製程式碼
9. 說一下 mixin機制? mixin 是Dart 2.1 加入的特性,以前版本通常使用abstract class代替。簡單來說,mixin是為了解決繼承方面的問題而引入的機制,Dart為了支援多重繼承,引入了mixin關鍵字,它最大的特殊處在於: mixin定義的類不能有構造方法,這樣可以避免繼承多個類而產生的父類構造方法衝突。
mixins的物件是類,mixins絕不是繼承,也不是介面,而是一種全新的特性,可以mixins多個類,mixins的使用需要滿足一定條件。
Flutter
1. 請簡單介紹下Flutter框架,以及它的優缺點? Flutter是Google推出的一套開源跨平臺UI框架,可以快速地在Android、iOS和Web平臺上構建高質量的原生使用者介面。同時,Flutter還是Google新研發的Fuchsia作業系統的預設開發套件。在全世界,Flutter正在被越來越多的開發者和組織使用,並且Flutter是完全免費、開源的。Flutter採用現代響應式框架構建,其中心思想是使用元件來構建應用的UI。當元件的狀態發生改變時,元件會重構它的描述,Flutter會對比之前的描述,以確定底層渲染樹從當前狀態轉換到下一個狀態所需要的最小更改。
優點
- 熱過載(Hot Reload),利用Android Studio直接一個ctrl+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. 介紹下FFlutter的FrameWork層和Engine層,以及它們的作用
Flutter的FrameWork層是用Drat編寫的框架(SDK),它實現了一套基礎庫,包含Material(Android風格UI)和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 Graphics / 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由兩部分組成,在初始化時必須要在createState()時初始化一個與之相關的State物件。
6. StatefulWidget 的生命週期 Flutter的Widget分為StatelessWidget和StatefulWidget兩種。其中,StatelessWidget是無狀態的,StatefulWidget是有狀態的,因此實際使用時,更多的是StatefulWidget。StatefulWidget的生命週期如下圖。
- initState():Widget 初始化當前 State,在當前方法中是不能獲取到 Context 的,如想獲取,可以試試 Future.delayed()
- didChangeDependencies():在 initState() 後呼叫,State物件依賴關係發生變化的時候也會呼叫。
- deactivate():當 State 被暫時從檢視樹中移除時會呼叫這個方法,頁面切換時也會呼叫該方法,和Android裡的 onPause 差不多。
- dispose():Widget 銷燬時呼叫。
- didUpdateWidget:Widget 狀態發生變化的時候呼叫。
7. 簡述Widgets、RenderObjects 和 Elements的關係 首先看一下這幾個物件的含義及作用。
- Widget :僅用於儲存渲染所需要的資訊。
- RenderObject :負責管理佈局、繪製等操作。
- Element :才是這顆巨大的控制元件樹上的實體。
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 不會被重建,只是更新而已。
8. 什麼是狀態管理,你瞭解哪些狀態管理框架? Flutter中的狀態和前端React中的狀態概念是一致的。React框架的核心思想是元件化,應用由元件搭建而成,元件最重要的概念就是狀態,狀態是一個元件的UI資料模型,是元件渲染時的資料依據。
Flutter的狀態可以分為全域性狀態和區域性狀態兩種。常用的狀態管理有ScopedModel、BLoC、Redux / FishRedux和Provider。詳細使用情況和差異可以自行了解。
9. 簡述Flutter的繪製流程
Flutter的繪製流程如下圖所示。
Flutter只關心向 GPU提供檢視資料,GPU的 VSync訊號同步到 UI執行緒,UI執行緒使用 Dart來構建抽象的檢視結構,這份資料結構在 GPU執行緒進行圖層合成,檢視資料提供給 Skia引擎渲染為 GPU資料,這些資料通過 OpenGL或者 Vulkan提供給 GPU。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)的通訊。
同時 Platform Channel 並非是執行緒安全的 ,更多詳細可查閱閒魚技術的 《深入理解Flutter Platform Channel》
12. 簡述Flutter 的熱過載 Flutter 的熱過載是基於 JIT 編譯模式的程式碼增量同步。由於 JIT 屬於動態編譯,能夠將 Dart 程式碼編譯成生成中間程式碼,讓 Dart VM 在執行時解釋執行,因此可以通過動態更新中間程式碼實現增量同步。
熱過載的流程可以分為 5 步,包括:掃描工程改動、增量編譯、推送更新、程式碼合併、Widget 重建。Flutter 在接收到程式碼變更後,並不會讓 App 重新啟動執行,而只會觸發 Widget 樹的重新繪製,因此可以保持改動前的狀態,大大縮短了從程式碼修改到看到修改產生的變化之間所需要的時間。
另一方面,由於涉及到狀態的儲存與恢復,涉及狀態相容與狀態初始化的場景,熱過載是無法支援的,如改動前後 Widget 狀態無法相容、全域性變數與靜態屬性的更改、main 方法裡的更改、initState 方法裡的更改、列舉和泛型的更改等。
可以發現,熱過載提高了除錯 UI 的效率,非常適合寫介面樣式這樣需要反覆檢視修改效果的場景。但由於其狀態儲存的機制所限,熱過載本身也有一些無法支援的邊界。
更多詳細的資料可以參考Flutter的Hot Reload是如何實現的
參考資料
1,Flutter 應用程式除錯
2,Flutter For Web入門實戰
3,Flutter開發之路由與導航
4,Flutter 必備開源專案
5,Flutter混合開發
6,Flutter的Hot Reload是如何做到的
7,《Flutter in action》開源
8,Flutter開發之JSON解析
9,Flutter開發之基礎Widgets
10,Flutter開發之導航與路由管理
11,Flutter開發之網路請求
12,Flutter基礎知識
13,Flutter開發之Dart語言基礎
14,Flutter入門與環境搭建
15,移動跨平臺方案對比:WEEX、React Native、Flutter和PWA