重磅!!Flutter 面試知識點集錦

轉載:戀貓de小郭(掘金)

谷歌大會以後,有很多人諮詢了我 Flutter 相關的問題,其中有很多是和麪試相關的,現在一些招聘上也開始羅列 Flutter 相關要求,最後想了想仍是寫一期總結吧,也算是  Flutter  的階段複習。java

⚠️系統完整的學習是必須須要的,這裏只能幫你總結一些知識點,更多的還請查閱 Dart/Flutter 官網。

本篇主要是知識點總結,若有疑問可點擊各文章連接瞭解詳情,或者查閱我 掘金專欄。android

https://juejin.im/user/582aca...git

Dart 部分

其實學習過 JavaScript 或者 Java/Kotlin 的人,在學習 Dart 上幾乎是沒什麼難度的,Dart 綜合了動態語言和靜態語言的特性, 這裏主要提供github

一些不同,或者有意思的概念面試

一、Dart 屬因而強類型語言 ,但能夠用 var  來聲明變量,Dart 會自推導出數據類型,var 其實是編譯期的「語法糖」。dynamic 表示動態類型, 被編譯後,實際是一個 object 類型,在編譯期間不進行任何的類型檢查,而是在運行期進行類型檢查。json

二、Dart 中 if 等語句只支持 bool 類型,switch 支持 String 類型。redux

三、Dart 中數組和 List 是同樣的。數組

四、Dart 中,Runes 表明符號文字 ,  是 UTF-32 編碼的字符串, 用於如 Runes input = new Runes('🖖 👍');安全

五、Dart 支持閉包。閉包

六、Dart 中 number 類型分爲 int 和 double ,沒有 float 類型。

七、Dart 中 級聯操做符 能夠方便配置邏輯,以下代碼:

event
  ..id = 1
  ..type = ""
  ..actor = "";

八、賦值操做符

比較有意思的賦值操做符有:

AA ?? "999"  ///表示若是 AA 爲空,返回999
AA ??= "999" ///表示若是 AA 爲空,給 AA 設置成 999
AA ~/999 ///AA 對於 999 整除

九、可選方法參數

Dart 方法能夠設置 參數默認值 和 指定名稱 。

好比:getDetail(Sting userName, reposName, {branch = "master"}){} 方法,這裏 branch 不設置的話,默認是 「master」 。參數類型 能夠指定或者不指定。

調用效果: 

getRepositoryDetailDao(「aaa", "bbbb", branch: "dev"); 。

十、做用域

Dart 沒有關鍵詞 public 、private 等修飾符,_ 下橫向直接表明 private ,可是有 @protected 註解 。

十一、構造方法

Dart 中的多構造方法,能夠經過命名方法實現。

默認構造方法只能有一個,而經過 Model.empty() 方法能夠建立一個空參數的類,其實方法名稱隨你喜歡,而變量初始化值時,只須要經過 this.name 在構造方法中指定便可:

class ModelA {
  String name;
  String tag;

  //默認構造方法,賦值給name和tag
  ModelA(this.name, this.tag);

  //返回一個空的ModelA
  ModelA.empty();

  //返回一個設置了name的ModelA
  ModelA.forName(this.name);
}

十二、getter setter 重寫

Dart 中全部的基礎類型、類等都繼承 Object ,默認值是 NULL, 自帶 getter 和 setter ,而若是是 final 或者 const 的話,那麼它只有一個 getter 方法,Object  都支持 getter、setter 重寫:

@override
  Size get preferredSize {
    return Size.fromHeight(kTabHeight + indicatorWeight);
  }

1三、Assert(斷言)

assert 只在檢查模式有效,在開發過程當中,assert(unicorn == null); 只有條件爲真才正常,不然直接拋出異常,通常用在開發過程當中,某些地方不該該出現什麼狀態的判斷。

1四、重寫運算符,以下所示重載 operator 後對類進行 +/- 操做。

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  ···
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}

支持重載的操做符 :

15. 類、接口、繼承

Dart 中沒有接口,類均可以做爲接口,把某個類當作接口實現時,只須要使用 implements ,而後複寫父類方法便可。

Dart 中支持 mixins ,按照出現順序應該爲extends 、 mixins 、implements 。

Zone

Dart 中可經過 Zone 表示指定代碼執行的環境,相似一個沙盒概念,在 Flutter 中 C++ 運行 Dart 也是在 _runMainZoned 內執行 runZoned 方法啓動,而咱們也能夠經過 Zone ,在運行環境內捕獲全局異常等信息:

  runZoned(() {
    runApp(FlutterReduxApp());
  }, onError: (Object obj, StackTrace stack) {
    print(obj);
    print(stack);
  });

同時你能夠給 runZoned 註冊方法,在須要時執行回調,以下代碼所示,這樣的在一個 Zone 內任何地方,只要能獲取 onData 這個 ZoneUnaryCallback,就均可以調用到 handleData

///最終須要處理的地方
handleData(result) {
  print("VVVVVVVVVVVVVVVVVVVVVVVVVVV");
  print(result);
}

///返回獲得一個 ZoneUnaryCallback 
var onData = Zone.current.registerUnaryCallback<dynamic, int>(handleData);

///執行 ZoneUnaryCallback 返回數據
Zone.current.runUnary(onData, 2);

異步邏輯能夠經過 scheduleMicrotask 能夠插入異步執行方法:

Zone.current.scheduleMicrotask((){
  //todo something
});

更多可參看 :

《Flutter完整開發實戰詳解(11、全面深刻理解Stream)》

https://juejin.im/post/5cc2ac...

Future

Future 簡單了說就是對 Zone 的封裝使用。

好比 Future.microtask 中主要是執行了 Zone 的 scheduleMicrotask ,而 result._complete 最後調用的是 _zone.runUnary 等等。

  factory Future.microtask(FutureOr<T> computation()) {
    _Future<T> result = new _Future<T>();
    scheduleMicrotask(() {
      try {
        result._complete(computation());
      } catch (e, s) {
        _completeWithErrorCallback(result, e, s);
      }
    });
    return result;
  }

Dart 中可經過 async/await 或者 Future 定義異步操做,而事實上 async/await 也只是語法糖,最終仍是經過編譯器轉爲 Future。

有興趣看這裏 :

generators

code_generator.dart

Flutter完整開發實戰詳解(11、全面深刻理解Stream)

Stream

Stream 也是有對Zone 的另一種封裝使用。

Dart 中另一種異步操做, async / yield 或者 Stream 可定義 Stream 異步,  async / yield 也只是語法糖,最終仍是經過編譯器轉爲 Stream。

Stream 還支持同步操做。

1)、Stream 中主要有 Stream 、 StreamController 、StreamSink 和 StreamSubscription 四個關鍵對象,大體能夠總結爲:

StreamController :如類名描述,用於整個  Stream 過程的控制,提供各種接口用於建立各類事件流。

StreamSink :通常做爲事件的入口,提供如 add   ,   addStream   等。

Stream :事件源自己,通常可用於監聽事件或者對事件進行轉換,如 listen 、where 。

StreamSubscription :事件訂閱後的對象,表面上用於管理訂閱過等各種操做,如 cacenl 、pause ,同時在內部也是事件的中轉關鍵。

2)、通常經過 StreamController 建立 Stream;經過 StreamSink 添加事件;經過 Stream 監聽事件;經過 StreamSubscription 管理訂閱。

3)、Stream 中支持各類變化,好比map 、expand 、where 、take 等操做,同時支持轉換爲 Future 。

更多可參看 :

《Flutter完整開發實戰詳解(11、全面深刻理解Stream)》

https://juejin.im/post/5cc2ac...

Flutter 部分

Flutter 和 React Native 不一樣主要在於 Flutter UI是直接經過 skia 渲染的 ,而 React Native 是將 js 中的控件轉化爲原生控件,經過原生去渲染的  ,相關更多可查看:

《移動端跨平臺開發的深度解析》

https://juejin.im/post/5b395e...

Flutter 中存在 Widget 、 Element 、RenderObject 、Layer 四棵樹,其中Widget 與 Element 是多對一的關係 

Element  中持有Widget 和 RenderObject , 而 Element 與 RenderObject 是一一對應的關係 ,

當 RenderObject 的 isRepaintBoundary 爲 true 時,那麼個區域造成一個 Layer,因此不是每一個 RenderObject 都具備 Layer 的,由於這受 isRepaintBoundary 的影響。

更多相關可查閱 

《Flutter完整開發實戰詳解(9、 深刻繪製原理)》

https://juejin.im/post/5ca0e0...

Flutter 中 Widget 不可變,每次保持在一幀,若是發生改變是經過 State 實現跨幀狀態保存,而真實完成佈局和繪製數組的是 RenderObject ,  Element 充當二者的橋樑, State 就是保存在 Element 中。

Flutter 中的 BuildContext 只是接口,而  Element  實現了它。

Flutter 中 setState  實際上是調用了 markNeedsBuild  ,該方法內部標記此Element 爲 Dirty ,而後在下一幀 WidgetsBinding.drawFrame 纔會被繪製,這能夠看出 setState 並非當即生效的。

Flutter 中 RenderObject 在 attch/layout 以後會經過 markNeedsPaint();  使得頁面重繪,流程大概以下:

經過isRepaintBoundary 往上肯定了更新區域,經過 requestVisualUpdate 方法觸發更新往下繪製。

正常狀況 RenderObject 的佈局相關方法調用順序是 :layout ->  performResize -> performLayout -> markNeedsPaint ,  可是用戶通常不會直接調用  layout,而是經過  markNeedsLayout ,具體流程以下:

Flutter 中通常 json 數據從 String 轉爲 Object 的過程當中都須要先通過 Map 類型。

Flutter 中 InheritedWidget 通常用於狀態共享,如Theme 、Localizations 、 MediaQuery 等,都是經過它實現共享狀態,這樣咱們能夠經過 context 去獲取共享的狀態,好比 ThemeData theme = Theme.of(context);

在 Element 的 inheritFromWidgetOfExactType 方法實現裏,有一個 Map<Type, InheritedElement> _inheritedWidgets 的對象。

_inheritedWidgets 通常狀況下是空的,只有當父控件是 InheritedWidget 或者自己是 InheritedWidgets 時纔會有被初始化,而當父控件是 InheritedWidget  時,這個 Map 會被一級一級往下傳遞與合併 。

因此當咱們經過 context 調用 inheritFromWidgetOfExactType 時,就能夠往上查找到父控件的 Widget 。

Flutter 中默認主要經過 runtimeType 和 key 判斷更新:

static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
}

Flutter 中的生命週期

initState() 表示當前 State 將和一個 BuildContext 產生關聯,可是此時BuildContext 沒有徹底裝載完成,若是你須要在該方法中獲取 BuildContext ,能夠 new Future.delayed(const Duration(seconds: 0, (){//context}); 一下。

didChangeDependencies() 在 initState() 以後調用,當 State 對象的依賴關係發生變化時,該方法被調用,初始化時也會調用。

deactivate() 當 State 被暫時從視圖樹中移除時,會調用這個方法,同時頁面切換時,也會調用。

dispose()  Widget 銷燬了,在調用這個方法以前,總會先調用 deactivate()。

didUpdateWidge 當  widget 狀態發生變化時,會調用。

經過 StreamBuilder 和 FutureBuilder 咱們能夠快速使用 Stream 和 Future 快速構建咱們的異步控件: 

 《Flutter完整開發實戰詳解(11、全面深刻理解Stream)》

https://juejin.im/post/5cc2ac...

Flutter 中 runApp 啓動入口實際上是一個 WidgetsFlutterBinding ,它主要是經過 BindingBase 的子類 GestureBinding  、ServicesBinding  、 SchedulerBinding  、PaintingBinding  、SemanticsBinding  、 RendererBinding  、WidgetsBinding 等,經過 mixins 的組合而成的。

Flutter 中的 Dart 的線程是以事件循環和消息隊列的形式存在,包含兩個任務隊列,一個是 microtask 內部隊列,一個是 event 外部隊列,而 microtask 的優先級又高於 event 。

由於 microtask 的優先級又高於 event, 同時會阻塞event 隊列,因此若是 microtask 太多就可能會對觸摸、繪製等外部事件形成阻塞卡頓哦。

Flutter 中存在四大線程,分別爲 UI Runner、GPU Runner、IO Runner, Platform Runner (原生主線程) ,同時在 Flutter 中能夠經過 isolate 或者 compute 執行真正的跨線程異步操做。

PlatformView

Flutter 中經過 PlatformView 能夠嵌套原生 View 到 Flutter UI 中,這裏面實際上是使用了 Presentation + VirtualDisplay + Surface 等實現的,大體原理就是:

使用了相似副屏顯示的技術,VirtualDisplay 類表明一個虛擬顯示器,調用 DisplayManager 的 createVirtualDisplay() 方法,將虛擬顯示器的內容渲染在一個 Surface 控件上,而後將 Surface 的 id 通知給 Dart,讓 engine 繪製時,在內存中找到對應的 Surface 畫面內存數據,而後繪製出來。em...  實時控件截圖渲染顯示技術。

Flutter 的 Debug 下是 JIT 模式,release下是AOT模式。

Flutter 中能夠經過 mixins AutomaticKeepAliveClientMixin  ,而後重寫 wantKeepAlive 保持住頁面,記得在被保持住的頁面 build 中調用 super.build 。(由於 mixins 特性)。

Flutter 手勢事件主要是經過競技判斷的:

主要有 hitTest 把全部須要處理的控件對應的 RenderObject , 從  child 到 parent 所有組合成列表,從最裏面一直添加到最外層。

而後從隊列頭的 child 開始 for 循環執行 handleEvent 方法,執行 handleEvent 的過程不會被攔截打斷。

通常狀況下 Down 事件不會決出勝利者,大部分時候是在 MOVE 或者 UP 的時候纔會決出勝利者。

競技場關閉時只有一個的就直接勝出響應,沒有勝利者就拿排在隊列第一個強制勝利響應。

同時還有 didExceedDeadline 處理按住時的 Down 事件額外處理,同時手勢處理通常在 GestureRecognizer 的子類進行。

更多詳細請查看:

《Flutter完整開發實戰詳解(十3、全面深刻觸摸和滑動原理)》

https://juejin.im/post/5cd548...

Flutter 中 ListView 滑動其實都是經過改變 ViewPort 中的 child 佈局來實現顯示的。

經常使用狀態管理的:目前有 scope_model 、flutter_redux 、fish_redux 、bloc + Stream 等幾種模式,具體可見 : 

《Flutter完整開發實戰詳解(12、全面深刻理解狀態管理設計)》

https://juejin.im/post/5cc816...

Platform Channel

Flutter 中能夠經過 Platform Channel 讓 Dart 代碼和原生代碼通訊的:

  • BasicMessageChannel :用於傳遞字符串和半結構化的信息。
  • MethodChannel :用於傳遞方法調用(method invocation)。
  • EventChanne l: 用於數據流(event streams)的通訊。

同時 Platform Channel 並不是是線程安全的 ,更多詳細可查閱閒魚技術的 

《深刻理解Flutter Platform Channel》

https://www.jianshu.com/p/395...

其中基礎數據類型映射以下:

Android 啓動頁

Android 中 Flutter 默認啓動時會在 FlutterActivityDelegate.java 中讀取 AndroidManifset.xml 內 meta-data 標籤,其中 io.flutter.app.android.SplashScreenUntilFirstFrame 標誌位若是爲 ture ,就會啓動 Splash 畫面效果(相似IOS的啓動頁面)。

啓動時原生代碼會讀取 android.R.attr.windowBackground  獲得指定的 Drawable , 用於顯示啓動閃屏效果,以後而且經過 flutterView.addFirstFrameListener,在onFirstFrame 中移除閃屏。

好了,暫時都這裏了,有問題修改會或則補充的,後面再加上。

資源推薦

Github :

https://github.com/CarGuo

本文Demo :

https://github.com/CarGuo/sta...

本文代碼 :

https://github.com/CarGuo/GSY...

完整開源項目推薦:

GSYGithubApp Flutter

https://github.com/CarGuo/GSY...

GSYGithubApp React Native

https://github.com/CarGuo/sta...

GSYGithubAppWeex

https://github.com/CarGuo/GSY...

Flutter資料+讀者福利限時分享

Android開發資料+面試架構資料 免費分享 點擊連接 便可領取

《Android架構師必備學習資源免費領取(架構視頻+面試專題文檔+學習筆記)》

相關文章
相關標籤/搜索