真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

flutterui渲染,flutter 渲染

Flutter浪潮下的音視頻研發(fā)探索

文/陳爐軍

玉龍ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為成都創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!

整理/LiveVideoStack

大家好,我是阿里巴巴閑魚事業(yè)部的陳爐軍,本次分享的主題是Flutter浪潮下的音視頻研發(fā)探索,主要內(nèi)容是針對閑魚APP在當(dāng)下流行的跨平臺框架Flutter的大規(guī)模實(shí)踐,介紹其在音視頻領(lǐng)域碰到的一些困難以及解決方案。

分享內(nèi)容主要分為四個方面,首先會對Flutter有一個簡單介紹以及選擇Flutter作為跨平臺框架的原因,其次會介紹Flutter中與音視頻關(guān)系非常大的外接紋理概念,以及對它做出的一些優(yōu)化。之后會對閑魚在音視頻實(shí)踐過程中碰到的一些Flutter問題提出了一些解決方案——TPM音視頻框架。最后是閑魚Flutter多媒體開源組件的介紹。

Flutter

Flutter是一個跨平臺框架,以往的做法是將音頻、視頻和網(wǎng)絡(luò)這些模塊都下沉到C++層或者ARM層,在其上封裝成一個音視頻的SDK,供UI層的PC、iOS和Android調(diào)用。

而Flutter做為一個UI層的跨平臺框架,顧名思義就是在UI層也實(shí)現(xiàn)了一個跨平臺開發(fā)??梢灶A(yù)想的是未Flutter發(fā)展的好的話,會逐漸變?yōu)橐粋€從底層到UI層的一個全鏈路的跨平臺開發(fā),技術(shù)人員分別負(fù)責(zé)SDK和UI層的開發(fā)。

在Flutter之前已經(jīng)有很多跨平臺UI解決方案,那為什么選擇Flutter呢?

我們主要考慮性能和跨平臺的能力。

以往的跨平臺方案比如Weex,ReactNative,Cordova等等因?yàn)榧軜?gòu)的原因無法滿足性能要求,尤其是在音視頻這種性能要求幾乎苛刻的場景。

而諸如Xamarin等,雖然性能可以和原生App一致,但是大部分邏輯還是需要分平臺實(shí)現(xiàn)。

我們可以看一下,為什么Flutter可以實(shí)現(xiàn)高性能:

原生的native組件渲染以IOS為例,蘋果的UIKit通過調(diào)用平臺自己的繪制框架QuaztCore來實(shí)現(xiàn)UI的繪制,圖形繪制也是調(diào)用底層的API,比如OpenGL、Metal等。

而Flutter也是和原生API邏輯一致,也是通過調(diào)用底層的繪制框架層SKIA實(shí)現(xiàn)UI層。這樣相當(dāng)于Flutter他自己實(shí)現(xiàn)了一套UI框架,提供了一種性能超越原生API的跨平臺可能性。

但是我們說一個框架最終性能怎樣,其實(shí)取決于設(shè)計(jì)者和開發(fā)者。至于現(xiàn)在到底是一個什么狀況:

在閑魚的實(shí)踐中,我們發(fā)現(xiàn)在正常的開發(fā)沒有特意的去優(yōu)化UI代碼的情況下,在一些低端機(jī)上,F(xiàn)lutter界面的流暢性是比Native界面要好的。

雖然現(xiàn)在閑魚某些場景下會有卡頓閃退等情況,但是這是一個新事物發(fā)展過程中的必然問題,我們相信未來性能肯定不會成為限制Flutter發(fā)展的瓶頸的。

在閑魚實(shí)踐Flutter的過程中,混合棧和音視頻是其中比較難解決的兩個問題,混合棧是指一個APP在Flutter過程中不可能一口氣將所有業(yè)務(wù)全部重寫為Flutter,所以這是一個逐步迭代的過程,這期間原生native界面與Flutter界面共存的狀態(tài)就稱之為混合棧。閑魚在混合棧上也有一些比較好的輸出,例如FlutterBoost。

外接紋理

在講音視頻之前需要簡要介紹一下外接紋理的概念,我們將它稱之為是Flutter和Frame之間的橋梁。

Flutter渲染一幀屏幕數(shù)據(jù)首先要做的是,GPU發(fā)出的VC信號在Flutter的UI線程,通過AOT編譯的機(jī)器碼結(jié)合當(dāng)前Dart Runtime,生成Layer Tree UI樹,Layer Tree上每一個葉子節(jié)點(diǎn)都代表了當(dāng)前屏幕上所需要渲染的每一個元素,包含了這些元素渲染所需要的內(nèi)容。將Layer Tree拋給GPU線程,在GPU線程內(nèi)調(diào)用Skia去完成整個UI的渲染過程。Layer Tree中有PictureLayer和TextureLayer兩個比較重要的節(jié)點(diǎn)。PictureLayer主要負(fù)責(zé)屏幕圖片的渲染,F(xiàn)lutter內(nèi)部實(shí)現(xiàn)了一套圖片解碼邏輯,在IO線程將圖片讀取或者從網(wǎng)絡(luò)上拉取之后,通過解碼能夠在IO線程上加載出紋理,交給GPU線程將圖片渲染到屏幕上。但是由于音視頻場景下系統(tǒng)API太過繁多,業(yè)務(wù)場景過于復(fù)雜。Flutter沒有一套邏輯去實(shí)現(xiàn)跨平臺的音視頻組件,所以說Flutter提出了一種讓第三方開發(fā)者來實(shí)現(xiàn)音視頻組件的方式,而這些音視頻組件的視頻渲染出口,就是TextureLayer。

在整個Layer Tree渲染的過程中,TextureLayer的數(shù)據(jù)紋理需要由外部第三方開發(fā)者來指定,可以把視頻數(shù)據(jù)和播放器數(shù)據(jù)送到TextureLayer里,由Flutter將這些數(shù)據(jù)渲染出來。

TextureLayer渲染過程:首先判斷Layer是否已經(jīng)初始化,如果沒有就創(chuàng)建一個Texture,然后將Texture Attach到一個SufaceTexture上。

這個SufaceTexture是音視頻的native代碼可以獲取到的對象,通過這個對象創(chuàng)建的Suface,我們可以將視頻數(shù)據(jù)、攝像頭數(shù)據(jù)解碼放到Suface中,然后Flutter端通過監(jiān)聽SufaceTexture的數(shù)據(jù)更新就可以順利把剛才創(chuàng)建的數(shù)據(jù)更新到它的紋理中,然后再將紋理交給SKIA渲染到屏幕上。

然而我們?nèi)绻枰肍lutter實(shí)現(xiàn)美顏,濾鏡,人臉貼圖等等功能,就需要將視頻數(shù)據(jù)讀取出來,更新到紋理中,再將GPU紋理經(jīng)過美顏濾鏡處理后生成一個處理后的紋理。按Flutter提供的現(xiàn)有能力,必須先將紋理中的數(shù)據(jù)從GPU讀出到CPU中,生成Bitmap后再寫入Surface中,這樣在Flutter中才能順利的更新到視頻數(shù)據(jù),這樣做對系統(tǒng)性能的消耗很大。

通過對Flutter渲染過程分析,我們知道Flutter底層需要渲染的數(shù)據(jù)就是GPU紋理,而我們經(jīng)過美顏濾鏡處理完成以后的結(jié)果也是GPU紋理,如果可以將它直接交給Flutter渲染,那就可以避免GPU-CPU-GPU這樣的無用循環(huán)。這樣的方法是可行的,但是需要一個條件,就是OpenGL上下文共享。

OpenGL

在說上下文之前,得提到一個和上線文息息相關(guān)的概念:線程。

Flutter引擎啟動后會啟動四個線程:

第一個線程是UI線程,這是Flutter自己定義的UI線程,主要負(fù)責(zé)GPU發(fā)出的VSync信號時候用當(dāng)前Dart編譯的機(jī)器碼和當(dāng)前運(yùn)行環(huán)境創(chuàng)建出Layer Tree。

還有就是IO線程和GPU線程。和大部分OpenGL處理解決方案中一樣,F(xiàn)lutter也采取一個線程責(zé)資源加載,一部分負(fù)責(zé)資源渲染這種思路。

兩個線程之間紋理共享有兩種方式。一種是EGLImage(IOS是 CVOpenGLESTextureCache)。一種是OpenGL Share Context。Flutter通過Share Context來實(shí)現(xiàn)紋理共享,將IO線程的Context和GPU線程的Context進(jìn)行Share,放到同一個Share Group下面,這樣兩個線程下資源是互相可見可以共享的。

Platform線程是主線程,F(xiàn)lutter中有一個很奇怪的設(shè)定,GPU線程和主線程共用一個Context。并且在主線程也有很多OpenGL 操作。

這樣的設(shè)計(jì)會給音視頻開發(fā)帶來很多問題,后面會詳細(xì)說。

音視頻端美顏處理完成的OpenGL紋理能夠讓Flutter直接使用的條件就是Flutter的上下文需要和平臺音視頻相關(guān)的OpenGL上下文處在一個Share Group下面。

由于Flutter主線程的Context就是GPU的Context,所以在音視頻端主線程中有一些OpenGL操作的話,很有可能使Flutter整個OpenGL被破壞掉。所以需要將所有的OpenGL操作都限制在子線程中。

通過上述這兩個條件的處理,我們就可以在沒有增加GPU消耗的前提下實(shí)現(xiàn)美顏和濾鏡等等功能。

TPM

在經(jīng)過demo驗(yàn)證之后,我們將這個方案應(yīng)用到閑魚音視頻組件中,但改造過程中發(fā)現(xiàn)了一些問題。

上圖是攝像頭采集數(shù)據(jù)轉(zhuǎn)換為紋理的一段代碼,其中有兩個操作:首先是切進(jìn)程,將后面的OpenGL操作都切到cameraQueue中。然后是設(shè)置一次上下文。然后這種限制條件或者說是潛規(guī)則往往在開發(fā)過程中容易被忽略的。而這個條件一旦忽略后果就是出現(xiàn)一些莫名其妙的詭異問題極難排查。因此我們就希望能抽象出一套框架,由框架本身實(shí)現(xiàn)線程的切換、上下文和模塊生命周期等的管理,開發(fā)者接入框架以后只需要安心實(shí)現(xiàn)自己的算法,而不需要關(guān)心這些潛規(guī)則還有其他一些重復(fù)的邏輯操作。

在引入Flutter之前閑魚的音視頻架構(gòu)與大部分音視頻邏輯一樣采用分層架構(gòu):

1:底層是一些獨(dú)立模塊

2:SDK層是對底層模塊的封裝

3:最上層是UI層。

引入Flutter之后,通過分析各個模塊的使用場景,我們可以得出一個假設(shè)或者說是抽象:音視頻應(yīng)用在終端上可以歸納為視頻幀解碼之后視頻數(shù)據(jù)幀在各個模塊之間流動的過程,基于這種假設(shè)去做Flutter音視頻框架的抽象。

咸魚Flutter多媒體開源組件

整個Flutter音視頻框架抽象分為管線和數(shù)據(jù)的抽象、模塊的抽象、線程統(tǒng)一管理和上下文同一管理四部分。

管線,其實(shí)就是視頻幀流動的管道。數(shù)據(jù),音視頻中涉及到的數(shù)據(jù)包括紋理、Bit Map以及時間戳等。結(jié)合現(xiàn)有的應(yīng)用場景我們定義了管線流通數(shù)據(jù)以Texture為主數(shù)據(jù),同時可以選擇性的添加Bit Map等作為輔助數(shù)據(jù)。這樣的數(shù)據(jù)定義方式,避免重復(fù)的創(chuàng)建和銷毀紋理帶來的性能開銷以及多線程訪問紋理帶來的一些問題。也滿足一些特殊模塊對特殊數(shù)據(jù)的需求。同時也設(shè)計(jì)了紋理池來管理管線中的紋理數(shù)據(jù)。

模塊:如果把管線和數(shù)據(jù)比喻成血管和血液,那框架音視頻的場景就可以比喻成器官,我們根據(jù)模塊所在管線的位置抽象出采集、處理和輸出三個基類。這三個基類里實(shí)現(xiàn)了剛才說的線程切換,上下文切換,格式轉(zhuǎn)換等等共同邏輯,各個功能模塊通過集成自這些基類,可以避免很多重復(fù)勞動。

線程:每一個模塊初始化的時候,初始化函數(shù)就會去線程管理的模塊去獲取自己的線程,線程管理模塊可以決定給初始化函數(shù)分配新的線程或者已經(jīng)分配過其他模塊的線程。

這樣有三個好處:

一是可以根據(jù)需要去決定一個線程可以掛載多少模塊,做到線程間的負(fù)載均衡。第二,多線程并發(fā)式能夠保證模塊內(nèi)的OpenGL操作是在當(dāng)前線程內(nèi)而不會跑到主線程去,徹底避免Flutter的OpenGL 環(huán)境被破壞。第三,多線程并行可以充分利用CPU多核架構(gòu),提升處理速度。

從Flutter端修改Flutter引擎將Context取出后,根據(jù)Context創(chuàng)建上下文的統(tǒng)一管理模塊,每一個模塊在初始化的時候會獲取它的線程,獲取之后會調(diào)用上下文管理模塊獲取自己的上下文。這樣可以保證每一個模塊的上下文都是與Flutter的上下文進(jìn)行Share的,每個模塊之間資源都是共享可見的,F(xiàn)lutter和音視頻native之間也是互相共享可見的。

基于上述框架如果要實(shí)現(xiàn)一個簡單的場景,比如畫面實(shí)時預(yù)覽和濾鏡處理功能,

1:需要選擇功能模塊,功能模塊包括攝像頭模塊、濾鏡處理模塊和Flutter畫面渲染模塊,

2:需要配置模塊參數(shù),比如采集分辨率、濾鏡參數(shù)和前后攝像頭設(shè)置等,

3:在創(chuàng)建視頻管線后使用已配置的參數(shù)創(chuàng)建模塊

4:最后管線搭載模塊,開啟管線就可以實(shí)現(xiàn)這樣簡單的功能。

上圖為整個功能實(shí)現(xiàn)的代碼和結(jié)構(gòu)圖。

結(jié)合上述音視頻框架,閑魚實(shí)現(xiàn)了Flutter多媒體開源組件。

組要包含四個基本組件分別是:

1:視頻圖像拍攝組件

2:播放器組件

3:視頻圖像編輯組件

4:相冊選擇組件

現(xiàn)在這些組件正在走內(nèi)部開源流程。預(yù)計(jì)9月份,相冊和播放器會實(shí)現(xiàn)開源。

后續(xù)展望和規(guī)劃

1:實(shí)現(xiàn)開頭所說的從底層SDK到UI的全鏈路的跨端開發(fā)。目前底層框架層和模塊層都是各個平臺各自實(shí)現(xiàn),反而是Flutter的UI端進(jìn)行了跨平臺的統(tǒng)一,所以后續(xù)會將底層也按照音視頻常用做法把邏輯下沉到C++層,盡可能的實(shí)現(xiàn)全鏈路跨平臺。

2:第二部分內(nèi)容為開源共建,閑魚開源的內(nèi)容不僅包括拍攝、編輯組件,還包括了很多底層模塊,希望有開發(fā)者在基于Flutter開發(fā)音視頻應(yīng)用時可以充分利用閑魚開源出的音視頻模塊能力,搭建APP框架,開發(fā)者只要去負(fù)責(zé)實(shí)現(xiàn)特殊需求模塊就可以,盡可能的減少重復(fù)勞動。

為什么Flutter開發(fā)APP性能最接近原生,前端程序員請關(guān)注

Flutter是谷歌公司推出的跨終端的開發(fā)框架,支持Android、iOS和WEB終端。1.0版在2018年12月5日發(fā)布,目前的最新版本是1.5,它采用的開發(fā)語言是Dart,Dart也是谷歌開發(fā)的計(jì)算機(jī)編程語言,語法類似C,是編譯型語言:

hello world例子,打印字符串“Hello World!”:

1、沒有橋接層

React Native、Weex等技術(shù)都是跨終端的框架,然而性能跟原生App存在很大差距。這是由于它們的工作原理決定的:

React Native、Weex等技術(shù)多了一個橋接層,所以界面渲染會慢一些,由于UI渲染非常頻繁,想要不卡頓,基本上比較難,性能和用戶體驗(yàn)跟原生代碼有差距。而這恰恰是Flutter的優(yōu)勢所在:

Dart可以被編譯成不同平臺的本地代碼,讓Flutter不通過橋接層直接跟平臺通信,自然性能會快一些。

2、編譯執(zhí)行

JavaScript是解釋執(zhí)行的,Dart是編譯執(zhí)行的,性能誰好一目了然。

3、Flutter Engine虛擬機(jī)

Flutter是依靠Flutter Engine虛擬機(jī)在iOS和Android上運(yùn)行的,F(xiàn)lutter Engine使用C/C++編寫,開發(fā)人員通過Flutter框架直接和API在內(nèi)部進(jìn)行交互,所以具有輸入低延遲和UI渲染高幀速率的特點(diǎn)。除了這特點(diǎn)之外,F(xiàn)lutter還提供了自己的小部件,F(xiàn)lutter小部件是使用從React獲取靈感的現(xiàn)代框架構(gòu)建的。 中心思想是您使用小部件構(gòu)建UI。

窗口小部件根據(jù)其當(dāng)前配置和狀態(tài)描述了它們的視圖。 當(dāng)窗口小部件的狀態(tài)發(fā)生更改時,窗口小部件會重建其描述,框架將根據(jù)前面的描述進(jìn)行區(qū)分,以確定底層呈現(xiàn)樹從一個狀態(tài)轉(zhuǎn)換到下一個狀態(tài)所需的最小更改。可以直接在OS平臺提供的畫布上進(jìn)行描繪,也就是一些核心類庫直接放到虛擬機(jī)里面,調(diào)用起來更快。

從它的系統(tǒng)結(jié)構(gòu)可以看出,類似安卓的ART(Android Run Time)虛擬機(jī),同樣采用AOT(Ahead of TIme)技術(shù),會在APP安裝時就編譯成機(jī)器語言,不再解釋執(zhí)行,從而優(yōu)化了APP運(yùn)行的性能。

4、自帶渲染引擎

Flutter使用谷歌自己的Skia渲染引擎,而Android系統(tǒng)自帶Skia引擎,iOS平臺上Flutter也會把Skia引擎打包到APP中,從而實(shí)現(xiàn)了高效渲染。而React Native通過橋接層訪問原生UI,操作頻繁就容易出性能問題。

綜合所述,F(xiàn)lutter 是性能最接近原生代碼 的一種開發(fā)框架,未來也會是構(gòu)建谷歌Fuchsia應(yīng)用的主要方式,前途不可限量,唯一的問題就是需要學(xué)習(xí)一門新的語言:Dart,而有Java或者C#語言基礎(chǔ)的程序員會比較容易學(xué)習(xí)。

Flutter入門這一篇效率文章就夠了

本文面向 Flutter 初學(xué)者,旨在用易懂的方式帶大家入門。除了 Flutter 代碼,還會介紹到語法、原理、特性等基礎(chǔ)知識。相信本文能幫助你學(xué)習(xí)和理解 Flutter。

我們先看一下目前的一些跨平臺方案,從前端渲染的角度來分類的話,大致可以分為以下幾種方案。

WebView 渲染

這種方案就很好理解,現(xiàn)在很多項(xiàng)目都會嵌入 H5 的頁面。就是用 JavaScript 等前端技術(shù)進(jìn)行開發(fā),在客戶端上用 WebView 來進(jìn)行渲染。微信小程序目前使用的就是這種方案。

它的優(yōu)點(diǎn)很明顯,使用成熟的前端技術(shù)進(jìn)行開發(fā),學(xué)習(xí)成本低,開發(fā)效率高,并且支持動態(tài)發(fā)布代碼。

但缺點(diǎn)也很明顯,在性能體驗(yàn)上,和原生還是存在較大差距的。

原生控件渲染

既然 WebView 的性能不夠好,于是就有了使用原生控件進(jìn)行渲染的方案。這種方案,同樣也是使用 JavaScript 開發(fā),區(qū)別是它最終是調(diào)用原生控件進(jìn)行渲染的。這種方案的代表是 Facebook 的 React Native。

由于使用原生控件進(jìn)行渲染,性能體驗(yàn)也會更接近原生。但也只是更接近,和原生還是有差距的,因?yàn)樗枰l繁的進(jìn)行 JavaScript 和原生之間的通信,這個通信效率是比較低的。

另外,由于需要適配各個平臺的控件,那就有可能出現(xiàn),系統(tǒng)控件更新了,而框架本身還沒有更新,由此產(chǎn)生了一些問題。換句話說,這種方案是受到原生控件限制的。

繪圖引擎渲染

接下來就是主角了。

在前端,如果完全不使用原生控件,我們可以通過系統(tǒng)的繪圖 API 繪制出一個用戶界面。從這個角度出發(fā),可以在各個平臺使用一個統(tǒng)一接口的繪圖引擎來進(jìn)行界面繪制,這個引擎最終調(diào)用的是系統(tǒng)的 API 繪制的。這樣的話,它的性能可以做到接近原生,并且又不受原生控件的限制,在不同平臺上能夠做到 UI 統(tǒng)一。

Flutter 就是這樣的一個開發(fā)框架。

一個跨平臺 UI 解決方案

Flutter 是由 Google 開發(fā)的,一個跨平臺 UI 解決方案。換句話說,它原則上只管 UI 的問題,如果涉及到平臺本身的一些功能,比如調(diào)用藍(lán)牙、攝像頭,一般還是需要原生代碼去操作。但現(xiàn)在也會有一些第三方庫幫我們解決這些問題。

繪圖引擎 Skia

Flutter 使用 Skia 作為它的繪圖引擎。Skia 已經(jīng)被 Google 收購,目前很多 Google 旗下的產(chǎn)品都是用 Skia 繪制的,包括 Android。

Android 內(nèi)置了 Skia,但 iOS 沒有,所以在打 iOS 安裝包的時候,會把 Skia 一起打進(jìn)去。這就導(dǎo)致了,用同一份 Flutter 代碼打包之后,iOS 的包要比 Android 的包大一些。

開發(fā)語言 Dart

Flutter 使用的開發(fā)語言,叫 Dart。Dart 也是 Google 自家的,它是一門面向?qū)ο蟮恼Z言,從它身上會看到一些其他開發(fā)語言的影子。學(xué)習(xí)起來難度不大的。

前面講跨平臺方案的時候,可以發(fā)現(xiàn)別的方案基本都是用 JavaScript 作為開發(fā)語言的,但為什么 Flutter 不用?就因?yàn)?Dart 是谷歌自家的嗎?這個問題先留著,我們后面會提到。

這里部分就簡單點(diǎn)帶過了,具體的搭建流程可以在官網(wǎng)查看:

主要的搭建步驟如下:

下載 Flutter SDK

官網(wǎng)下載地址:

由于在國內(nèi)訪問可能受限,官方為中國開發(fā)者搭建了鏡像:

更新環(huán)境變量

解壓后,將 flutter\bin 的全路徑添加到環(huán)境變量 PATH 中。

安裝開發(fā)工具

理論上,任何文本編輯器都可以用來開發(fā) Flutter 應(yīng)用,但推薦的開發(fā)工具是 Android Studio、IntelliJ 以及 VS Code。因?yàn)樵谶@些開發(fā)工具上,可以安裝官方的 Flutter 和 Dart 插件,得到更好的開發(fā)體驗(yàn)。文章里使用 Android Studio 來演示。

如果你打算開發(fā) iOS 應(yīng)用,則還需要安裝 Xcode。

安裝插件

在開發(fā)工具的插件設(shè)置中,安裝上面說到的 Flutter 和 Dart 插件。Flutter 插件用于支持 Flutter 的運(yùn)行、調(diào)試、熱重載等功能,而 Dart 插件則提供了代碼的輸入校驗(yàn)、代碼補(bǔ)全等功能。

萬物始于 Hello World,我們先來創(chuàng)建一個顯示 Hello World 的 Flutter 項(xiàng)目。

在 Android Studio 的歡迎頁面選擇 Start a new Flutter project ,或者通過菜單欄的 File New New Flutter Project ,創(chuàng)建一個新的 Flutter 項(xiàng)目。

創(chuàng)建好的項(xiàng)目里面包含了 android 和 ios 兩個文件夾,它們是標(biāo)準(zhǔn)的 Android 和 iOS 項(xiàng)目。我們的 Flutter 代碼,存放在 lib 文件夾里。項(xiàng)目創(chuàng)建好后,會默認(rèn)帶一個計(jì)數(shù)器的示例,我們不管它,把 main.dart 的代碼改成 Hello World:

啟動一個模擬器,或者連上真機(jī),點(diǎn)擊 Run 運(yùn)行一下,就能看這樣一個界面了:

具體代碼先混個眼熟就好,具體的后面會再講到。

在寫 Flutter 之前,還要先跟大家簡單介紹一下 Dart 的語法。如果你有 Java 或 JavaScript 的開發(fā)經(jīng)驗(yàn),以及面向?qū)ο蟮木幊趟枷?,學(xué)起來是很快的。

我們可以在 test 文件夾下新建一個 dart 文件,用來寫測試代碼。

指定類型

var

但和 JavaScript 不同的是,以下代碼在 JavaScript 是不會報(bào)錯的,但在 Dart 里會報(bào)錯:

Object

如果非要上面這樣寫,那也可以。把 var 換成 Object 就不報(bào)錯了:

和 Java 類似,Object 是所有對象的根基類。但是這樣的話,如果想打印一下 num 的字符串長度,是會報(bào)錯的:

因?yàn)?length 是屬于 String 的,但系統(tǒng)只知道 num 是一個對象,并不知道它是一個 String。

dynamic

如果還是非要這樣寫,那也可以。Dart 有一個特有的關(guān)鍵字 dynamic,把 Object 改成 dynamic 就不報(bào)錯了:

我們運(yùn)行一下這個文件,可以在控制臺看到正確打印出了字符串長度。

函數(shù)

dynamic

在 Dart 里,函數(shù)也是可以不寫返回類型的,不寫的話會被當(dāng)做 dynamic 來處理。這樣的話,函數(shù)的類型就是 return 的類型,如果沒有 return 則是 void 類型。比如可以這樣:

運(yùn)行之后是能正確打印出字符串長度的。

用于傳參

Dart 里的函數(shù)也是一個對象,所以可以把函數(shù)作為參數(shù)來傳遞,比如:

可選參數(shù)

在 Dart 的函數(shù)傳參里,有一個叫可選參數(shù)的概念,我們以文字控件 Text 為例,在源碼里可以看到 Text 的構(gòu)造函數(shù)是這樣的:

首先,在參數(shù)里有一個 data,它是要顯示的文字內(nèi)容,是一個必填項(xiàng)。而 data 后面的一堆參數(shù),是用一個大括號括起來的,這些參數(shù)就叫做可選參數(shù),意思是這些參數(shù)可傳可不傳。

假如我們要顯示一個比較長的文字,又想限制它最多顯示兩行,就可以這樣來創(chuàng)建一個 Text:

可選參數(shù),在 Flutter 里面用的非常多。

異步

Future

在 Dart 里使用 Future 來處理異步任務(wù),比如我們現(xiàn)在延時一秒打印 666,代碼如下:

Future 的語法和 Promise 非常像。任務(wù)執(zhí)行成功會調(diào)用 then,執(zhí)行失敗會調(diào)用 catchError,而無論成功還是失敗,都會調(diào)用 whenComplete。

async/await

如果你不喜歡上面那種寫法,或者是想把異步轉(zhuǎn)成同步,就可以用 async 和 await 這兩個關(guān)鍵字來轉(zhuǎn)換。

我們把上面的代碼轉(zhuǎn)換一下,寫一個 getString 方法,返回的類型是 Future,它會延時返回一個字符串。在 main 函數(shù)后面加上 async 關(guān)鍵字,在 getString() 前面加上 await,代碼如下:

運(yùn)行之后可以看到,能正常延時一秒后,把字符串打印出來。這里 getString() 返回的類型是 Future,而 await getString() 則是返回了延時之后返回的字符串。await 要在 async 的函數(shù)里面才能使用。

async 和 await 其實(shí)是一個語法糖,它最終也是轉(zhuǎn)換成 Future 調(diào)用鏈的形式執(zhí)行的。

接下來回到 Flutter,F(xiàn)lutter 里最重要的一個概念是 Widget(下面翻譯作控件)。

在原生開發(fā)里面,我們可能會在界面上區(qū)分,這是一個 View,這是一個 Layout,這是一個 View Controller。但在 Flutter 里面,它們?nèi)紝儆谝粋€統(tǒng)一的模型 Widget??梢哉f,在 Flutter 界面里,所有東西都是 Widget。

以前學(xué)面向?qū)ο蟮臅r候,我們都聽過一句話,叫萬物皆對象。我這里套用一下,在 Flutter 里, 萬物皆控件 。

具體有哪些控件,我做了一下簡單的分類。

根控件

所有的控件都屬于 StatefulWidget 或 StatelessWidget 。它們的區(qū)別是,StatefulWidget 擁有狀態(tài) State ,而 StatelessWidget 沒有。

StatefulWidget

當(dāng)一個控件是可變的時候,就要使用 StatefulWidget 來構(gòu)建。StatefulWidget 本身不可變,但它持有的狀態(tài) State 是可變的。

StatelessWidget

當(dāng)一個控件狀態(tài)是固定不可變的時候,就可以使用 StatelessWidget。前面我們寫的 Hello World 就是使用 StatelessWidget。

容器控件

容器類控件一般是將某些屬性或配置,作用在它的子控件上,比如控件所在的寬高、背景、位置等。

常用的容器控件有 Container、Center、Padding 等。

布局控件

布局控件可以類比作原生開發(fā)中的 Layout,通常它會擁有一個 children 的屬性,用于接收一個控件數(shù)組,對這些控件進(jìn)行特定的排版。

常用的布局控件有 Row、Column、Stack、Flex 等。

基礎(chǔ)控件

基礎(chǔ)控件就是常用的文字、按鈕、圖片等控件。

常用的基礎(chǔ)控件有 Text、TextField、Button、Image 等。

功能控件

在 Flutter 里還有一類控件,它們不影響 UI 布局,但帶有一些特定的功能,比如頁面跳轉(zhuǎn)、事件監(jiān)聽、定義主題等。我們把這一類控件稱作功能控件。

常用的功能控件有 Navigator、NotificationListener、Theme 等。

開始寫 Flutter 代碼了。還記不記得,在 Flutter 項(xiàng)目創(chuàng)建之后,是自帶一個計(jì)數(shù)器 demo 的,現(xiàn)在我們用自己的代碼實(shí)現(xiàn)一遍。代碼修改成如下:

運(yùn)行之后,就可以看到這樣的界面了:

按鈕每點(diǎn)擊一次,數(shù)字就會加一。下面我們來分析一下這段代碼,看下里面用到的一些 Widget。

StatefulWidget

由于頁面中的數(shù)字是跟隨狀態(tài)變化的,所以該頁面改用 StatefulWidget。StatefulWidget 并不會直接返回一個 Widget,而是返回狀態(tài) State,在 State 里再返回 Widget。

Scaffold

Scaffold 是一個標(biāo)準(zhǔn)的 Material Design 頁面,它包含了標(biāo)題欄、浮動按鈕、側(cè)滑菜單、底部導(dǎo)航欄等配置。我們這里用到了標(biāo)題欄 appBar、頁面內(nèi)容 body、浮動按鈕 floatingActionButton。

AppBar

AppBar 就是標(biāo)題欄,通過查看控件的構(gòu)造方法,我們可以知道它可配置的屬性。

AppBar 的可選參數(shù)除了標(biāo)題 title,還可以配置標(biāo)題前的內(nèi)容 leading,右側(cè)的操作按鈕 anctions,控件垂直高度 elevation 等。我們只傳了 title,其他屬性都用默認(rèn)值。

Center

Center 是一個容器類控件,它的作用就是讓它的子控件居中顯示。

FloatingActionButton

熟悉安卓開發(fā)的應(yīng)該對這個控件比較熟悉,它就是頁面右下角一個特定樣式的 Button,參數(shù)里面的 onPressed 是一個必填項(xiàng),要傳一個點(diǎn)擊之后的回調(diào)函數(shù)。

根據(jù)這個例子,下面給大家介紹一下 Flutter 兩個比較重要的特性。

點(diǎn)擊 Button 之后,我們把 num 變量加一,并使用 setState 通知狀態(tài)發(fā)生了改變,F(xiàn)lutter 會根據(jù)新的狀態(tài)更新 UI。如果有接觸過小程序開發(fā),setState 就和小程序的 setData 類似。

在 Flutter 里面我們不需要用 set 方法來更新 UI,可變控件是和狀態(tài)綁定的,這就是 Flutter 的響應(yīng)式 UI 編程。

在 Android Q 和 iOS 13 里都加入了暗黑模式,我們也換一個暗黑主題來玩一下。MaterialApp 里有一個 theme 的屬性,我們把它配置一下:

這次改完之后不點(diǎn) Run 了,我們點(diǎn)一下閃電圖標(biāo) Flutter Hot Reload ,就能看到界面發(fā)生了變化:

這就是 Flutter 的 熱重載 ,在修改完代碼之后,通過熱重載就能馬上在設(shè)備上看到修改結(jié)果,可以很大程度上增加開發(fā)效率。

下面再給大家介紹幾個 Flutter 里的常見操作。

在 Flutter 里,使用 Navigator 來管理頁面跳轉(zhuǎn),比如要跳轉(zhuǎn)到一個 NewPage 可以這樣寫:

進(jìn)棧使用 push,出棧則是 pop。

使用 MaterialPageRoute 會模擬出 Android 上頁面跳轉(zhuǎn)的過場效果。

我們來看看怎么顯示一張本地圖片。

先在根目錄新建一個存放圖片的文件夾,比如叫 images,把圖片 picture.png 放進(jìn)去。

找到根目錄下的 pubspec.yaml 文件,這個便是 Flutter 依賴配置文件,我們需要在這里配置一下剛才的圖片:

這樣,我們就能使用 Image 控件把這張圖片顯示出來了:

和 node 的 npm 以及 Android 的 jcenter 類似,F(xiàn)lutter 也擁有一個公共倉庫 pub.dev。pub.dev 是 Google 官方的 Dart 倉庫,在上面可以找到我們需要的包和插件。

Flutter 本身沒有 Toast,我們來接入一個。在 pub.dev 上搜索后,我決定使用 fluttertoast:

按照說明,在 pubspec.yaml 文件里的 dependencies 下配置:

點(diǎn)一下 Android Studio 右上角的 Packages get 同步之后就可以使用了:

我們上面使用的都是 Material Design 的控件,它們都是在 flutter/material.dart 包里面的。如果要使用 iOS 風(fēng)格的控件,則要用到 flutter/cupertino.dart 包:

iOS 風(fēng)格的控件,基本都以 Cupertino 開頭。我們把計(jì)時器頁面里的控件替換一下:

效果如下:

代碼的部分就到這里了,接下來跟大家聊一下編譯方式,編程語言的編譯方式有兩種。

關(guān)于它們孰優(yōu)孰劣,就要看從哪個角度去對比了。JIT 的話,它的一大特點(diǎn)就是支持動態(tài)發(fā)布代碼,也就是支持熱更新。但要是從性能的角度考慮,AOT 會更好,因?yàn)樵谶\(yùn)行的時候不用再進(jìn)行編譯的操作的,運(yùn)行的效率會更高一些。

回到我們一開始的時候留下的問題,為什么別的跨平臺方案都是用 JavaScript,而 Flutter 要用 Dart 來開發(fā)。JavaScript 的編譯方式是 JIT 的,它不支持 AOT。而 Dart 同時支持 JIT 和 AOT。

Flutter 在開發(fā)階段使用 JIT,讓我們用上了熱重載,增加了開發(fā)效率。在打包時改用 AOT,保證了正式版應(yīng)用的性能。

最后講一下大家比較關(guān)心的一個東西,F(xiàn)lutter 是否支持熱更新?前面說到 Dart 支持 JIT,所以從技術(shù)層面它是支持的。但是目前是不支持的,在官方的計(jì)劃文檔中,可以看到:

至于原因,官方在這里進(jìn)行了說明。總的來說,是由于政策的限制,以及出于對性能和安全性的考慮,暫時不支持了。

到這就結(jié)束啦。由于想把 Flutter 基礎(chǔ)在一篇內(nèi)講完,沒有涉及太多細(xì)節(jié),如果要寫 Flutter 代碼還需要深入學(xué)習(xí)。但相信理解之后再學(xué),會輕松很多。

Flutter 啟動頁的前世今生適配歷程

APP 啟動頁在國內(nèi)是最常見也是必備的場景,其中啟動頁在 iOS 上算是強(qiáng)制性的要求,其實(shí)配置啟動頁挺簡單,因?yàn)樵?Flutter 里現(xiàn)在只需要:

一般只要配置無誤并且圖片尺寸匹配,基本上就不會有什么問題, 那既然這樣,還有什么需要適配的呢?

事實(shí)上大部分時候 iOS 是不會有什么問題, 因?yàn)? LaunchScreen.storyboard 的流程本就是 iOS 官方用來做應(yīng)用啟動的過渡;而對于 Andorid 而言,直到 12 之前 windowBackground 這種其實(shí)只能算“民間”野路子 ,所以對于 Andorid 來說,這其中就涉及到一個點(diǎn):

所以下面主要介紹 Flutter 在 Android 上為了這個啟動圖做了哪些騷操作~

在已經(jīng)忘記版本的“遠(yuǎn)古時期” , FlutterActivity 還在 io.flutter.app.FlutterActivity 路徑下的時候,那時啟動頁的邏輯相對簡單,主要是通過 App 的 AndroidManifest 文件里是否配置了 SplashScreenUntilFirstFrame 來進(jìn)行判斷。

在 FlutterActivity 內(nèi)部 FlutterView 被創(chuàng)建的時候,會通過讀取 meta-data 來判斷是否需要使用 createLaunchView 邏輯 :

是不是很簡單,那就會有人疑問為什么要這樣做?我直接配置 Activity 的 android:windowBackground 不就完成了嗎?

這就是上面提到的時間差問題, 因?yàn)閱禹摰?Flutter 渲染完第一幀畫面中間,會出現(xiàn)概率出現(xiàn)黑屏的情況,所以才需要這個行為來實(shí)現(xiàn)過渡 。

經(jīng)歷了“遠(yuǎn)古時代”之后, FlutterActivity 來到了 io.flutter.embedding.android.FlutterActivity , 在到 2.5 版本發(fā)布之前,F(xiàn)lutter 又針對這個啟動過程做了不少調(diào)整和優(yōu)化,其中主要就是 SplashScreen 。

自從開始進(jìn)入 embedding 階段后, FlutterActivity 主要用于實(shí)現(xiàn)了一個叫 Host 的 interface ,其中和我們有關(guān)系的就是 provideSplashScreen 。

默認(rèn)情況下它會從 AndroidManifest 文件里是否配置了 SplashScreenDrawable 來進(jìn)行判斷 。

默認(rèn)情況下當(dāng) AndroidManifest 文件里配置了 SplashScreenDrawable ,那么這個 Drawable 就會在 FlutterActivity 創(chuàng)建 FlutterView 時被構(gòu)建成 DrawableSplashScreen 。

DrawableSplashScreen 其實(shí)就是一個實(shí)現(xiàn)了 io.flutter.embedding.android.SplashScreen 接口的類,它的作用就是:

之后 FlutterActivity 內(nèi)會創(chuàng)建出 FlutterSplashView ,它是個 FrameLayout。

FlutterSplashView 將 FlutterView 和 ImageView 添加到一起, 然后通過 transitionToFlutter 的方法來執(zhí)行動畫,最后動畫結(jié)束時通過 onTransitionComplete 移除 splashScreenView 。

所以整體邏輯就是:

當(dāng)然這里也是分狀態(tài):

當(dāng)然這個階段的 FlutterActivity 也可以通過 override provideSplashScreen 方法來自定義 SplashScreen 。

看到?jīng)]有,做了這么多其實(shí)也就是為了彌補(bǔ)啟動頁和 Flutter 渲染之間, 另外還有一個優(yōu)化,叫 NormalTheme 。

通過該配置 NormalTheme ,在 Activity 啟動時,就會首先執(zhí)行 switchLaunchThemeForNormalTheme(); 方法將主題從 LaunchTheme 切換到 NormalTheme 。

大概配置完就是如下樣子, 前面分析那么多其實(shí)就是為了告訴你,如果出現(xiàn)問題了,你可以從哪個地方去找到對應(yīng)的點(diǎn) 。

講了那么多, Flutter 2.5 之后 provideSplashScreen 和 io.flutter.embedding.android.SplashScreenDrawable 就被棄用了,驚不喜驚喜,意不意外,開不開心 ?

通過源碼你會發(fā)現(xiàn),當(dāng)你設(shè)置了 splashScreen 的時候,會看到一個 log 警告:

為什么會棄用?

其實(shí)這個提議是在 這個 issue 上,然后通過 這個 pr 完成調(diào)整。

大概意思就是: 原本的設(shè)計(jì)搞復(fù)雜了,用 OnPreDrawListener 更精準(zhǔn),而且不需要為了后面 Andorid12 的啟動支持做其他兼容,只需要給 FlutterActivity 等類增加接口開關(guān)即可 。

也就是2.5之后 Flutter 使用 ViewTreeObserver.OnPreDrawListener 來實(shí)現(xiàn)延遲直到加載出 Flutter 的第一幀。

為什么說默認(rèn)情況? 因?yàn)檫@個行為在 FlutterActivity 里,是在 getRenderMode() == RenderMode.surface 才會被調(diào)用,而 RenderMode 又和 BackgroundMode 有關(guān)心 。

所以在 2.5 版本后, FlutterActivity 內(nèi)部創(chuàng)建完 FlutterView 后就會執(zhí)行一個 delayFirstAndroidViewDraw 的操作。

這里主要注意一個參數(shù): isFlutterUiDisplayed 。

當(dāng) Flutter 被完成展示的時候, isFlutterUiDisplayed 就會被設(shè)置為 true。

所以當(dāng) Flutter 沒有執(zhí)行完成之前, FlutterView 的 onPreDraw 就會一直返回 false ,這也是 Flutter 2.5 開始之后適配啟動頁的新調(diào)整。

看了這么多,大概可以看到其實(shí)開源項(xiàng)目的推進(jìn)并不是一帆風(fēng)順的,沒有什么是一開始就是最優(yōu)解,而是經(jīng)過多方嘗試和交流,才有了現(xiàn)在的版本,事實(shí)上開源項(xiàng)目里,類似這樣的經(jīng)歷數(shù)不勝數(shù):

Flutter基礎(chǔ)篇——常用Widget

對于初學(xué)flutter的朋友來說,要知道,flutter的UI萬物皆Widget。

flutter所寫的頁面的結(jié)構(gòu)可以被看成套娃,一層套一層,一層套一層,一層套一層。。。。。。

Flutter Widget采用現(xiàn)代響應(yīng)式框架構(gòu)建,這是從 React 中獲得的靈感,中心思想是用widget構(gòu)建你的UI。 Widget描述了他們的視圖在給定其當(dāng)前配置和狀態(tài)時應(yīng)該看起來像什么。當(dāng)widget的狀態(tài)發(fā)生變化時,widget會重新構(gòu)建UI,F(xiàn)lutter會對比前后變化的不同, 以確定底層渲染樹從一個狀態(tài)轉(zhuǎn)換到下一個狀態(tài)所需的最小更改。

Text : 該 widget 可讓創(chuàng)建一個帶格式的文本。

Row 、 Column : 這些具有彈性空間的布局類Widget可讓您在水平( Row )和垂直( Column )方向上創(chuàng)建靈活的布局。

Stack :取代線性布局 (和Android中的LinearLayout相似),Stack允許子 widget 堆疊, 你可以使用 Positioned 來定位他們相對于 Stack 的上下左右四條邊的位置。

Container : Container 可讓您創(chuàng)建矩形視覺元素。 您可以為 Container 裝飾一個 BoxDecoration , 如 background、一個邊框、或者一個陰影。 Container 也可以具有邊距(margins)、填充(padding)和應(yīng)用于其大小的約束(constraints)。另外, Container 可以使用矩陣在三維空間中對其進(jìn)行變換。

具體的演示見我另外的博客

有一部分Widget都有一個 child 屬性,用于容納唯一的子Widget。

例如:Container、Center、Padding、Align等Widget。

還有一部分Widget允許存在多個子Widget,用 children 作為屬性。

例如:Row、Column、Stack等Widget。

在StatefulWidget調(diào)用createState之后,框架將新的狀態(tài)插入樹種,然后調(diào)用狀態(tài)對象的initState。子類化State可以重寫initState,以完成僅需要一次執(zhí)行的工作。當(dāng)然在initState的實(shí)現(xiàn)中需要調(diào)用super.initState

當(dāng)一個狀態(tài)對象不再需要時,框架調(diào)用狀態(tài)對象的dispose。也可以通過覆蓋dispose方法來執(zhí)行清理工作。

OVER~

Flutter動態(tài)化方案調(diào)研

騰訊課堂14M

今日頭條3M

閑魚22M

百度貼吧13M

螞蟻財(cái)富56.8M

百度網(wǎng)盤14M

手機(jī)淘寶15M

貝殼找房8M

由粗粒度小組件動態(tài)拼裝出頁面,Native端已經(jīng)有很多成熟的框架,如天貓的Tangram。

開發(fā)語言:iOS、Android

適用場景:快速迭代的活動營銷頁面

優(yōu)點(diǎn):無侵入,更新簡單

缺點(diǎn):提前預(yù)埋,擴(kuò)展性差,靈活性差

以webview作為容器的app,歷史悠久,最早到2011年。

開發(fā)語言:HTML

適用場景:雙端嚴(yán)格一致的銀行類app,容器類的支付寶小程序等

優(yōu)點(diǎn):動態(tài)更新,跨平臺

缺點(diǎn):性能,加載速度

UI用Xml+JS表達(dá),用Native View渲染。

開發(fā)語言:Xml+JS

適用場景:雙端嚴(yán)格一致的銀行類app,容器類的支付寶小程序等

優(yōu)點(diǎn):native組件,生態(tài)成熟

缺點(diǎn):三方庫crash,性能缺陷

UI用Dart表達(dá),用Dart engine渲染。

Flutter官方不支持動態(tài)化。原因是Flutter在 Release 模式下構(gòu)建的是 AOT 編譯產(chǎn)物,在 Debug 模式下構(gòu)建的是 JIT ,AOT 依賴的 Dart VM 和 JIT 并不一樣, JIT Release 并不支持 iOS 設(shè)備??尚械姆桨甘牵篈OT 需要一個編譯后的 “Dart VM”。抽離一份 DartVM 獨(dú)立編譯,再以動態(tài)庫的形式引入項(xiàng)目。

開發(fā)語言:Dart

適用場景:iOS、Android、Web、Desktop、Embed

優(yōu)點(diǎn):性能最佳

缺點(diǎn):增大包體積 20MB+

大廠的主流方案。UI用JS表達(dá),用Dart engine渲染。

開發(fā)語言:JS、類JS

適用場景:iOS、Android

優(yōu)點(diǎn):性能最佳

缺點(diǎn):需要掌握J(rèn)S、Dart兩個語言和框架

大廠的主流方案。UI用Dart表達(dá),用Dart engineX渲染。

開發(fā)語言:Dart

適用場景:iOS、Android

優(yōu)點(diǎn):性能最佳

缺點(diǎn):需要改造Dart engine

1、 美團(tuán)外賣Flutter動態(tài)化實(shí)踐

2、 攜程App 首頁動態(tài)化探索

3、 Flutter 動態(tài)化在最右 App 中的實(shí)踐

4、 Flutter 動態(tài)化熱更新的思考與實(shí)踐

5、 NOW直播Flutter動態(tài)搜索列表頁實(shí)現(xiàn)

6、 Flutter動態(tài)化的方案對比及最佳實(shí)現(xiàn)-閑魚

7、 基于JavaScript 的MXFlutter


標(biāo)題名稱:flutterui渲染,flutter 渲染
瀏覽地址:http://weahome.cn/article/hogodc.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部