11 月 23 日,字節跳動技術沙龍 | Flutter 技術專場 在北京後山藝術空間圓滿結束。咱們邀請到字節跳動移動平臺部 Flutter 架構師袁輝輝,Google Flutter 團隊工程師 Justin McCandless,字節跳動移動平臺部 Flutter 資深工程師李夢雲,阿里巴巴高級技術專家王樹彬和你們進行分享交流。html
如下是字節跳動移動平臺部 Flutter 架構師袁輝輝的分享主題沉澱,《跨平臺技術趨勢及字節跳動 Flutter 架構實踐》。前端
演講內容大綱:算法
移動跨平臺技術趨勢 解讀 Flutter 引擎原理 字節跳動在 Flutter 架構實踐
你們下午好!今天跟你們分享的主題是《跨平臺技術趨勢及字節跳動 Flutter 架構實踐》。小程序
我是今年加入字節跳動,目前在移動平臺部負責 Flutter 架構優化工做,在此以前我在小米作了 3 年安卓手機系統的底層優化與研發工做。平時工做之餘喜歡寫寫博客,從 16 年開始,堅持寫了 4 年,產生 200 餘篇原創技術篇章,與此期間也受邀出席業界的一些安卓相關的技術大會。緩存
在正式分享以前問你們一個問題:面對層出不窮的新技術,你是選擇繼續深耕原有技術,仍是會嘗試新技術? 你們不要思考,你直觀印象會選擇什麼?網絡
但其實面對這個問題的時候,不少人想的是:我是否是要嘗試新技術了?就像上面這個例子,不少人會想到,我若是不斷換新技術,是否是最終一無所得?我若是堅持一個方向,最終我能得到我想要的東西、可以有所成果,我就不要去嘗試新技術了。但其實這裏面有個誤區:架構
第一,若是你在一個領域作得足夠長時間,3 年、5 年、10 年,那麼你有可能會有所收穫,這時候你就會進入技術疲憊期,有可能就不肯意再去挑戰新的東西了,有可能以爲成長空間受到限制,這個時候若是你願意跳出技術溫馨區,去看一看周邊新的技術,會拓寬你的知識面。以我我的爲例,我在此前作過安卓手機驅動層的開發,我又作過上面安卓 Framework 層,來到字節跳動又開始作 Flutter,在我不斷選擇新方向時,這未嘗不是一種深耕呢?把你的技術棧打通。框架
第二,若是你選擇學習更多新技術時,能夠看到下面有不少鑽石,你要選擇找到一顆最大的鑽石,怎麼找?要看趨勢,一個好的趨勢不必定幫你找到最大鑽石,可是必定能夠幫你更高几率找到更大的鑽石。less
第三,如何找到一個新的想法,是否是每次工做時都像左邊那我的從頭開始?其實這也是一個誤區。有經驗的人都知道,你在一個領域深耕時,再學習一個新的領域絕非從零開始,到必定程度你會橫向發展、橫向打通,而不是回到零點,你的學習成本時間不會那麼長,由於技術是相通的。運維
因此咱們在追求技術過程當中,如何找到更大的鑽石?就是看技術趨勢。如何收穫更多鑽石?不要拘泥於一個方向,固然,也不建議你們頻繁的切換,但能夠在多個領域收穫。如何更高效的找到鑽石?咱們的技術是相通的,當你在學習新技術時,要你把原有的技術聯通、打通,融通你的的任督六脈。
爲了解決這幾個問題,咱們以移動跨平臺爲例。首先,咱們先看看移動端技術發展趨勢;再則看看 Flutter 引擎到底有哪些核心技術,如何跟原有技術相通的;最後介紹一下字節跳動在 Flutter 新技術領域有哪些技術進展與落地實踐,看看在新領域是如何作到快速深耕。
移動互聯網發展 10 多年來,由以前傳統 PC 端時代到移動時代,在移動時代競爭激烈,各大移動互聯網公司都在爭相搶奪市場,如何提升研發效率,產品快速迭代、快速試錯成爲很是重要的因素。
與此同時,還要看在關注研發效率的同時可否作出一個性能比較高的應用,每每咱們的研發效率和性能是天平兩端,性能好了開發效率可能就差了,開發效率好了性能可能就差了,這就須要思考如何把開發效率和性能這二者作到更好的平衡。
再者,面對這麼多跨平臺技術,可否用一種語言開發出應用在多端體驗是一致的?UI 小姐姐設計這版小龍女,研發小哥哥開發出另外一款小龍女,到安卓平臺變成各類各樣的小龍女,發現 UI 長得都不同。咱們可否實現高效的多端一致性體驗?
再者,可否突破渠道去快速更新個人應用?這個不光是跨平臺技術,Native 技術自己也是一樣關注這個問題。
以上,是跨平臺技術方案優劣最爲關注的幾個因素。
咱們在 2011 年時,以 WebView 技術作跨端,這個跨端有比較大的侷限性,性能不好、功能受限,不能作複雜場景。到了 2015 年 Facebook 出了 React Native,它是以 JS 語言,經過中間層轉換,最終渲染調到 Native 層,性能相對更好一些。接下來阿里推出了 Weex,也是經過 JS 語言調到原生渲染。2018 年 Google 出了 Flutter,它有自本身的引擎、自渲染機制,它的性能比較好。這就是大概的演進。
跨端技術分爲幾類:
原生渲染:經過各類技術調中間層轉換,最終渲染,安卓仍是調安卓的,iOS 的仍是調 iOS 的。
WebView:經過調 WebView 的技術去渲染,咱們的小程序也是基於這一塊。
自渲染:能夠不借助系統的,本身寫一套自渲染技術,這就是今天要講的重點——Flutter。
無論跨端技術怎麼演變,主要就是分這三大類。
咱們來看高一致性,上面是 Flutter 寫的一個 Demo,在安卓和 iOS 幾乎同樣。UI 小姐姐設計 UI 時每每但願多端體驗一致,它能夠作到高度一致。
咱們再講講它的高性能,爲何常說它是高性能?以安卓爲例,安卓原生框架 APP 調到你們熟悉的安卓 Framework,Flutter 再調到 Skia,Skia 再最終渲染調到 GPU。
右邊通常常規框架原生渲染怎麼作的?它有一個轉換層,經過轉換,最終安卓調安卓渲染,iOS 調 iOS 渲染機制,中間多了一個層。
對於 Flutter 中間調的是一個 Dart Framework,再調到 Skia,用 Flutter 平臺和原生很接近,Flutter 是上限很高的技術,若是你優化足夠好,它能夠媲美原生。固然,如今 Flutter 做爲一個新生嬰兒可能有些方面有所不足,可是不斷髮展,它終究能夠作得更好。
業界公司能夠直接作 Flutter,BAT、字節跳動等或多或少在它們業務上有落地,這是一個趨勢。
除此以外,Google 還有一個內部作的系統叫 Fusicha,它最上層也是用的 Flutter 和 Dart,這個系統將來如何?在 5G 和 IoT 市場不斷髮展,頗有可能這個系統也作得不錯,若是它有不錯的效果,你提早入局 Flutter,可能多了一次彎道超車的機會。
咱們來看看 Flutter 技術棧。上面是用 Dart 寫的 APP,下面有 DartFramework,Framework 裏有安卓和 iOS 的主體,裏面有不少動畫等等。再往下會調到引擎,引擎裏有消息、PlatformChannel、Dart VM 等,引擎層再到平臺,咱們看的是平臺、安卓仍是 Web,這是咱們常規的一個架構。
可是咱們看待一個系統必定要從動態視角去看待,什麼叫動態視角?這個 Flutter 是如何創立的?它的生命週期看它最終是怎麼起來的?
上圖依然是以安卓爲例,安卓有 Application 和 Activity,用 Dart 寫的 APP 終究也要這樣去跟原生鏈接起來,Flutter 有 Flutter 的 Application 和 Flutter 的 Activity。在 Flutter Application 中就會把 Dart 寫的代碼生成一個產物,把它加載起來,咱們的 Flutter Activity 過程當中會拉起咱們的引擎。
到了引擎層這塊,Flutter 裏四個核心線程:平臺線程、UI 線程、GPU 線程、IO 線程,它們各有分工:這個平臺線程對於安卓和 iOS 來講就是常說的主線程,這裏的 UI 線程面對安卓自己的主線程,就是一個獨立的線程;GPU 線程指的是跑在 CPU 上的線程,它作的主要是 Skia 相關工做。IO 線程好比圖片解碼、編解碼,主要作 IO 相關的工做。這裏既然有幾個線程,確定涉及線程之間的通訊,就回到技術的相通,若是你熟悉安卓、iOS 就知道線程怎麼通訊,一下子看看它們兩個的區別。
右邊有一個 Dart 虛擬機,這裏有一個 Isolate 的概念,這是 Flutter 裏纔有的概念,從名字也能夠看出來,「孤立的」,孤立的是什麼意思?兩個內存之間不會孤立、不會不共享。再看右上角,就到了 Dart 層,會有 UI 繪製。
接下來依次講一下這四塊。
第一個圖,先看看 Flutter 用 Dart 寫的代碼最終編譯的東西長什麼樣,最開始 Dart 代碼最立刻用前端編譯器編譯,左邊綠色的是安卓,右邊藍色的是 iOS,根據不一樣的平臺會產生不一樣的產物。左邊組成了一個 APK,右邊在咱們的 iOS 上是 Runner APP。最下面引擎代碼經過編譯產生在安卓和 iOS 有所不一樣,兩部分拼湊起來成爲相應不一樣平臺的 APK。發現用 Dart 寫的代碼在不一樣平臺會編譯成適應不一樣平臺的應用,這是最終編譯出來的產物。Flutter 可以把這個產物加載起來運行。
第二個圖,說到線程通訊,依然以安卓爲例,你們很是熟悉安卓,咱們是怎麼通訊的呢?這裏面有不少消息,簡單來講是把一個消息放到消息隊列裏去了,這是一個安卓的技術。咱們來看一下右邊 Flutter,會發現很是神奇,技術就是這樣的相通。Flutter 依然有一個 Looper 線程,對於主線程複用了原來的 Native 的,對於另外三個線程建立一個獨立的 Looper,不一樣的是它有兩個消息隊列,依然是跑消息的方式,經過 Task Runner 就比如安卓的 Handle,經過 PostTask 就比如把一個消息放在一個消息隊列裏去。一樣在 Dart 裏常常用 Furture 和異步的方法,核心仍是用咱們的 Task Runner 發一個消息。
咱們要看到技術的相通,也要看到技術的不一樣,這裏面不一樣的是什麼?不一樣的是在於咱們這裏有一個 MessageQueue,這裏有兩個隊列,它怎麼處理呢?先處理微型任務,微型任務處理完了再處理普通任務,因此在 Flutter 裏叫 Task,在安卓裏叫 Message,技術神奇的類似。你們不用擔憂,學習新技術成本很是高,把技術掌握透了,學習新技術會發現成本很低,很好去理解。
再看看 Isolate,同一個進程裏能夠有不少 Isolate,兩個 Isolate 的堆是不能共享的,但它們也要交互。怎麼交互?在 Dart 虛擬機裏面也有一個特殊 Isolate,是運行在 UI 線程中,和 Root Isolate 是運行在一個線程的。當兩個 Isolate 要通訊,會找一個共同的可訪問的內存,安卓聽到很是多的概念是「進程間通訊」,它最終怎麼通訊?用戶態進程不能共享,可是內核態能夠共享,這就找到一個能夠共享的地方,把通訊數據在內核態放進對方的隊列裏,另一邊就能夠拿到了。
一樣,Isolate 也是相似的邏輯,Isolate1 和 Isolate2 怎麼通訊?在 Isolate2 裏創一個 ReceivePort,在 Isolate1 中調用其對應的 SendPort 的 send 方法,在引擎 PortMap 裏面有映射表,每個 port 端口對應一個相應 Isolate 的 MessageHandler,該 Handler 裏面有兩個隊列,一個是普通的消息隊列,一個是 OOB 高優先級消息,根據優先級把它放到相應消息隊列。再把這個事件封裝成一個 MessageTask,拋到另一個 Isolate 裏去,咱們通常建立一個 Isolate,它裏面是一個 worker 線程,worker 線程放入一個新的 Task,它就會最終去執行這個 Task,最終會解析出這個消息,你會發現技術再一次相通了。
咱們再往上走,到了 UI 層是 Widget,在整個 Flutter 裏有不少 Widget。Widget 能夠是一行文本、一張圖片、一個顏色,全部都是 Widget。最大有兩類,一個是無狀態的 Widget,一個是有狀態的。無狀態顧名思義是一類 StatelessWidget,一旦建立狀態是不可改變的;第二類 StatefulWidget 是狀態能夠改變的。這涉及狀態是跟安卓不一樣的地方,既然有了狀態,應用越寫越複雜時就涉及狀態管理,如何去管理狀態。
對應 Widget 建立 Widget 樹,它是 Element 的配置,若是兩個 Widget 之間作了變化它會作差分,比較一下到底哪部分作了變化,只把變化更新到 Element 裏去,以最小粒度作更新。到了 Element 裏構建渲染過程當中會建立 Render 對象,建立渲染的過程。
渲染來了一個信號,UI 線程更新動畫,動畫完了作一個創建,創建過程當中生成 Render,再往下佈局、繪製大小等工做。這個完成之後會生成一個 Layer Tree,也跨兩個線程,UI 和 GPU 線程。到了 GPU 線程以後會調用 Skia 作渲染。
爲何用平臺的 Channel?你們常說,Flutter 是一個漂亮的 UI 工具,有時真的須要 Native 能力怎麼辦?好比調用相機的特性須要寫 Native 代碼,因此 Flutter 提供一套 Channel 機制,橙色部分代碼,寫相應平臺安卓或者 iOS 定製代碼,中間有一套機制幫你實現封裝好,你寫 Dart 代碼直接能夠調到安卓或者 iOS 代碼,這個過程是異步的。
你們大概看了 Flutter 引擎核心的技術,不光是 UI 層的,以及引擎層的機制。我過程當中屢次提到一點,技術是相通的,你在一個領域深耕,新的領域依然能夠作到繼續深耕。
字節跳動有超過 20 個應用在用 Flutter,頭條、西瓜視頻等都在用 Flutter,內部不少應用都已經採用 Flutter 落地了,這不是紙上工程,在不少業務上都已經落地了。
咱們移動平臺部在 Flutter 上作了哪些工做?
上層爲了支撐咱們這些應用,首先在應用框架層在工程化作了不少努力,用容器化思想,如何讓業務接起來更快;有混合工程的知識,讓業務快速接入 Flutter;咱們的狀態管理,在狀態混了的狀況下如何高效的接入;也爲了咱們業務更好的接入,咱們有不少技術組件的開發,提供了不少豐富的庫。
除此以外,爲了監控 Flutter,有不少性能高可用平臺,有穩定性測試等等。
引擎層也作了大量優化,雖然號稱 Flutter 高性能,可是業務使用中仍是要作到優化。首先,咱們不少工程師剛開始寫 Dart 不知道如何寫出更高效率的 Flutter 代碼,因此業務能夠優化,其次,引擎能夠作不少優化,以及多端一體化的嘗試,以及編譯、黑科技的,咱們在每一個領域都花了不少投入。
簡單看看咱們的容器化,爲業務支撐,有不少 APP,提供容器化擴展接口。好比要調圖片,圖片調到咱們的協議層,會調一個圖片的協議,裏面有一個默認的適配,還有一個用戶能夠自定義的,能夠不作任何定製,咱們會幫你提供經常使用技術組建。下面跟安卓、iOS 的交互均可以忽略,寫代碼很方便,不少庫都有,這就是容器化的思想,幫你封閉了底層差別性,讓你接入起來更加快捷。
再說說監控體系,看過咱們 Flutter 發現引擎自己有咱們的監控,上面是 GPU 線程,下面是 UI 線程,這是一個比較粗糙的性能統計方法。爲何粗糙?有幾點作不到位的地方:第一,把性能分紅 UI 和 GPU 線程,顯然咱們看參數時但願看一個參數,第二,它的算法很粗糙,它統計每一幀耗時,作物理平均,最後再將平均耗時根據 16.6ms 對應 60fps,來歸一化處理。
業界很多公司作了改進,都怎麼改進的?一般的方法在框架層去統計出 UI 線程的耗時時間,看它跨了多長時間,看它有多少幀是丟幀的,這個方案有什麼缺陷?咱們能夠看這個圖,理解這個渲染的過程,來了一個信號,你把這個消息 post 到 UI,裏面再作 Handle,再把這個事情 Post 到咱們的 GPU 線程。這個方案的缺陷是:第一,線程 Post 到 UI 線程中間有一個消息傳遞過程,有可能在 Post 過程當中要等待的這段時間沒有考慮進去。第二,若是你只統計 UI 線程有什麼缺陷?咱們舉個例子,UI 線程很是快,可是 GPU 線程很是慢,UI 線程每次可能一兩毫秒就完成了,可是 GPU 線程每次要一兩百毫秒,雖然看到性能很是好,可是真實體驗發現卡爆了,幾乎刷不動,爲何?由於 UI 線程向 GPU 線程跑消息時最多 Post 兩個消息,這時候 GPU 線程依然處理不過來,UI 線程就不會 Post 消息,可是 UI 線程體現不出來,因此這也是業界方案的缺陷。
咱們作了高精度無侵入性能監控方案:
首先,要知道繪製多少幀,因此咱們會統計你發了多少信號,統計 GPU 線程,最終真正繪出來多少幀,這是真正的體驗。咱們是在引擎層提供了一套。
第二,爲何叫無侵入?經常的性能監控會在咱們開始滾動時調一個開始,結束時調一個 End,咱們不須要作這個動做,接入框架系統會自動識別場景,自動判斷何時開始,何時結束,因此是高精度無侵入的性能監控。
在多端一體化也作過嘗試,內部寫一個應用能夠同時運行在 iOS、安卓端、Web,把一些內部組件串起來,作一個多端一體化的嘗試。多端一體化的核心就在於常規有一個 Flutter 引擎,右邊多一個 Flutter Web,調用 Dart2JS,這樣就把咱們 Web 包容進來了。
Flutter Turbo,作了性能提高的方案,如何在有限資源狀況下把咱們的性能發揮到極致?這個核心思想就是我要找到核心場景作有效的資源調度,好比用戶在界面滑動這個場景很是重要,你可否把一些資源有效控制?好比消息調度可否優先調度響應個人首次,這時候我在用戶很是重要的場景可否把 GC 抑制了,關閉能夠關閉的特效,有一系列手段,爲了提升咱們的性能,內部有一個 Benchmark 跑分,這些方案都可以帶來提高。
現有圖片怎麼傳?常規 Dart 裏要上網絡中去下載一張圖片會調 Image.network,而後把這張圖片加載以後,要把數據傳到 Dart 層,能夠傳一個路徑等等之類的,Dart 層會再調用引擎層解碼。外界紋理也很不錯,但它有一些問題,沒有在這個基礎上改造,而是作了本身的一套透存的方案。
圖中右邊是咱們在 Dart 層調引擎的示意,引擎加載圖片時咱們調到裏面加載,它生成一個 Bitmap,雖然引擎是 Dart 虛擬機,但二者之間咱們作了一個 Bitmap 的共享,你不須要把數據傳過來,咱們是共享的方式,咱們再轉成 Pixel buffer,對性能一樣會大大提高。
啓動速度優化咱們作得比較早,剛開始它啓動速度也很慢,咱們在引擎層裏也作了不少修改,發現它的啓動速度幾乎提高了 1 倍。
包體積是你們很是關注的一個點,爲何關注包體積?會決定用戶願意不肯意去下載這個應用,因此咱們在包體積方面作了不少工做,作了很大努力。
作了編譯優化,如何採用更高效率的編譯,iOS 上用了 O 編譯,發現包體積進一步壓縮。
Flutter 產物有數據端和代碼端,咱們把數據端進一步壓縮,包體積會減小。
在 Skia 咱們作了裁剪,把不太須要的功能以及不那麼必要的東西裁剪了。
引擎層咱們也作了一些功能三方庫的裁剪。
在指令這端發現用 Flutter 寫的指令會比咱們用 OC 代碼指令更長一些。這塊咱們跟 Google 作了積極的溝通反饋問題,Google 花了不少精力,咱們也跟進推動 Google 作這件事情。
一系列方案下來,咱們的包體積會接近 50%的優化。
以上是咱們字節跳動在 Flutter 裏作了衆多實踐中的部分重點工做。
第一,分享了 Flutter 的技術趨勢,Flutter 是你們爭相追尋的技術。
第二,跟你們介紹了 Flutter 引擎,發現裏面的技術是相通的,消息通訊跟安卓的 Handle 機制幾乎同樣;可是它也有它的差別,技術也有不一樣之處。
第三,字節跳動布了一個新的領域,發現咱們作了不少技術深耕。回到最開始問的問題,面對一個新技術,你是願意繼續選擇深耕老的技術,仍是願意嘗試新的技術?發如今作新技術時並無你們想象的那麼難,並且在過程當中你會收穫不少。
第四,既然選趨勢,你怎麼知道你選擇的就是那顆最大的鑽石?最後有一句話送給你們:
有時候你選擇一個方向,不是說它必定成爲將來,而是它有可能成爲不同的將來。
謝謝你們!
提問:我是一位安卓開發者,本身常常瞭解 Flutter,頗有疑問是關於代碼遷移,不少應用開發不少年了,代碼量是很大的,遷到 Flutter 上,您這邊在項目上有什麼經驗呢?或者有什麼建議呢?就是在代碼量很大的這種場景下。
回答:這是一個很好的問題。Flutter 有兩塊,第一,新業務是一個比較好的選擇,直接從零開始。第二,已有業務如何改造?建議剛開始先嚐試選擇頁面相對簡單的頁面,只能逐步切,先找相對輕量的頁面嘗試把路跑通,各方面數據指標都能達標,再嘗試更多,按部就班把更多切進去。不要想到工程這麼大一次所有規劃好全切過去,這個風險很是高,不光是技術問題,還涉及到商業問題。
提問:Native 和引擎之間 Bitmap 如何作共享的?
回答:很簡單,它們都在一個進程裏,爲何不能通訊呢?不是物理隔離,而是邏輯隔離,什麼叫邏輯隔離?寫代碼咱們在引擎層,引擎不少東西均可以直接去把地址傳過來,若是你在應用層可能作不到,可是 Flutter 能夠理解爲一個小型系統,好比就像你能夠改安卓的 Framework 和底層的東西,那麼什麼東西改不了呢?
提問:咱們如今一個項目兩個平臺,您是怎麼解決做爲項目當中的一個 Model,可是 Flutter 引用在原生當中,怎麼解決兩個平臺的嵌入問題、接入問題?
回答:你問的實際上是混合工程的問題。對於老業務改造必定離不開混合工程。混合工程問題,咱們也有工具叫 FlutterW,把混合工程一鍵化快速接入,好比我剛纔提的容器化都是讓混合工程快速接入的過程。
提問:我有一個比較有意思的問題,我看到 PPT 裏有說大家用了 Web 的東西。我比較好奇的是,這個東西處於一個 Build 的階段,以前說這個東西比較傾向作手機端的應用,由於它是比較傾向於作跟移動端同樣的體驗,因此我想知道大家對於用 FlutterWseb 是什麼規劃?好比是作成 H5 降級方案嗎?
回答:這個問題問得很好,首先 Flutter for Web 是一個預覽狀態,字節跳動即使是預覽版,也會跟進。剛纔展現的是技術跟進,除此以外內地也有一些內部項目落地 Flutter for Web,這是一個技術嘗試。也能夠用 Flutter for Web 做爲 Fallback 方案,也是一種不錯的備選方案。隨着 Flutter 的發展,相信明年 for Web 會更好。如今主要是安卓、iOS,Web 這個版也要補齊,如今它確實不夠成熟,從內部數據來看如今 Flutter for Web 的性能補 H5 還會差,雖然有一些優化,但還比 H5 差。若是真正用到線上大型應用的話,建議大家觀察一段時間,若是大家團隊人力比較多的話,也能夠如今接入去作相應改造。
上海沙龍回顧 | 字節跳動在Spark SQL上的核心優化實踐
上海沙龍回顧 | Apache Kylin 原理介紹與新架構分享(Kylin On Parquet)
字節跳動技術沙龍是由字節跳動技術學院發起,字節跳動技術學院、掘金技術社區聯合主辦的技術交流活動。
字節跳動技術沙龍邀請來自字節跳動及業內互聯網公司的技術專家,分享熱門技術話題與一線實踐經驗,內容覆蓋架構、大數據、前端、測試、運維、算法、系統等技術領域。
字節跳動技術沙龍旨在爲技術領域人才提供一個開放、自由的交流學習平臺,幫助技術人學習成長,不斷進階。
歡迎關注「字節跳動技術團隊」