flutter web有三種渲染模式,auto 、html 和 canvaskit。
10余年品牌的成都網站建設公司,上千家企業網站設計經驗.價格合理,可準確把握網頁設計訴求.提供定制網站建設、成都做商城網站、微信平臺小程序開發、成都響應式網站建設等服務,我們設計的作品屢獲殊榮,是您值得信賴的專業網站制作公司。
flutter build web命令默認的渲染模式為auto,這種模式在移動端使用html渲染,在pc端使用canvaskit渲染。
目前我的flutter版本是2.5.2,pc端瀏覽器使用canvaskit渲染時中文會出現短暫的亂碼(方塊叉號),像這樣:
我們可以指定渲染模式為html,就不會有這個問題了,命令如下:
指定渲染模式為canvaskit的命令為:
2021.10.21:flutter web對中文的支持貌似不太好,在手機瀏覽器調試web項目時,textfield hinttext有中文輸入會有卡頓bug
前言
最近街邊討論買基金大佬們又多起來了,一些技術交流群也時不時看到某某某大佬在討論股票,看來最近行情很好啊,雖然我不懂交易,但我總覺得可以做些什么來彌補我的不足,于是有了接下來要跟大家分享的“盯盤小工具”。
準備開干
那么接下來我的目標是開發一款PC端的桌面盯盤小工具,特點首先就是小、方便整天盯著電腦屏幕的白領們打開瞧瞧,省去費事各種操作;然后就是無需關注太多費腦筋的指標,所以能夠顯示名稱和漲跌幅即可。有的上面的需求后,那就可以開始搬磚了,但是對于我這種只懂點Android皮毛又沒做過桌面應用的人來說,簡直是比登天還難,那該咋辦?在夜深人靜時,我恍然想起了Flutter,沒錯先來一張圖:
還記得當時看Flutter的時候還是1.2版本,如今回過頭來看,已經不是曾經那個Flutter了。
搬磚
為了實現這個小小的業余需求并且又能學習Flutter,于是我白天下班回來又開始不同場景不同程序語言的搬磚,重新安裝了Flutter的最新開發環境,重新學習Flutter開發-萬物皆widget。
功夫不負有心人
效果圖展示
當前為最初版本,很多功能還不夠完善,后續目標就是完善及優化,
GitHub項目地址:
致謝
為了實現這個小小的業余需求并且又能學習Flutter,我也參考了很多Flutter大佬的開源項目,在此感謝所有優秀的開源項目 _ 。
文/陳爐軍
整理/LiveVideoStack
大家好,我是阿里巴巴閑魚事業部的陳爐軍,本次分享的主題是Flutter浪潮下的音視頻研發探索,主要內容是針對閑魚APP在當下流行的跨平臺框架Flutter的大規模實踐,介紹其在音視頻領域碰到的一些困難以及解決方案。
分享內容主要分為四個方面,首先會對Flutter有一個簡單介紹以及選擇Flutter作為跨平臺框架的原因,其次會介紹Flutter中與音視頻關系非常大的外接紋理概念,以及對它做出的一些優化。之后會對閑魚在音視頻實踐過程中碰到的一些Flutter問題提出了一些解決方案——TPM音視頻框架。最后是閑魚Flutter多媒體開源組件的介紹。
Flutter
Flutter是一個跨平臺框架,以往的做法是將音頻、視頻和網絡這些模塊都下沉到C++層或者ARM層,在其上封裝成一個音視頻的SDK,供UI層的PC、iOS和Android調用。
而Flutter做為一個UI層的跨平臺框架,顧名思義就是在UI層也實現了一個跨平臺開發。可以預想的是未Flutter發展的好的話,會逐漸變為一個從底層到UI層的一個全鏈路的跨平臺開發,技術人員分別負責SDK和UI層的開發。
在Flutter之前已經有很多跨平臺UI解決方案,那為什么選擇Flutter呢?
我們主要考慮性能和跨平臺的能力。
以往的跨平臺方案比如Weex,ReactNative,Cordova等等因為架構的原因無法滿足性能要求,尤其是在音視頻這種性能要求幾乎苛刻的場景。
而諸如Xamarin等,雖然性能可以和原生App一致,但是大部分邏輯還是需要分平臺實現。
我們可以看一下,為什么Flutter可以實現高性能:
原生的native組件渲染以IOS為例,蘋果的UIKit通過調用平臺自己的繪制框架QuaztCore來實現UI的繪制,圖形繪制也是調用底層的API,比如OpenGL、Metal等。
而Flutter也是和原生API邏輯一致,也是通過調用底層的繪制框架層SKIA實現UI層。這樣相當于Flutter他自己實現了一套UI框架,提供了一種性能超越原生API的跨平臺可能性。
但是我們說一個框架最終性能怎樣,其實取決于設計者和開發者。至于現在到底是一個什么狀況:
在閑魚的實踐中,我們發現在正常的開發沒有特意的去優化UI代碼的情況下,在一些低端機上,Flutter界面的流暢性是比Native界面要好的。
雖然現在閑魚某些場景下會有卡頓閃退等情況,但是這是一個新事物發展過程中的必然問題,我們相信未來性能肯定不會成為限制Flutter發展的瓶頸的。
在閑魚實踐Flutter的過程中,混合棧和音視頻是其中比較難解決的兩個問題,混合棧是指一個APP在Flutter過程中不可能一口氣將所有業務全部重寫為Flutter,所以這是一個逐步迭代的過程,這期間原生native界面與Flutter界面共存的狀態就稱之為混合棧。閑魚在混合棧上也有一些比較好的輸出,例如FlutterBoost。
外接紋理
在講音視頻之前需要簡要介紹一下外接紋理的概念,我們將它稱之為是Flutter和Frame之間的橋梁。
Flutter渲染一幀屏幕數據首先要做的是,GPU發出的VC信號在Flutter的UI線程,通過AOT編譯的機器碼結合當前Dart Runtime,生成Layer Tree UI樹,Layer Tree上每一個葉子節點都代表了當前屏幕上所需要渲染的每一個元素,包含了這些元素渲染所需要的內容。將Layer Tree拋給GPU線程,在GPU線程內調用Skia去完成整個UI的渲染過程。Layer Tree中有PictureLayer和TextureLayer兩個比較重要的節點。PictureLayer主要負責屏幕圖片的渲染,Flutter內部實現了一套圖片解碼邏輯,在IO線程將圖片讀取或者從網絡上拉取之后,通過解碼能夠在IO線程上加載出紋理,交給GPU線程將圖片渲染到屏幕上。但是由于音視頻場景下系統API太過繁多,業務場景過于復雜。Flutter沒有一套邏輯去實現跨平臺的音視頻組件,所以說Flutter提出了一種讓第三方開發者來實現音視頻組件的方式,而這些音視頻組件的視頻渲染出口,就是TextureLayer。
在整個Layer Tree渲染的過程中,TextureLayer的數據紋理需要由外部第三方開發者來指定,可以把視頻數據和播放器數據送到TextureLayer里,由Flutter將這些數據渲染出來。
TextureLayer渲染過程:首先判斷Layer是否已經初始化,如果沒有就創建一個Texture,然后將Texture Attach到一個SufaceTexture上。
這個SufaceTexture是音視頻的native代碼可以獲取到的對象,通過這個對象創建的Suface,我們可以將視頻數據、攝像頭數據解碼放到Suface中,然后Flutter端通過監聽SufaceTexture的數據更新就可以順利把剛才創建的數據更新到它的紋理中,然后再將紋理交給SKIA渲染到屏幕上。
然而我們如果需要用Flutter實現美顏,濾鏡,人臉貼圖等等功能,就需要將視頻數據讀取出來,更新到紋理中,再將GPU紋理經過美顏濾鏡處理后生成一個處理后的紋理。按Flutter提供的現有能力,必須先將紋理中的數據從GPU讀出到CPU中,生成Bitmap后再寫入Surface中,這樣在Flutter中才能順利的更新到視頻數據,這樣做對系統性能的消耗很大。
通過對Flutter渲染過程分析,我們知道Flutter底層需要渲染的數據就是GPU紋理,而我們經過美顏濾鏡處理完成以后的結果也是GPU紋理,如果可以將它直接交給Flutter渲染,那就可以避免GPU-CPU-GPU這樣的無用循環。這樣的方法是可行的,但是需要一個條件,就是OpenGL上下文共享。
OpenGL
在說上下文之前,得提到一個和上線文息息相關的概念:線程。
Flutter引擎啟動后會啟動四個線程:
第一個線程是UI線程,這是Flutter自己定義的UI線程,主要負責GPU發出的VSync信號時候用當前Dart編譯的機器碼和當前運行環境創建出Layer Tree。
還有就是IO線程和GPU線程。和大部分OpenGL處理解決方案中一樣,Flutter也采取一個線程責資源加載,一部分負責資源渲染這種思路。
兩個線程之間紋理共享有兩種方式。一種是EGLImage(IOS是 CVOpenGLESTextureCache)。一種是OpenGL Share Context。Flutter通過Share Context來實現紋理共享,將IO線程的Context和GPU線程的Context進行Share,放到同一個Share Group下面,這樣兩個線程下資源是互相可見可以共享的。
Platform線程是主線程,Flutter中有一個很奇怪的設定,GPU線程和主線程共用一個Context。并且在主線程也有很多OpenGL 操作。
這樣的設計會給音視頻開發帶來很多問題,后面會詳細說。
音視頻端美顏處理完成的OpenGL紋理能夠讓Flutter直接使用的條件就是Flutter的上下文需要和平臺音視頻相關的OpenGL上下文處在一個Share Group下面。
由于Flutter主線程的Context就是GPU的Context,所以在音視頻端主線程中有一些OpenGL操作的話,很有可能使Flutter整個OpenGL被破壞掉。所以需要將所有的OpenGL操作都限制在子線程中。
通過上述這兩個條件的處理,我們就可以在沒有增加GPU消耗的前提下實現美顏和濾鏡等等功能。
TPM
在經過demo驗證之后,我們將這個方案應用到閑魚音視頻組件中,但改造過程中發現了一些問題。
上圖是攝像頭采集數據轉換為紋理的一段代碼,其中有兩個操作:首先是切進程,將后面的OpenGL操作都切到cameraQueue中。然后是設置一次上下文。然后這種限制條件或者說是潛規則往往在開發過程中容易被忽略的。而這個條件一旦忽略后果就是出現一些莫名其妙的詭異問題極難排查。因此我們就希望能抽象出一套框架,由框架本身實現線程的切換、上下文和模塊生命周期等的管理,開發者接入框架以后只需要安心實現自己的算法,而不需要關心這些潛規則還有其他一些重復的邏輯操作。
在引入Flutter之前閑魚的音視頻架構與大部分音視頻邏輯一樣采用分層架構:
1:底層是一些獨立模塊
2:SDK層是對底層模塊的封裝
3:最上層是UI層。
引入Flutter之后,通過分析各個模塊的使用場景,我們可以得出一個假設或者說是抽象:音視頻應用在終端上可以歸納為視頻幀解碼之后視頻數據幀在各個模塊之間流動的過程,基于這種假設去做Flutter音視頻框架的抽象。
咸魚Flutter多媒體開源組件
整個Flutter音視頻框架抽象分為管線和數據的抽象、模塊的抽象、線程統一管理和上下文同一管理四部分。
管線,其實就是視頻幀流動的管道。數據,音視頻中涉及到的數據包括紋理、Bit Map以及時間戳等。結合現有的應用場景我們定義了管線流通數據以Texture為主數據,同時可以選擇性的添加Bit Map等作為輔助數據。這樣的數據定義方式,避免重復的創建和銷毀紋理帶來的性能開銷以及多線程訪問紋理帶來的一些問題。也滿足一些特殊模塊對特殊數據的需求。同時也設計了紋理池來管理管線中的紋理數據。
模塊:如果把管線和數據比喻成血管和血液,那框架音視頻的場景就可以比喻成器官,我們根據模塊所在管線的位置抽象出采集、處理和輸出三個基類。這三個基類里實現了剛才說的線程切換,上下文切換,格式轉換等等共同邏輯,各個功能模塊通過集成自這些基類,可以避免很多重復勞動。
線程:每一個模塊初始化的時候,初始化函數就會去線程管理的模塊去獲取自己的線程,線程管理模塊可以決定給初始化函數分配新的線程或者已經分配過其他模塊的線程。
這樣有三個好處:
一是可以根據需要去決定一個線程可以掛載多少模塊,做到線程間的負載均衡。第二,多線程并發式能夠保證模塊內的OpenGL操作是在當前線程內而不會跑到主線程去,徹底避免Flutter的OpenGL 環境被破壞。第三,多線程并行可以充分利用CPU多核架構,提升處理速度。
從Flutter端修改Flutter引擎將Context取出后,根據Context創建上下文的統一管理模塊,每一個模塊在初始化的時候會獲取它的線程,獲取之后會調用上下文管理模塊獲取自己的上下文。這樣可以保證每一個模塊的上下文都是與Flutter的上下文進行Share的,每個模塊之間資源都是共享可見的,Flutter和音視頻native之間也是互相共享可見的。
基于上述框架如果要實現一個簡單的場景,比如畫面實時預覽和濾鏡處理功能,
1:需要選擇功能模塊,功能模塊包括攝像頭模塊、濾鏡處理模塊和Flutter畫面渲染模塊,
2:需要配置模塊參數,比如采集分辨率、濾鏡參數和前后攝像頭設置等,
3:在創建視頻管線后使用已配置的參數創建模塊
4:最后管線搭載模塊,開啟管線就可以實現這樣簡單的功能。
上圖為整個功能實現的代碼和結構圖。
結合上述音視頻框架,閑魚實現了Flutter多媒體開源組件。
組要包含四個基本組件分別是:
1:視頻圖像拍攝組件
2:播放器組件
3:視頻圖像編輯組件
4:相冊選擇組件
現在這些組件正在走內部開源流程。預計9月份,相冊和播放器會實現開源。
后續展望和規劃
1:實現開頭所說的從底層SDK到UI的全鏈路的跨端開發。目前底層框架層和模塊層都是各個平臺各自實現,反而是Flutter的UI端進行了跨平臺的統一,所以后續會將底層也按照音視頻常用做法把邏輯下沉到C++層,盡可能的實現全鏈路跨平臺。
2:第二部分內容為開源共建,閑魚開源的內容不僅包括拍攝、編輯組件,還包括了很多底層模塊,希望有開發者在基于Flutter開發音視頻應用時可以充分利用閑魚開源出的音視頻模塊能力,搭建APP框架,開發者只要去負責實現特殊需求模塊就可以,盡可能的減少重復勞動。
在前端項目開發過程中,現在很少有人會使用原生的CSS來搭建頁面,總歸都會引入一些前端UI框架以減少代碼的書寫。一般為了方便自己的使用,很多大公司都有自己的一套UI框架,同時也會把其開源出來。下面就是最近經常使用并且很流行的一些前端UI框架,總有一款適合你:
Mint UI
Mint UI
Mint UI是餓了么團隊開發的基于Vue .js的移動端UI框架,它包含豐富的 CSS 和 JS 組件,能夠滿足日常的移動端開發需要。
WeUI
WeUI是一套同微信原生視覺體驗一致的基礎樣式庫,由微信官方設計團隊為微信內網頁和微信小程序量身設計,令用戶的使用感知更加統一。包含button、cell、dialog、toast、article、icon等各式元素。
Cube-ui
Cube-ui
Cube-ui 是滴滴團隊開發的基于 Vue.js 實現的精致移動端組件庫。支持按需引入和后編譯,輕量靈活;擴展性強,可以方便地基于現有組件實現二次開發。
iView UI
iView UI
iView UI是一個強大的UI庫,基于vue,有很多實用的基礎組件比elementui的組件更豐富,主要服務于 PC 界面的中后臺產品。使用單文件的 Vue 組件化開發模式 基于 npm + webpack + babel 開發,支持 ES2015 高質量、功能豐富 友好的 API ,自由靈活地使用空間。
LayUI
LayUI
LayUI是一款采用自身模塊規范編寫的前端 UI 框架,遵循原生 HTML/CSS/JS 的書寫與組織形式,門檻極低,拿來即用。其外在極簡,卻又不失飽滿的內在,體積輕盈,組件豐盈,從核心代碼到 API 的每一處細節都經過精心雕琢,非常適合界面的快速開發。
ElementUI
ElementUI
Element是餓了么前端開源維護的Vue UI組件庫,組件齊全,基本涵蓋后臺所需的所有組件,文檔講解詳細,例子也很豐富。 主要用于開發PC端的頁面,是一個質量比較高的Vue UI組件庫。
at-ui
at-ui
at-ui 是一款阿里團隊創建的基于 Vue 2.x 的前端 UI 組件庫,主要用于快速開發 PC 網站產品。 它提供了一套 npm + webpack + babel 前端開發工作流程,CSS 樣式獨立,即使采用不同的框架實現都能保持統一的 UI 風格。
amaze UI
amaze UI
Amaze UI 是一個移動優先的跨屏前端框架。提供基礎樣式,網格,表格、表單、按鈕及常用組件樣式。是一個輕量級(所有 CSS 和 JS gzip 后 100 kB 左右)、?Mobile first?的前端框架
Vant UI
Vant UI
Vant UI是有贊前端團隊基于有贊統一的規范實現的 Vue 組件庫,提供了一整套 UI 基礎組件和業務組件。通過 Vant,可以快速搭建出風格統一的頁面,提升開發效率。
Flutter
Flutter
Flutter 是谷歌的移動端 UI 框架,可在極短的時間內構建 Android 和 iOS 上高質量的原生級應用。 Flutter 可與現有代碼一起工作, 它被世界各地的開發者和組織使用, 并且 Flutter 是免費和開源的.
ionic
Ionic既是一個CSS框架也是一個Javascript UI庫,Ionic 是目前最有潛力的一款 HTML5 手機應用開發框架。通過 SASS 構建應用程序,它 提供了很多 UI 組件來幫助開發者開發強大的應用。 它使用 JavaScript MVVM 框架和 AngularJS 來增強應用。提供數據的雙向綁定,使用它成為 Web 和移動開發者的共同選擇。
Dart的 IO 庫包含了文件讀寫的相關類,它屬于 Dart 語法標準的一部分,所以通過 Dart IO 庫,無論是 Dart VM 下的腳本還是 Flutter,都是通過 Dart IO 庫來操作文件的,不過和 Dart VM 相比,Flutter 有一個重要差異是文件系統路徑不同,這是因為Dart VM 是運行在 PC 或服務器操作系統下,而 Flutter 是運行在移動操作系統中,他們的文件系統會有一些差異。
Android 和 iOS 的應用存儲目錄不同, PathProvider 插件提供了一種平臺透明的方式來訪問設備文件系統上的常用位置。該類當前支持訪問兩個文件系統位置:
File代表一個整體的文件,他有三個構造函數,分別是:
文件讀取本身有兩種形式,一種是文本,一種是二進制。
2.2.1 讀取文本內容
如果是文本文件,File提供了readAsString、readAsLines、readAsStringSync、readAsLinesSync方法,讀取文本內容
readAsString 一次性讀取所有文本
readAsLines 一行行的讀取文本
結果返回的是一個List,list中表示文件每行的內容
readAsStringSync、readAsLinesSync同步讀取文本
2.2.2 讀取二進制內容
如果文件是二進制,那么可以使用readAsBytes或者同步的方法readAsBytesSync:
dart中表示二進制有一個專門的類型叫做Uint8List,他實際上表示的是一個int的List。
上面提到的讀取方式,都是一次性讀取整個文件,缺點就是如果文件太大的話,可能造成內存空間的壓力。
所以File為我們提供了另外一種讀取文件的方法,流的形式來讀取文件.
示例
dart提供了open和openSync兩個方法來進行隨機文件讀寫:
寫入和文件讀取一樣,可以一次性寫入或者獲得一個寫入句柄,然后再寫入。
一次性寫入的方法有四種,分別對應字符串和二進制
句柄形式可以調用openWrite方法,返回一個IOSink對象,然后通過這個對象進行寫入:
默認情況下寫入是會覆蓋整個文件的,但是可以通過下面的方式來更改寫入模式:
雖然dart中所有的異常都是運行時異常,但是和java一樣,要想手動處理文件讀寫中的異常,則可以使用try,catch:
我們還是以計數器為例,實現在應用退出重啟后可以恢復點擊次數。 這里,我們使用文件來保存數據:
1.引入PathProvider插件;在pubspec.yaml文件中添加如下聲明:
執行 flutter pub get
2.實現如下
參考:
當前文章:flutterpc的簡單介紹
文章轉載:http://vcdvsql.cn/article22/dsdshjc.html
成都網站建設公司_創新互聯,為您提供網站營銷、品牌網站建設、App開發、自適應網站、網站內鏈、全網營銷推廣
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯