<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          給 Android 和 iOS 開發(fā)人員不一樣的 Flutter 基礎(chǔ)講解

          共 6389字,需瀏覽 13分鐘

           ·

          2021-06-06 16:59

          ????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????
          作者丨戀貓de小郭
          來源丨GSYTech(ID:gsy-tech-zone)

          一、單頁面應(yīng)用

          了解 Flutter 之前,首先介紹一個(gè)簡(jiǎn)單基礎(chǔ)知識(shí)點(diǎn),「那就是大部分的移動(dòng)端跨平臺(tái)框架都是“單頁面”應(yīng)用」。

          什么是“單頁面”應(yīng)用?也就是對(duì)于原生 Android 和 iOS 而言,「整個(gè)跨平臺(tái) UI 默認(rèn)都是運(yùn)行在一個(gè) Activity / ViewController 上面」,默認(rèn)情況下只會(huì)有一個(gè)  Activity / ViewController, Flutter、 ReactNative 、Weex 、Ionic 默認(rèn)情況下都是如此,「所以一般情況下框架的路由和原生的路由是沒有直接關(guān)系」。

          舉個(gè)例子,如下圖所示,

          • 在當(dāng)前 Flutter 端路由堆棧里有 FlutterA 和 FlutterB 兩個(gè)頁面 Flutter 頁面;
          • 這時(shí)候打開新的 Activity / ViewController,啟動(dòng)了「原生頁面X」,可以看到「原生頁面X」作為新的原生頁面加入到原生層路由后,把 FlutterActivity / FlutterViewController給擋住,也就是把  FlutterA 和 FlutterB 都擋??;
          • 這時(shí)候在 Flutter 層再打開新的 FlutterC 頁面,可以看到依然會(huì)被原生頁面X擋住;

          所以通過這部分內(nèi)容可以看出來,「跨平臺(tái)應(yīng)用默認(rèn)情況下作為單頁面應(yīng)用,他們的路由堆棧是和原生層存在不兼容的隔離」。

          ?

          當(dāng)然這里面重復(fù)用了一個(gè)詞:「“默認(rèn)”」,也就是其實(shí)可以支持自定義混合堆棧的,比如官方的 FlutterEngineGroup ,第三方框架 flutter_boost 、 mix_stack 、flutter_thrio 等等。

          ?

          二、渲染邏輯

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

          在渲染層面 Flutter 和其他跨平臺(tái)框架存在較大差異,如下圖所示是現(xiàn)階段常見的渲染模式對(duì)比:

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

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

          ?

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

          ReactNative/Weex 這類跨平臺(tái)和原生平臺(tái)存在較大關(guān)聯(lián):

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

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

          ?

          Flutter 與之不同的地方就是渲染直接利用 skia 和 GPU 交互,在 Android 和 iOS 平臺(tái)上實(shí)現(xiàn)了平臺(tái)無關(guān)的控件,簡(jiǎn)單說就是 Flutter 里的 Widget 大部分都是和 Android 和 iOS 沒有關(guān)系。

          「本質(zhì)上原生平臺(tái)是提供一個(gè)類似 Surface 的畫板,之后剩下的只需要由 Flutter 來渲染出對(duì)應(yīng)的控件」

          ?

          一般是使用 FlutterView 作為渲染承載,它在 Android 上內(nèi)部使用可以是 SurfaceView 、 TextureView 或者 FlutterImageView ;在 iOS 上是 UIView 通過 Layer 實(shí)現(xiàn)的渲染。

          ?

          「所以 Flutter 的控件在不同平臺(tái)可以得到一致效果,但是和原生控件進(jìn)行混合也會(huì)有較高的成本和難度」,在接入原生控件的能力上,F(xiàn)lutter 提供了 PlatformView 的機(jī)制來實(shí)現(xiàn)接入, PlatformView 本身的實(shí)現(xiàn)會(huì)比較容易引發(fā)內(nèi)存和鍵盤等問題,所以也帶來了較高的接入成本。

          三、項(xiàng)目結(jié)構(gòu)

          如上圖所示,默認(rèn)情況下 Flutter 工程結(jié)構(gòu)是這樣的:

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

          如下圖是使用是關(guān)于 pubspec.yaml 文件的結(jié)構(gòu)介紹

          ?

          需要注意,當(dāng)這個(gè)文件發(fā)生改變時(shí),需要重新執(zhí)行 flutter pub get,并且 stop應(yīng)用之后重新運(yùn)行項(xiàng)目,而不是使用 hotload 。

          ?

          如下所示是 Flutter 的插件工程,F(xiàn)lutter 中分為 Package 和 Plugin ,如果是

          • Package 項(xiàng)目屬于 Flutter 包工程,不會(huì)包含原生代碼;
          • Plugin 項(xiàng)目屬于 Flutter 插件工程,包含了 Android 和 iOS 代碼;

          四、打包調(diào)試

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

          下載依賴成功后,可以直接通過 flutter run 或者 IDE 工具點(diǎn)擊運(yùn)行來啟動(dòng) Flutter 項(xiàng)目,這個(gè)過程會(huì)需要原生工程的一些網(wǎng)絡(luò)同步工作,比如:

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

          如果需要在項(xiàng)目同步過程中查看進(jìn)度:

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

          同步的插件中,如果是 Plugin 帶有原生平臺(tái)的代碼邏輯,那么可以在項(xiàng)目根目錄下看到一個(gè)叫做 .flutter_plugins 和 .flutter-plugins-dependencies 的文件,它們是 git ignore 的文件,Android 和 iOS 中會(huì)根據(jù)這個(gè)文件對(duì)本地路徑的插件進(jìn)行引用,后面 Flutter 運(yùn)行時(shí)會(huì)根據(jù)這個(gè)路徑動(dòng)態(tài)添加依賴。

          默認(rèn)情況下 Flutter 在 「debug 下是 JIT 的運(yùn)行模式」所以運(yùn)行效率會(huì)比較低,速度相對(duì)較慢,但是可以 hotload。

          在 「release 下是 AOT 模式」,運(yùn)行速度會(huì)快很多,同時(shí) Flutter 在「模擬器上一般默認(rèn)會(huì)使用 CPU 運(yùn)行,在真機(jī)上會(huì)使用 GPU 運(yùn)行」,所以性能表現(xiàn)也不同。

          ?

          另外 iOS 14 真機(jī)上 debug 運(yùn)行,斷后鏈接后再次啟動(dòng)是無法運(yùn)行的。

          ?

          如果項(xiàng)目存在緩存問題,可以「直接執(zhí)行 flutter clean 來清理緩存」。

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

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

          而 Flutter 打包后的文件是二進(jìn)制文件,推送二進(jìn)制文件明顯是不符合平臺(tái)要求的。

          ?

          release 打包后的 Android 會(huì)生成 app.so 和 flutter.so 兩個(gè)動(dòng)態(tài)庫;iOS 會(huì)生成 App.framework 和 Flutter.framework 兩個(gè)文件。

          ?

          五、Flutter 簡(jiǎn)單介紹

          最后簡(jiǎn)單介紹下 Flutter Dart 部分相關(guān)的內(nèi)容,對(duì)于原生開發(fā)來說,F(xiàn)lutter 主要優(yōu)先了解這三點(diǎn):「響應(yīng)式、Widget 和狀態(tài)管理」 。

          響應(yīng)式

          響應(yīng)式編程也叫做聲明式編程,這是現(xiàn)在前端開發(fā)的主流,當(dāng)然對(duì)于客戶端開發(fā)的一種趨勢(shì),比如 Jetpack Compose 、SwiftUI 。

          ?

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

          ?

          「響應(yīng)式簡(jiǎn)單來說其實(shí)就是你不需要手動(dòng)更新界面,只需要把界面通過代碼“聲明”好,然后把數(shù)據(jù)和界面的關(guān)系接好,數(shù)據(jù)更新了界面自然就更新了?!?/strong>

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

          ?

          響應(yīng)式開發(fā)比數(shù)據(jù)綁定或者 MVVM 不同的地方是,它每次都是重新構(gòu)建和調(diào)整整個(gè)渲染樹,而不是簡(jiǎn)單的對(duì) UI 進(jìn)行 visibility 操作。

          ?

          Widget

          Widget 是 Flutter 里的基礎(chǔ)概念,也是我們寫代碼最直接接觸的對(duì)象,「Flutter 內(nèi)一切皆 Widget ,Widget 是不可變的(immutable),每個(gè) Widget 狀態(tài)都代表了一幀?!?/strong>

          所以 Widget 作為一個(gè) immutable 對(duì)象,它不可能是真正工作的 UI 對(duì)象,「在 Flutter 里真正的 View 級(jí)別對(duì)象是 Element 和 RenderObject , 其中 Element 的抽象對(duì)象就是我們經(jīng)常用到的 BuildContext。

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

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

          狀態(tài)管理

          Flutter 作為響應(yīng)式開發(fā)框架,本質(zhì)上它其實(shí)不再追求什么 MVC 、MVP、MVVVM 的設(shè)計(jì)模式,它更多是對(duì)界面狀態(tài)的管理。

          ?

          就是要拋棄以前在原生平臺(tái)上,需要拿到 View 的對(duì)象,然后做對(duì)其進(jìn)行 UI 設(shè)置這種思路。

          ?

          Flutter 上更多需要管理數(shù)據(jù)的流向,比如:

          • 數(shù)據(jù)是從哪里發(fā)出,然后再到哪里消費(fèi);
          • 數(shù)據(jù)是單向還是雙向;
          • 數(shù)據(jù)需要進(jìn)過哪些中間轉(zhuǎn)化;
          • 數(shù)據(jù)是從哪一層開始往下傳遞;
          • 數(shù)據(jù)綁定了哪些地方;
          • 如何實(shí)現(xiàn)多個(gè)地方的局部刷新;

          因?yàn)閷?duì)于界面來說,它只需要根據(jù)數(shù)據(jù)進(jìn)行變化即可,我們不需要獲取它去單獨(dú)設(shè)置,所以 Flutter 中有各種數(shù)據(jù)管理和共享的框架,比較流行的有 provider 、 getx 、 flutter_redexflutter_mobx 等等。

          有趣的問題

          最后說一個(gè)比較有意思的問題,之前有人說 「Flutter 里是傳遞值還是引用」?這個(gè)問題看過網(wǎng)上有不少文章解釋得很奇怪,存在一些誤導(dǎo)性的解釋,其實(shí)這個(gè)問題很簡(jiǎn)單:

          「Flutter 里一切皆是對(duì)象, 就連 int 、 double 、bool 也是對(duì)象,你覺得對(duì)象傳遞的是什么?」

          但是對(duì)于對(duì)象的操作是有區(qū)別的,比如對(duì)于 int 、  double 等 class 的 + 、- 、* 、 \ 等操作,其實(shí)是執(zhí)行了這個(gè) class 的 operator 操作符的操作, 然后返回了一個(gè) num 對(duì)象。

          而對(duì)于這個(gè)操作,只需要要去 dart vm 看看 Double 對(duì)象在進(jìn)行加減乘除時(shí)做了什么,如下圖所示,看完相信就知道方法里傳遞 int 、double 對(duì)象后進(jìn)行操作會(huì)是什么樣的結(jié)果。

          -End-

          最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

          點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點(diǎn)這里好文分享給更多人↓↓

          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    欧美乱伦第一页 | 影音先锋一区二区三区视频特色 | 射操操操逼逼逼 | 欧美日韩亚州在线观看 | 东京热2网站|