摘要:中的的線程是以事件循環和消息隊列的形式存在,包含兩個任務隊列,一個是內部隊列,一個是外部隊列,而的優先級又高于。同時還有處理按住時的事件額外處理,同時手勢處理一般在的子類進行。
谷歌大會之后,有不少人咨詢了我 Flutter 相關的問題,其中有不少是和面試相關的,如今一些招聘上也開始羅列 Flutter 相關要求,最后想了想還是寫一期總結吧,也算是 Flutter 的階段復習。
??系統完整的學習是必須需要的,這里只能幫你總結一些知識點,更多的還請查閱 Dart/Flutter 官網。
本篇主要是知識點總結,如有疑問可點擊各文章鏈接了解詳情,或者查閱我 掘金專欄。
Dart 部分
其實學習過 JavaScript 或者 Java/Kotlin 的人,在學習 Dart 上幾乎是沒什么難度的,Dart 綜合了動態語言和靜態語言的特性, 這里主要提供一些不一樣,或者有意思的概念。
1、Dart 屬于是強類型語言 ,但可以用 var 來聲明變量,Dart 會自推導出數據類型,var 實際上是編譯期的“語法糖”。dynamic 表示動態類型, 被編譯后,實際是一個 object 類型,在編譯期間不進行任何的類型檢查,而是在運行期進行類型檢查。
2、Dart 中 if 等語句只支持 bool 類型,switch 支持 String 類型。
3、Dart 中數組和 List 是一樣的。
4、Dart 中,Runes 代表符號文字 , 是 UTF-32 編碼的字符串, 用于如 Runes input = new Runes("u{1f596} u{1f44d}");
5、Dart 支持閉包。
6、Dart 中 number 類型分為 int 和 double ,沒有 float 類型。
7、Dart 中 級聯操作符 可以方便配置邏輯,如下代碼:
event ..id = 1 ..type = "" ..actor = "";
8、賦值操作符
比較有意思的賦值操作符有:
AA ");
9、可選方法參數
Dart 方法可以設置 參數默認值 和 指定名稱 。
比如: getDetail(Sting userName, reposName, {branch = "master"}){} 方法,這里 branch 不設置的話,默認是 “master” 。參數類型 可以指定或者不指定。調用效果: getRepositoryDetailDao(“aaa", "bbbb", branch: "dev"); 。
10、作用域
Dart 沒有關鍵詞 public 、private 等修飾符,_ 下橫向直接代表 private ,但是有 @protected 注解 。
11、構造方法
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); }
12、getter setter 重寫
Dart 中所有的基礎類型、類等都繼承 Object ,默認值是 NULL, 自帶 getter 和 setter ,而如果是 final 或者 const 的話,那么它只有一個 getter 方法,Object 都支持 getter、setter 重寫:
@override Size get preferredSize { return Size.fromHeight(kTabHeight + indicatorWeight); }
13、Assert(斷言)
assert 只在檢查模式有效,在開發過程中,assert(unicorn == null); 只有條件為真才正常,否則直接拋出異常,一般用在開發過程中,某些地方不應該出現什么狀態的判斷。
14、重寫運算符,如下所示重載 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)); }
支持重載的操作符 :
類、接口、繼承
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
(handleData); ///執行 ZoneUnaryCallback 返回數據 Zone.current.runUnary(onData, 2); 異步邏輯可以通過 scheduleMicrotask 可以插入異步執行方法:
Zone.current.scheduleMicrotask((){ //todo something });
更多可參看 :《Flutter完整開發實戰詳解(十一、全面深入理解Stream)》
Future
Future 簡單了說就是對 Zone 的封裝使用。
比如 Future.microtask 中主要是執行了 Zone 的 scheduleMicrotask ,而 result._complete 最后調用的是 _zone.runUnary 等等。
factory Future.microtask(FutureOr
computation()) { _Future result = new _Future (); 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完整開發實戰詳解(十一、全面深入理解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完整開發實戰詳解(十一、全面深入理解Stream)》
Flutter 部分
Flutter 和 React Native 不同主要在于 Flutter UI是直接通過 skia 渲染的 ,而 React Native 是將 js 中的控件轉化為原生控件,通過原生去渲染的 ,相關更多可查看:《移動端跨平臺開發的深度解析》。
Flutter 中存在 Widget 、 Element 、RenderObject 、Layer 四棵樹,其中 Widget 與 Element 是多對一的關系 ,
Element 中持有Widget 和 RenderObject , 而 Element 與 RenderObject 是一一對應的關系 ,
當 RenderObject 的 isRepaintBoundary 為 true 時,那么個區域形成一個 Layer,所以不是每個 RenderObject 都具有 Layer 的,因為這受 isRepaintBoundary 的影響。
更多相關可查閱 《Flutter完整開發實戰詳解(九、 深入繪制原理)》
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
_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完整開發實戰詳解(十一、全面深入理解Stream)》
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完整開發實戰詳解(十三、全面深入觸摸和滑動原理)》
Flutter 中 ListView 滑動其實都是通過改變 ViewPort 中的 child 布局來實現顯示的。
常用狀態管理的:目前有 scope_model 、flutter_redux 、fish_redux 、bloc + Stream 等幾種模式,具體可見 : 《Flutter完整開發實戰詳解(十二、全面深入理解狀態管理設計)》
Platform Channel
Flutter 中可以通過 Platform Channel 讓 Dart 代碼和原生代碼通信的:
BasicMessageChannel :用于傳遞字符串和半結構化的信息。
MethodChannel :用于傳遞方法調用(method invocation)。
EventChanne l: 用于數據流(event streams)的通信。
同時 Platform Channel 并非是線程安全的 ,更多詳細可查閱閑魚技術的 《深入理解Flutter Platform Channel》
其中基礎數據類型映射如下:
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 : github.com/CarGuo
本文Demo :github.com/CarGuo/stat…
本文代碼 :github.com/CarGuo/GSYG…
完整開源項目推薦:
GSYGithubApp Flutter
GSYGithubApp React Native
GSYGithubAppWeex
文章
《Flutter完整開發實戰詳解(一、Dart語言和Flutter基礎)》
《Flutter完整開發實戰詳解(二、 快速開發實戰篇)》
《Flutter完整開發實戰詳解(三、 打包與填坑篇)》
《Flutter完整開發實戰詳解(四、Redux、主題、國際化)》
《Flutter完整開發實戰詳解(五、 深入探索)》
《Flutter完整開發實戰詳解(六、 深入Widget原理)》
《Flutter完整開發實戰詳解(七、 深入布局原理)》
《Flutter完整開發實戰詳解(八、 實用技巧與填坑)》
《Flutter完整開發實戰詳解(九、 深入繪制原理)》
《Flutter完整開發實戰詳解(十、 深入圖片加載流程)》
《Flutter完整開發實戰詳解(十一、全面深入理解Stream)》
《Flutter完整開發實戰詳解(十二、全面深入理解狀態管理設計)》
《Flutter完整開發實戰詳解(十三、全面深入觸摸和滑動原理)》
《跨平臺項目開源項目推薦》
《移動端跨平臺開發的深度解析》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/6701.html
摘要:獲取的對象范圍方法獲取的是最終應用在元素上的所有屬性對象即使沒有代碼,也會把默認的祖宗八代都顯示出來而只能獲取元素屬性中的樣式。因此對于一個光禿禿的元素,方法返回對象中屬性值如果有就是據我測試不同環境結果可能有差異而就是。 花了很長時間整理的前端面試資源,喜歡請大家不要吝嗇star~ 別只收藏,點個贊,點個star再走哈~ 持續更新中……,可以關注下github 項目地址 https:...
摘要:創建一個創建遵循準則的應用程序時,請為您的應用程序提供一致的外觀結構。例如,如果用戶不小心刪除了一條消息,則他們可以在中使用可選的動作來恢復該消息。來自第三方庫的依賴,是一個來自第三方庫的給用戶彈出提示的實現,它覆蓋了安卓,,及等平臺。 來自Material Design的官方Toast,Snackbars在執...
摘要:基礎問題的的性能及原理之區別詳解備忘筆記深入理解流水線抽象關鍵字修飾符知識點總結必看篇中的關鍵字解析回調機制解讀抽象類與三大特征時間和時間戳的相互轉換為什么要使用內部類對象鎖和類鎖的區別,,優缺點及比較提高篇八詳解內部類單例模式和 Java基礎問題 String的+的性能及原理 java之yield(),sleep(),wait()區別詳解-備忘筆記 深入理解Java Stream流水...
閱讀 1209·2021-11-23 09:51
閱讀 1986·2021-10-08 10:05
閱讀 2344·2019-08-30 15:56
閱讀 1905·2019-08-30 15:55
閱讀 2643·2019-08-30 15:55
閱讀 2494·2019-08-30 13:53
閱讀 3504·2019-08-30 12:52
閱讀 1255·2019-08-29 10:57