摘要:穩(wěn)定性中大部分異常是不會引起應(yīng)用崩潰,更多會在上體現(xiàn)為紅色錯誤堆棧,上異常等等。它是的實現(xiàn)類,實現(xiàn)跨幀保存的就是存放在這里,同時它也充當(dāng)了和之間的橋梁。一整塊的重繪區(qū)域,決定重繪的影響區(qū)域。手勢在手勢中引入了競技的概念事件在中尤為重要。
大家好,我是郭樹煜,Github GSY 系列開源項目的作者,系列包括有 GSYVideoPlayer 、GSYGitGithubApp(FlutterReactNativeKotlinWeex)四大版本,目前總 star 在 17 k+ 左右,主要活躍在掘金社區(qū),id 是戀貓的小郭,主要專欄有《Flutter完整開發(fā)實戰(zhàn)詳解》系列等,平時工作負(fù)責(zé)移動端項目的開發(fā),工作經(jīng)歷從 Android 到 React Native 、Weex 再到如今的 Flutter ,期間也參與過 React 、 Vue 、小程序等相關(guān)的開發(fā),算是一個大前端的選手吧。
這次主要是給大家分享 Flutter 相關(guān)的內(nèi)容,主要涉及做一些實戰(zhàn)和科普性質(zhì)的內(nèi)容。
一、移動開發(fā)的現(xiàn)狀
恰逢最近谷歌 IO 大會結(jié)束,大會后也在線上線下和大家有過交流,總結(jié)了下大家最關(guān)系的問題有:
1、谷歌在 Kotlin-First 的口號下又推廣 Dart + Flutter 沖突嗎?這個問題算是被問得最多的一個,先說觀點:我個人認(rèn)為其實這并不沖突,因為有個 誤區(qū)就是認(rèn)為跨平臺開發(fā)就可以拋棄原生開發(fā)!
如果從事過跨平臺開發(fā)的同學(xué)應(yīng)該知道,平臺提供的功能向來是有限的,而面對產(chǎn)品經(jīng)理的各種 “點歪技能樹” 的需求,很多時候你是需要基于框架外提供支持,常見的就是 混合開發(fā)或者原生插件支持 。
所以這里我表達(dá)的是,目前 Kotlin 和 Dart 更多是相輔相成 ,而一旦業(yè)務(wù)復(fù)雜度到一定程度,跨平臺框架還可能存在降低工作效率的問題,比如針對新需求,需要重復(fù)開發(fā) Android/IOS 的原生插件做支持,這也是 Aribnb 曾經(jīng)選擇放棄 React Native 的原因之一。
與我而言,跨平臺的意義在于解決的是端邏輯的統(tǒng)一 ,至少避免了邏輯重復(fù)實現(xiàn),或者 IOS 和 Android 之間爭論 誰對誰錯 的問題,甚至可以統(tǒng)一到 web 端等等。
2、React Native 和 Flutter 之間的對比
Flutter 作為后來者,難免會被用來和 React Native 進(jìn)行對比,在這個萬物皆是 JS 的時代,Dart 和 Flutter 的出現(xiàn)顯得尤為扎眼。
在設(shè)計上它們有著許多相似之處,響應(yīng)式設(shè)計/async支持/setState更新 等等,同時也有著各種的差異,而大家最為關(guān)心的,無非 性能、支持、上手難易、穩(wěn)定性程度 這四方面:
性能上 Flutter 的確實會比 React Native 好 ,如下圖所示,這是由框架底層決定的,當(dāng)然目前 React Native 也在進(jìn)行下一代的優(yōu)化, 而對此最直觀的數(shù)據(jù)就是:GSY系列 在18年用于閑魚測試下的對比數(shù)據(jù)了 。
同時注意不要用模擬器測試性能,特別是IOS模擬器做性能測試,因為 Flutter 在 IOS模擬器中純 CPU ,而實際設(shè)備會是 GPU 硬件加速,同時只在 Release 下對比性能。
支持上 Flutter 和 React Native , 都存在第三方包質(zhì)量參差不齊的問題,而目前在這一塊 Flutter 是弱于 React Native 的 ,畢竟 React Native 發(fā)展已久,雖然版本號一直不到 1.0,但是在 JS 的加持下生態(tài)豐富,同時也是因為平臺特性的原因,諸如 WebView 、地圖等控件的支持上現(xiàn)在依舊不夠好,這個后面也會說道。
上手難易度上,Flutter 配置環(huán)境和運行的“成功率”比 React Native 高不少 ,這里面有 node_module 黑洞這個坑,也有 React Native 本身依賴平臺控件導(dǎo)致的,至少我曾經(jīng)試過接手一個 React Native 跑了一天都沒跑起來的經(jīng)歷,同時 Flutter 在運行和SDK版本升級的陣痛也會少很多。
穩(wěn)定性:Flutter 中大部分異常是不會引起應(yīng)用崩潰 ,更多會在 Debug 上體現(xiàn)為紅色錯誤堆棧,Release 上 UI 異常等等。
二、Flutter 實戰(zhàn) 1、Dart 中有意思的一些東西如果你是前端,我會推薦你先學(xué) React Native,如果你是原生開發(fā),我推薦你學(xué) Flutter 。
在 React Native 0.59.x 版本開始,React 已經(jīng)將許多內(nèi)置控件和庫移出主項目,希望模糊 React 和 React Native 的界線,統(tǒng)一開發(fā),這里的理念和 Flutter 很像。
Flutter 暫時不支持熱更新?。。。。。。。?/p>
var 的語法糖是在賦值時才自推導(dǎo)出類型的 ,而 dynamic 是動態(tài)聲明,在運行時檢測,它們的使用有時候容易出現(xiàn)錯誤。
如下圖所以說,
var 初始化時被指定為 dynamic 類型的。
然后賦值的時候初始化為 String 類型,這時候進(jìn)行 ++ 操作就會出現(xiàn)運行時報錯,
如下圖2如果在初始化指定類型的,那么編譯時就會告訴你錯誤了。
如下圖所示,Dart 支持很多有意思的操作符,如下圖:
執(zhí)行的時候首先是判斷 AA 如果為空,就返回 999 ;
之后如果 AA 為空,就為 AA 賦值 999;
之后對 AA 進(jìn)行整除 999 ,輸出結(jié)果 10 。
如下圖所示,Dart 中是支持操作符重載的,這樣可以比較直觀我們的代碼邏輯,并且簡化代碼時的調(diào)用。
如下圖所示,在 Dart 中方法時可以作為參數(shù)傳遞的,這樣的形式可以讓我們更靈活的組織代碼的邏輯。
在 Dart 中 async await / async* yield 等語法糖,代表 Dart 中的 Future 和 Stream 操作,它們對應(yīng) Dart 中的異步邏輯支持。
sync* / yield 對應(yīng) Stream 的同步操作。
在 Dart 中支持混入的模式,如下圖所示,混入時的基礎(chǔ)順序是從右到左依次執(zhí)行的,而且和 super 有關(guān),同時 Dart 還支持 mixin 關(guān)鍵字的定義。
Flutter 的啟動類用的就是 mixins 方式
Dart 中單線程模式中增加了 isolate 提供跨線程的真異步操作,而因為 Dart 中線程不會共享內(nèi)存,所以也不存在死鎖,從而也導(dǎo)致了 isolate 之間數(shù)據(jù)只能通過 port 的端口方式發(fā)送接口,類似于 Scoket 的方式,同時提供了 compute 的封裝接口方便調(diào)用。
Dart 為了讓類可以像函數(shù)一樣調(diào)用,默認(rèn)都可以實現(xiàn) call() 方法,同樣 typedef 定義的方法也是具備 call() 條件。
比如我定義了一個 CallObject
class CallObject {
List footerButton = [];
call(int i, double e) => "$i xxxx $e";
}
就可以通過以下執(zhí)行
CallObject callObject = CallObject();
print(callObject(11, 11.0));
print(callObject");
然后我定義了
typedef void ValueFunction(int i);
ValueFunction vt = (int i){
print("zzz $i");
};
就可以通過直接執(zhí)行和判空執(zhí)行處理
vt(666); vt");2、Flutter 中常見的
如下圖所示,ChangeNotifier 模式在 Flutter 中是十分常見的,比如 TextField 控件中,通過 TextEditingController 可以快速設(shè)置值的顯示,這是為什么呢?
如下圖所示,這是因為 TextEditingController 它是 ChangeNotifier 的子類,而 TextField 的內(nèi)部對其進(jìn)行了 addListener,同時我們改變值的時候調(diào)用了notifyListener,觸發(fā)內(nèi)部 setState。
在 Flutter 中所有的狀態(tài)共享都是通過它實現(xiàn)的,如自帶的 Theme ,Localizations ,或者狀態(tài)管理的 scoope_model 、 flutter_redux 等等,都是基于它實現(xiàn)的。
如下圖是 SliderTheme 的自定義實現(xiàn)邏輯,默認(rèn) Theme 中是包含了 SliderTheme,但是我們可以通過覆蓋一個新的 SliderTheme 嵌套去實現(xiàn)自定義,然后通過 SliderTheme theme = SliderTheme(context); 獲取,其中而 context 的實現(xiàn)就是 Element。
在 Element 的 inheritFromWidgetOfExactType 方法實現(xiàn)里,有一個 Map
_inheritedWidgets 一般情況下是空的,只有當(dāng)父控件是 InheritedWidget 或者本身是 InheritedWidgets 時才會有被初始化,而當(dāng)父控件是 InheritedWidget 時,這個 Map 會被一級一級往下傳遞與合并 。 所以當(dāng)我們通過 context 調(diào)用 inheritFromWidgetOfExactType 時,就可以往上查找到父控件的 Widget 。
StreamBuilder 一般用于通過 Stream 異步構(gòu)建頁面的,如下圖所示,通過點擊之后,綠色方框的文字會變成 addNewxxx,因為 Stream 進(jìn)行了 map 變化,同時一般實現(xiàn) bloc 模式的時候,經(jīng)常會用到它們。
類似的還有 FutureBuilder
一般 Widget 都是一幀的,而 State 實現(xiàn)了 Widget 的跨幀繪制,一般定義的時候,我們可以如下圖一樣實現(xiàn),而如下圖尖頭所示,這時候我們點擊 setState 改變的時候,是不會出現(xiàn)效果的,為什么呢?
其實 State 對象的創(chuàng)建和更新時機(jī)導(dǎo)致的:
1、createState 只在 StatefulElement 創(chuàng)建時才會被創(chuàng)建的。
2、StatefulElement 的 createElement 一般只在 inflateWidget 調(diào)用。
3、updateChild 執(zhí)行 inflateWidget 時, 如果 child 存在可以更新的話,不會執(zhí)行 inflateWidget。
3、四棵樹
Flutter 中主要有 Widget 、Element 、RenderObject 、Layer 四棵樹,它們的作用是:
Widget :就是我們平常寫的控件,Flutter 宇宙中萬物皆 Widget ,它們都是不可變一幀,同時也是被人吐槽很多的嵌套模式,當(dāng)然換個角度,事實上你把他當(dāng)作 Widget 配置文件來寫或者就好理解了。
Element :它是 BuildContext 的實現(xiàn)類,Widget 實現(xiàn)跨幀保存的 state 就是存放在這里,同時它也充當(dāng)了 Widget 和 RenderObject 之間的橋梁。
RenderObject :它才是真正干活(layout、paint)等,同時它才是真實的 “dom” 。
Layer :一整塊的重繪區(qū)域(isRepaintBoundary),決定重繪的影響區(qū)域。
skia 在繪制的時候,saveLayer 是比較消耗性能的,比如透明合成、clipRRect 等等都會可能需要 saveLayer 的調(diào)用, 而 saveLayer 會清空GPU繪制的緩存,導(dǎo)致性能上的損耗,所以開發(fā)過程中如果掉幀嚴(yán)重,可以針對這一塊進(jìn)行優(yōu)化。
4、手勢
Flutter 在手勢中引入了競技的概念, Down 事件在 Flutter 中尤為重要。
PointerDownEvent 是一切的起源,在 Down 事件中一般不會決出勝利者。
在 MOVE 和 UP 的時候才競爭得到響應(yīng)。
以點擊為例子:Down 時添加進(jìn)去參與競爭,UP 的時候才決定誰勝利,勝利條件是:
I、UP 的時候如果只有一個,那么就是它了。
II、UP 的時候如果有多個,那么強(qiáng)制隊列里第一個直接勝利。
這里包含了有趣的點就是,都在 UP 的時候才響應(yīng),那么 Down 事件怎么先傳遞出去了?
FLutter 在這里做了一個 didExceedDeadline 機(jī)制 ,事實上在上面的 addPointer 的時候,會啟動了一個定時器,默認(rèn) 100 ms,如果超過指定時間沒 UP ,那就先執(zhí)行這個 didExceedDeadline 響應(yīng) Down 事件。
那問題又來了,如果這時候隊列里兩個呢");
它們的 onTapDown 都會被觸發(fā),但是 onTap 只有一個獲得。
如果有兩個滑動 ScrollView 嵌套呢?
舉個簡單的例子,兩個 SingleChildScrollView 的嵌套時,在布局會經(jīng)歷:
performLayout -> applyContentDimensions -> applyNewDimensions -> context.setCanDrag(physics.shouldAcceptUserOffset(this));
只有 shouldAcceptUserOffset 為 ture 時,才會添加 VerticalDragGestureRecognizer 去處理手勢。
而判斷條件主要是 return math.max(0.0, child.size.height - size.height); ,也就是如果 child Scroll 的 height 小于父控件 Scroll 的時候,就會出現(xiàn) child 不添加 VerticalDragGestureRecognizer 的情況,這時候根本就沒有競爭了。
5、動畫Flutter 中的動畫是怎么執(zhí)行的呢?
我們先看一段代碼,然后這段代碼執(zhí)行的效果如下圖2所示。
那既然 Widget 都是一幀,那么動畫肯定有 setState 的地方了。
首先這里有個地方可以看下,這時候 200 這個數(shù)值執(zhí)行后是會報錯的,因為白框內(nèi)可見 Tween 中的 T 在這時候會出現(xiàn)既有 int 又有 double ,無法判斷的問題,所以真實應(yīng)該是 200.0 。
同時你發(fā)現(xiàn)沒有,代碼中 parent 的 Container 在 只有100的情況下,它的 child 可以正常的畫 200,這是因為我們的 paint 沒有跟著 RenerObjcet 的大小走, 所以一般情況下,整個屏幕都是我們的畫版,Canvas 繪制與父控件大小可以沒關(guān)系。
同時動畫是通過 vsync 同步信號去觸發(fā)的,就是我們 mixin 的 SingleTickerProviderStateMixin,它內(nèi)部的 Ticker 會通過 SchedulerBinding 的 scheduleFrameCallback 同步信號觸發(fā)重繪 。
6、狀態(tài)管理動畫后的控件的點擊區(qū)域,和你的動畫數(shù)據(jù)改變的是 paint 還是 layout 有關(guān) 。
scope_model 、flutter_redux、fish_redux 、甚至還有有 dva_flutter 等等,可以看出狀態(tài)管理在 flutter 中和前端十分相近。
這里簡單說說 scope_model ,它只有一個文件,但是很巧妙,它利用的就是 AnimationBuilder 的特性。
如下圖是使用代碼,在前面我們知道,狀態(tài)管理使用的是 InheritedWidget 實現(xiàn)共享的,而當(dāng)我們對 Model 進(jìn)行數(shù)據(jù)改變時,通過調(diào)用 notifyListeners 通知頁面更新了。
這里的原理是什么呢?
其實 scope_model 內(nèi)部利用了 AnimationBuilder ,而 Model 實現(xiàn)了 Listenable 接口。
當(dāng) Model 設(shè)置給了 AnimationBuilder 時, AnimationBuilder 會執(zhí)行 addListener 添加監(jiān)聽,而監(jiān)聽方法里會執(zhí)行 setState。
所以我們改變 set 方法時調(diào)用 notifyListeners 就觸發(fā)了 setState 去更新了,這樣體現(xiàn)出了前面說的 FLutter 常見的開發(fā)模式。
三、混合開發(fā)
以 Android 的角度來說,從方便調(diào)試和解耦集成上,我們一般會以 aar 的形式集成混合開發(fā),這里就會涉及到 gradle 打包的一個概念。
1、如下代碼所示,在項目中進(jìn)行 gradle 腳本修改,組件化開發(fā)模式,用 apk 開發(fā),用 aar 提供集成,正常修改 gradle 代碼即可快速打包。
那如果 Flutter 的項目插件帶有本地代碼呢?
如果開發(fā)過 React Native 的應(yīng)該知道,在原生插件安裝時會需要執(zhí)行 react-native link ,而這時候會修改項目的gradle 和java代碼。
2、 和 React Native 很有侵入性相比, Flutter 就很巧妙了。
如下圖所示,安裝過的插件會出現(xiàn)在 .flutter_plugins 文件中,然后通過讀取文件,動態(tài)在 setting.gradle 和 flutter.gradle 中引入和依賴:
所以這時候我們可以參考打包,修改我們的gradle腳本,利用 fat-aar 插件將本地 projcet 也打包的 aar 里。
3、混合開發(fā)的最大痛點是什么?
肯定是堆棧管理!!! 所以項目開發(fā)了 flutter_boost 來解決這個問題。
堆棧統(tǒng)一到了原生層。
通過一個唯一 engine ,切換 Surface 渲染顯示。
每個 Activity 就是一個 Surface ,不渲染的頁面通過截圖緩存畫面。
flutter_boost 截止到我測試的時間 2019-05-16, 只支持 1.2之前的版本
四、PlatformView
混合開發(fā)除了集成到原生工程,也有將原生控件集成到 Flutter 渲染樹里里的需求。
首先我們看看沒有 PlatformView 之前是如何實現(xiàn) WebView 的,這樣會有什么問題?
如下圖所示,事實上 dart 中僅僅是用了一個 SingleChildRenderObjectWidget 用于占位,將大小傳遞給原生代碼,然后在原生代碼里顯示出來而已。
這樣的時候必定會代碼畫面堆棧問題,因為這個顯示脫離了 Flutter 的渲染樹,通過出現(xiàn)動畫肯定會不一致。
4.1 AndroidViewAndroidView -> TextureLayer,利用Android 上的副屏顯示與虛擬內(nèi)存顯示原理。
共享內(nèi)存,實時截圖渲染技術(shù)。
存在問題,耗費內(nèi)存,頁面復(fù)雜時慢。
三、Flutter Web這部分因為之前以前聊過,就不贅述了
RN因為是原生控件,所以在react 和react native 整合這件事上存在難度。
flutter 作為一個UI 框架,與平臺無關(guān),在web上利用的是dart2js的能力。 比如Image
因為 Flutter 是一套 UI 框架,整體 UI 幾乎和平臺無關(guān),這和 React Native 有很大的區(qū)別。(我在開發(fā)過程中幾乎無知覺)
在 flutter_web 中 UI 層面與渲染邏輯和 Flutter 幾乎沒有什么區(qū)別,底層的一些區(qū)別如: flutter_web 中的 Canvas 是 EngineCanvas 抽象,內(nèi)部會借助 dart2js 的能力去生成標(biāo)簽。
React Native 平臺關(guān)聯(lián)性太強(qiáng),而 Flutter 在多平臺上優(yōu)勢明顯。我們期待官方幫我們解決大部分的適配問題。
Flutter 的平臺無關(guān)能力能帶來什么?
1、某些功能頁面,可以一套代碼實現(xiàn),利用插件安裝引入,在web、移動app、甚至 pc 上,都可以編譯出對應(yīng)平臺的高性能代碼,而不會像 Weex 等一樣存在各種兼容問題。
2、在應(yīng)用上可以快速實現(xiàn)“降級策略”,比如某種情況下應(yīng)用產(chǎn)生奔潰了,可以替換為同等 UI 的 h5 顯示,而這些代碼只需要維護(hù)一份。
資源推薦
Github : github.com/CarGuo
RTC社區(qū) : rtcdeveloper.com
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/7880.html
摘要:中的的線程是以事件循環(huán)和消息隊列的形式存在,包含兩個任務(wù)隊列,一個是內(nèi)部隊列,一個是外部隊列,而的優(yōu)先級又高于。同時還有處理按住時的事件額外處理,同時手勢處理一般在的子類進(jìn)行。谷歌大會之后,有不少人咨詢了我 Flutter 相關(guān)的問題,其中有不少是和面試相關(guān)的,如今一些招聘上也開始羅列 Flutter 相關(guān)要求,最后想了想還是寫一期總結(jié)吧,也算是 Flutter 的階段復(fù)習(xí)。 ??系統(tǒng)完...
摘要:但小程序的云開發(fā)也有一些局限性,我會在下一篇小程序云開發(fā)實戰(zhàn)系列云數(shù)據(jù)庫里分享我使用云開發(fā)數(shù)據(jù)庫的一些體會,敬請期待。 因為工作原因,使用過亞馬遜AWS的serverless構(gòu)架,當(dāng)時就覺得這是一個非常適合小程序的基礎(chǔ)設(shè)施。今年開始自己的微信小程序開發(fā),決定用serverless架構(gòu),于是便使用了騰訊云做為小程序的后端,總結(jié)下來有以下幾個痛點: 當(dāng)暴露API給小程序時,需要使用已備案的...
摘要:在這里可以處理一些傳過來的參數(shù),然后我們將參數(shù)放入類中實例化。因為雖然是一個,但是實例化的時候是兩個不同的,所以第一個頁面的數(shù)據(jù)變化了也不會影響到這里。說明 目前增加了路由跳轉(zhuǎn),可以帶參數(shù)跳轉(zhuǎn)頁面。下拉可以自定義刷新樣式,IOS點擊Status Bar回到頂部,目前已經(jīng)測試過。狀態(tài)管理器使用Mobx,我自己覺得對于Redux使用起來會復(fù)雜一點,下面是提供的預(yù)覽GIF圖,卡頓現(xiàn)象是因為屏幕錄制...
摘要:一什么是年月日谷歌官方宣布的首個發(fā)布預(yù)覽版正式發(fā)布,這標(biāo)志著谷歌進(jìn)入了正式版發(fā)布前的最后階段。是一個由谷歌開發(fā)的開源移動應(yīng)用軟件開發(fā)工具包,用于為和開發(fā)應(yīng)用,同時也將是目的是為了替換系統(tǒng)下開發(fā)應(yīng)用的主要工具。 一、Flutter 1.什么是Flutter 2018年6月21日谷歌官方宣布Flutter的首個發(fā)布預(yù)覽版(Release Preview 1)正式發(fā)布,這標(biāo)志著谷歌進(jìn)入了Fl...
閱讀 2028·2021-08-11 11:13
閱讀 1059·2021-07-25 21:37
閱讀 2601·2019-08-29 18:42
閱讀 2536·2019-08-26 12:18
閱讀 946·2019-08-26 11:29
閱讀 1711·2019-08-23 17:17
閱讀 2687·2019-08-23 15:55
閱讀 2634·2019-08-23 14:34