給 Android 和 iOS 開發人員不一樣的 Flutter 基礎講解

因為最近公司來了新人,之前很少接觸過跨平臺應用開發,所以為了給他們介紹關于 Flutter 的一些基礎,這里特意整理了一份通用性質的常識性講解,結尾順便介紹一個有趣的案例

一、單頁面應用

了解 Flutter 之前,首先介紹一個簡單基礎知識點,那就是大部分的移動端跨平臺框架都是“單頁面”應用

什么是“單頁面”應用?也就是對于原生 Android 和 iOS 而言,整個跨平臺 UI 默認都是運行在一個 Activity / ViewController 上面,默認情況下只會有一個 Activity / ViewController, Flutter、 ReactNative 、Weex 、Ionic 默認情況下都是如此,所以一般情況下框架的路由和原生的路由是沒有直接關系

舉個例子,如下圖所示,

  • 在當前 Flutter 端路由堆棧里有 FlutterAFlutterB 兩個頁面 Flutter 頁面;
  • 這時候打開新的 Activity / ViewController,啟動了原生頁面X,可以看到原生頁面X 作為新的原生頁面加入到原生層路由后,把 FlutterActivity / FlutterViewController 給擋住,也就是把 FlutterAFlutterB 都擋住;
  • 這時候在 Flutter 層再打開新的 FlutterC 頁面,可以看到依然會被原生頁面X擋住;
image

所以通過這部分內容可以看出來,跨平臺應用默認情況下作為單頁面應用,他們的路由堆棧是和原生層存在不兼容的隔離

當然這里面重復用了一個詞:“默認”,也就是其實可以支持自定義混合堆棧的,比如官方的 FlutterEngineGroup ,第三方框架 flutter_boostmix_stackflutter_thrio 等等。

二、渲染邏輯

介紹完“單頁面”部分的不同,接下來講講 Flutter 在渲染層面的不同。

在渲染層面 Flutter 和其他跨平臺框架存在較大差異,如下圖所示是現階段常見的渲染模式對比:

image
  • 對于原生 Android 而言,是原生代碼經過 skia 最后到 GPU 完成渲染繪制,Android 原生系統本身自帶了 skia;
  • 對于 Flutter 而言,Dart 代碼里的控件經過 skia 最后到 GPU 完成渲染繪制,這里在 Andriod 上使用的系統的 skia ,而在 iOS 上使用的是打包到項目里的 skia ;
  • 對于 ReactNative/Weex 等類似的項目,它們是運行在各自的 JS 引擎里面,最后通過映射為原生的控件,利用原生的渲染能力進行渲染
  • 對于 ionic 等這類 Hybird 的跨平臺框架,使用的主要就是 WebView 的渲染能力

skia 在 Android 上根據不同情況就可能會是 OpenGL 或者 Vulkan ,在 iOS 上如果有支持 Metal 也會使用 Metal 加速渲染。

通過前面的介紹,可以看出了:

ReactNative/Weex 這類跨平臺和原生平臺存在較大關聯:

  • 好處就是:如果需要使用原生平臺的控件能力,接入成本會比較低;
  • 壞處自然就是: 渲染嚴重依賴平臺控件的能力,耦合較多,不同系統之間原生控件的差異,同個系統的不同版本在控件上的屬性和效果差異,組合起來在后期開發過程中就是很大的維護成本。、

例如:在 iOS 上調試好的樣式,在 Android 上出現了異常;在 Android 上生效的樣式,在 iOS 上沒有支持;在 iOS 平臺的控件效果,在 Android 上出現了不一樣的展示,比如下拉刷新,Appbar等;

Flutter 與之不同的地方就是渲染直接利用 skia 和 GPU 交互,在 Android 和 iOS 平臺上實現了平臺無關的控件,簡單說就是 Flutter 里的 Widget 大部分都是和 Android 和 iOS 沒有關系。

本質上原生平臺是提供一個類似 Surface 的畫板,之后剩下的只需要由 Flutter 來渲染出對應的控件

一般是使用 FlutterView 作為渲染承載,它在 Android 上內部使用可以是 SurfaceViewTextureView 或者 FlutterImageView ;在 iOS 上是 UIView 通過 Layer 實現的渲染。

所以 Flutter 的控件在不同平臺可以得到一致效果,但是和原生控件進行混合也會有較高的成本和難度,在接入原生控件的能力上,Flutter 提供了 PlatformView 的機制來實現接入, PlatformView 本身的實現會比較容易引發內存和鍵盤等問題,所以也帶來了較高的接入成本。

三、項目結構

image

如上圖所示,默認情況下 Flutter 工程結構是這樣的:

  • android 原生的工程目錄,可以配置原生的 appNamelogo ,啟動圖, AndroidManifest 等等;
  • ios 工程目錄,配置啟動圖,logo,應用名稱,plist 文件等等;
  • build 目錄,這個目錄是編譯后出現,一般是 git 的 ignore 目錄,打包過程和輸入結果都在這個目錄下,Android 原生的打包過程輸出也被重定向輸出到這里;
  • lib 目錄,用來寫 dart 代碼的,入口文件一般是 main.dart
  • pubspec.yaml 文件,Flutter 工程里最重要的文件之一,不管是靜態資源引用(圖片,字體)、第三方庫依賴還是 Dart 版本聲明都寫在這里。

如下圖是使用是關于 pubspec.yaml 文件的結構介紹

image

需要注意,當這個文件發生改變時,需要重新執行 flutter pub get,并且 stop 應用之后重新運行項目,而不是使用 hotload

如下所示是 Flutter 的插件工程,Flutter 中分為 PackagePlugin ,如果是

  • Package 項目屬于 Flutter 包工程,不會包含原生代碼;
  • Plugin 項目屬于 Flutter 插件工程,包含了 Android 和 iOS 代碼;
image

四、打包調試

Flutter 運行之前都需要先執行 flutter pub get 來先同步下載第三方代碼,下載的第三方代碼一般存在于(Mac) /Users/你的用戶名/.pub-cache 目錄下 。

下載依賴成功后,可以直接通過 flutter run 或者 IDE 工具點擊運行來啟動 Flutter 項目,這個過程會需要原生工程的一些網絡同步工作,比如:

  • Android 上的 Gradle 和 aar 依賴包同步;
  • iOS 上的需要 pod install 同步一些依賴包;

如果需要在項目同步過程中查看進度:

  • Android 可以到 android/ 目錄下執行 ./gradlew assembleDebug 查看同步進度;
  • iOS 可以到 ios/ 目錄下執行 pod install,查看下載進度;

同步的插件中,如果是 Plugin 帶有原生平臺的代碼邏輯,那么可以在項目根目錄下看到一個叫做 .flutter_plugins.flutter-plugins-dependencies 的文件,它們是 git ignore 的文件,Android 和 iOS 中會根據這個文件對本地路徑的插件進行引用,后面 Flutter 運行時會根據這個路徑動態添加依賴。

image

默認情況下 Flutter 在 debug 下是 JIT 的運行模式所以運行效率會比較低,速度相對較慢,但是可以 hotload。

release 下是 AOT 模式,運行速度會快很多,同時 Flutter 在模擬器上一般默認會使用 CPU 運行,在真機上會使用 GPU 運行,所以性能表現也不同。

另外 iOS 14 真機上 debug 運行,斷后鏈接后再次啟動是無法運行的。

如果項目存在緩存問題,可以直接執行 flutter clean 來清理緩存

最后說下 Flutter 的為什么不支持熱更新?

前面講過 ReactNative 和 Weex 是通過將 JS 代碼里的控件轉化為原生控件進行渲染,所以本質上 JS 代碼部分都只是文本而已,利用 code-push 推送文本內容本質上并不會違法平臺要求。

而 Flutter 打包后的文件是二進制文件,推送二進制文件明顯是不符合平臺要求的。

release 打包后的 Android 會生成 app.soflutter.so 兩個動態庫;iOS 會生成 App.frameworkFlutter.framework 兩個文件。

五、Flutter 簡單介紹

最后簡單介紹下 Flutter Dart 部分相關的內容,對于原生開發來說,Flutter 主要優先了解這三點:響應式、Widget 和狀態管理

響應式

響應式編程也叫做聲明式編程,這是現在前端開發的主流,當然對于客戶端開發的一種趨勢,比如 Jetpack ComposeSwiftUI

Jetpack Compose 和 Flutter 的在某些表層上看真的很相似。

響應式簡單來說其實就是你不需要手動更新界面,只需要把界面通過代碼“聲明”好,然后把數據和界面的關系接好,數據更新了界面自然就更新了。

從代碼層面看,對于原生開發而言,沒有 xml 的布局,沒有 storyboard,布局完全由代碼完成,所見即所得,同時也不會需要操作界面“對象”去進行賦值和更新,你所需要做的就是配置數據和界面的關系

響應式開發比數據綁定或者 MVVM 不同的地方是,它每次都是重新構建和調整整個渲染樹,而不是簡單的對 UI 進行 visibility 操作。

Widget

Widget 是 Flutter 里的基礎概念,也是我們寫代碼最直接接觸的對象,Flutter 內一切皆 Widget ,Widget 是不可變的(immutable),每個 Widget 狀態都代表了一幀。

所以 Widget 作為一個 immutable 對象,它不可能是真正工作的 UI 對象,在 Flutter 里真正的 View 級別對象是 ElementRenderObject , 其中 Element 的抽象對象就是我們經常用到的 BuildContext

舉個例子,如下代碼所示,其中 testUseAll 這個 Text 在同一個頁面下在三處地方被使用,并且代碼可以正常運行渲染,如果是一個真正的 View ,是不能在一個頁面下這樣被多個地方加載使用的。

image

所以 Flutter 中 Widget 更多只是配置文件的地位,用于描述界面的配置代碼,具體它們的實現邏輯、關系還有分類,可以看我寫的書 《Flutter開發實戰詳解》中 的第三章和第四章部分。

狀態管理

Flutter 作為響應式開發框架,本質上它其實不再追求什么 MVC 、MVP、MVVVM 的設計模式,它更多是對界面狀態的管理。

就是要拋棄以前在原生平臺上,需要拿到 View 的對象,然后做對其進行 UI 設置這種思路。

Flutter 上更多需要管理數據的流向,比如:

  • 數據是從哪里發出,然后再到哪里消費;
  • 數據是單向還是雙向;
  • 數據需要進過哪些中間轉化;
  • 數據是從哪一層開始往下傳遞;
  • 數據綁定了哪些地方;
  • 如何實現多個地方的局部刷新;

因為對于界面來說,它只需要根據數據進行變化即可,我們不需要獲取它去單獨設置,所以 Flutter 中有各種數據管理和共享的框架,比較流行的有 providergetxflutter_redexflutter_mobx 等等。

有趣的問題

最后說一個比較有意思的問題,之前有人說 Flutter 里是傳遞值還是引用?這個問題看過網上有不少文章解釋得很奇怪,存在一些誤導性的解釋,其實這個問題很簡單:

Flutter 里一切皆是對象, 就連 intdoublebool 也是對象,你覺得對象傳遞的是什么?

但是對于對象的操作是有區別的,比如對于 intdoubleclass+-*\ 等操作,其實是執行了這個 classoperator 操作符的操作, 然后返回了一個 num 對象。

image

而對于這個操作,只需要要去 dart vm 看看 Double 對象在進行加減乘除時做了什么,如下圖所示,看完相信就知道方法里傳遞 intdouble 對象后進行操作會是什么樣的結果。

image
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容