相對(duì)于iOS開(kāi)發(fā),F(xiàn)lutter的布局更具有靈活性,每個(gè)頁(yè)面設(shè)計(jì)都不一樣,相同頁(yè)面可選擇的布局方式也不一樣,如果單純的說(shuō)應(yīng)該如何去布局,我覺(jué)得不現(xiàn)實(shí),大家可以參考下 Flutter官方的布局教程 。接下來(lái),筆者,通過(guò)項(xiàng)目中的一個(gè)頁(yè)面,來(lái)一步一步的拆解布局的流程。整個(gè)過(guò)程,基本上按照拆解、組件封裝、具體布局這三步來(lái)的。
10年積累的做網(wǎng)站、成都網(wǎng)站建設(shè)經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先做網(wǎng)站后付款的網(wǎng)站建設(shè)流程,更有梁平免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
根據(jù)設(shè)計(jì)圖,可以看出整體可以分成兩部分,上面一部分是系統(tǒng)介紹模塊,下面一部分是真正的登錄內(nèi)容,因?yàn)樯婕暗蒋B加,因此考慮用Stack;
系統(tǒng)介紹模塊部分:整體也是涉及到疊加,考慮用Stack,分為四部分。最底部漸變色背景用一個(gè)contanier,無(wú)須指定位置,全視圖擴(kuò)展;載放logo圖標(biāo)在上一層,用Image。最后兩個(gè)Text同級(jí)放在最上層。Image,Text各用Positioned包裹去指定位置。
登錄內(nèi)容模塊是最外層是一個(gè)Contanier容器,去控制背景色和圓角。然后是一個(gè)Column元素,逐行排列。
第一行為Image,
第二行為Text,
第三行可以看成一個(gè)小Column,分兩塊進(jìn)行布局
第四行可以看成一個(gè)小Column,分兩塊進(jìn)行布局
第五行可以看作一個(gè)TextButton,
第六行可以看作一個(gè)Row,分三塊進(jìn)行布局
通過(guò)上面這樣一步一步的分析后,基本上對(duì)大致的布局有了一個(gè)了解,最外層的控件大致選對(duì)(只要能實(shí)現(xiàn)的話,就是復(fù)雜度以及效率的問(wèn)題),然后一步一步的拆解每一行的元素,如果有重復(fù)的或者覺(jué)得可以封裝出來(lái)的部分,則進(jìn)行下一步。
每一行的拆解,大致也是按照這個(gè)思路來(lái)進(jìn)行,因此筆者在這里就不做講解了。
在做到第三第四行的時(shí)候,發(fā)現(xiàn)這兩個(gè)很相似,而且設(shè)計(jì)到一些交互邏輯,筆者就想對(duì)第三第四行的這種展示進(jìn)行封裝,覺(jué)得今后的布局可能會(huì)用到,因此在這一步,可以先把這一塊兒抽離出一個(gè)控件。利用TextField來(lái)實(shí)現(xiàn)這種輸入操作,具體的實(shí)現(xiàn)筆者不再詳細(xì)的描述了。
經(jīng)過(guò)這一步,整體的規(guī)劃設(shè)計(jì)圖已經(jīng)有了,各個(gè)組件也都有了,接下來(lái)的工作就是組裝了。
具體布局設(shè)計(jì)到一些細(xì)節(jié)的地方,例如整體Column的居中對(duì)齊(crossAxisAlignment)、間隔(Padding或Container包裹,筆者更喜歡用SizedBox占位)、居左居右居中(Align)、點(diǎn)擊事件(GestureDetector)以及圓角(BorderRadius)等一些特殊情況。
像第六行row是放在底部的,就可以在第六行前面增加一個(gè)Spacer()去填充空白區(qū)域。
對(duì)文字顏色大小等,可以用TextStyle直接設(shè)置。
對(duì)于輸入框的刪除按鈕,可以用Offstage這種Flutter特有的控制顯示隱藏的控件。
hello world例子
在終端打印字符串‘Hello World!’
計(jì)算斐波那契數(shù)列
一個(gè)簡(jiǎn)單的類
計(jì)算兩點(diǎn)距離
異步并發(fā)示例
使用了Isolate
1.面向?qū)ο?/p>
對(duì)于面向?qū)ο筮@個(gè)概念,相信了解Java的同學(xué)對(duì)這個(gè)概念一定不會(huì)陌生。
例如:我們有個(gè)Person Object他有很多特征和行為。
這些都是這個(gè)Person Object的屬性。
也就是因?yàn)橛辛诉@些特征,行為等等才決定了這個(gè)人是誰(shuí)。
那么回到Dart當(dāng)中,所有的都是對(duì)象,那么在就可以可以跟進(jìn)對(duì)象的屬性的特征,方法等等來(lái)進(jìn)行編程。
之所以我們?cè)谶@里又特別強(qiáng)調(diào)了一下面向?qū)ο蟮母拍?,是因?yàn)檫@個(gè)概念在Dart語(yǔ)言當(dāng)中,至關(guān)重要!
2.最重要的幾個(gè)概念
3.Dart的部分特性
4.Dart的內(nèi)置庫(kù)
包名
描述
dart:asynv
異步編程,提供Future,Stream類
dart:collection
集合
dart:convert
不同類型的字符編碼解碼
dart:core
Dart語(yǔ)言核心功能,內(nèi)置類型
dart:html
網(wǎng)頁(yè)開(kāi)發(fā)用到的庫(kù)
dart:io
文件讀寫,IO相關(guān)
dart:math
數(shù)字常量以及函數(shù),隨機(jī)算法等
dart:svg
事件和動(dòng)畫矢量圖支持
綜上述所說(shuō)要想學(xué)Flutter,先學(xué)Dart!關(guān)于Flutter下篇我會(huì)講到。為什么離不開(kāi)dart!
有關(guān)更多面經(jīng)、核心技術(shù)筆記;自己也是從事Android開(kāi)發(fā)5年有余了;整理了一些Android開(kāi)發(fā)技術(shù)核心筆記和面經(jīng)題綱, 如有需要的同學(xué)請(qǐng)私信我回復(fù)“核心筆記”或“面試”領(lǐng)??!
目前Flutter平臺(tái)主流的兩個(gè)播放器是video_player和fijkplayer
pub
github
1、Flutter平臺(tái)官方插件,作者是國(guó)外的,有問(wèn)題溝通比較困難,只能通過(guò)提交issue
2、硬解碼
4、UI封裝: better_player
基于video_player和Chewie的高級(jí)視頻播放器。它解決了許多典型的用例,并且易于運(yùn)行。
5、播放器寬高比例與視頻內(nèi)容寬高比例不一致時(shí),會(huì)出現(xiàn)圖像壓縮變形的問(wèn)題
6、調(diào)用原生內(nèi)核播放器:iOS--AVPlayer, Android--ExoPlayer
7、對(duì)于分段源 m3u8 的播放不友好,如果一個(gè)切片播放超時(shí),會(huì)導(dǎo)致整個(gè)播放都失敗
8、better_player可以緩存視頻,但不能自定義緩存的地址,只能指定key,和緩存的最大內(nèi)存量(還未研究超出最大的話是不能緩存新的,還是刪除最舊的)
9、better_player不能完全自定義UI,只能修改類中的一些開(kāi)放屬性,比如說(shuō)icon圖標(biāo),文字顏色啥的
10、無(wú)網(wǎng)絡(luò)有緩存時(shí),封面可以正常展示
11、better_player播放失敗有手動(dòng)retry的設(shè)計(jì)
pub
github
1、fijkplayer 是一個(gè) Flutter 生態(tài)的媒體播放器,是對(duì) ijkplayer 的 Flutter 封裝,支持 Android 和 iOS。 fijkplayer 使用 ijkplayer 作為播放器內(nèi)核,ijkplayer 使用 ffmpeg 進(jìn)行音視頻解封裝和解碼,同時(shí)添加了 Android 和 iOS 平臺(tái)特有的硬件加速解碼能力。
2 、國(guó)內(nèi)有QQ群,但是活躍度也是不高。
3、可以緩存視頻,可以自定義緩存的地址,方便后續(xù)的內(nèi)存維護(hù)。
4、可以通過(guò)FijkPanelWidgetBuilder較大程度上自定義UI。
5、無(wú)網(wǎng)絡(luò)有緩存視頻時(shí),無(wú)法展示封面,因?yàn)閮?nèi)部是通過(guò)imageProvider去加載網(wǎng)絡(luò)圖片的。
7、播放失敗無(wú)手動(dòng)retry的設(shè)計(jì)
1、兩種播放器都是通過(guò)外接紋理方案 (Texture),將播放器視頻畫面渲染接入 flutter 中,性能上優(yōu)于 PlatformView 的接入方法。
如何自己實(shí)現(xiàn)?
下面以video_palyer的iOS源碼部分解釋:
iOS用CVPixelBufferRef將渲染出來(lái)的數(shù)據(jù)存在內(nèi)存中,F(xiàn)lutter engine會(huì)將Texture的數(shù)據(jù)在內(nèi)存中直接進(jìn)行映射無(wú)需通過(guò)Channel傳輸,然后Texture Widget就可以把你提供的這些數(shù)據(jù)顯示出來(lái)。在我們傳輸數(shù)據(jù)的時(shí)候會(huì)需要將其與 TextureID 綁定,綁定的過(guò)程通過(guò)BasicMessageChannel實(shí)現(xiàn)數(shù)據(jù)流的傳輸,以做到實(shí)時(shí)展示的效果
文/陳爐軍
整理/LiveVideoStack
大家好,我是阿里巴巴閑魚事業(yè)部的陳爐軍,本次分享的主題是Flutter浪潮下的音視頻研發(fā)探索,主要內(nèi)容是針對(duì)閑魚APP在當(dāng)下流行的跨平臺(tái)框架Flutter的大規(guī)模實(shí)踐,介紹其在音視頻領(lǐng)域碰到的一些困難以及解決方案。
分享內(nèi)容主要分為四個(gè)方面,首先會(huì)對(duì)Flutter有一個(gè)簡(jiǎn)單介紹以及選擇Flutter作為跨平臺(tái)框架的原因,其次會(huì)介紹Flutter中與音視頻關(guān)系非常大的外接紋理概念,以及對(duì)它做出的一些優(yōu)化。之后會(huì)對(duì)閑魚在音視頻實(shí)踐過(guò)程中碰到的一些Flutter問(wèn)題提出了一些解決方案——TPM音視頻框架。最后是閑魚Flutter多媒體開(kāi)源組件的介紹。
Flutter
Flutter是一個(gè)跨平臺(tái)框架,以往的做法是將音頻、視頻和網(wǎng)絡(luò)這些模塊都下沉到C++層或者ARM層,在其上封裝成一個(gè)音視頻的SDK,供UI層的PC、iOS和Android調(diào)用。
而Flutter做為一個(gè)UI層的跨平臺(tái)框架,顧名思義就是在UI層也實(shí)現(xiàn)了一個(gè)跨平臺(tái)開(kāi)發(fā)。可以預(yù)想的是未Flutter發(fā)展的好的話,會(huì)逐漸變?yōu)橐粋€(gè)從底層到UI層的一個(gè)全鏈路的跨平臺(tái)開(kāi)發(fā),技術(shù)人員分別負(fù)責(zé)SDK和UI層的開(kāi)發(fā)。
在Flutter之前已經(jīng)有很多跨平臺(tái)UI解決方案,那為什么選擇Flutter呢?
我們主要考慮性能和跨平臺(tái)的能力。
以往的跨平臺(tái)方案比如Weex,ReactNative,Cordova等等因?yàn)榧軜?gòu)的原因無(wú)法滿足性能要求,尤其是在音視頻這種性能要求幾乎苛刻的場(chǎng)景。
而諸如Xamarin等,雖然性能可以和原生App一致,但是大部分邏輯還是需要分平臺(tái)實(shí)現(xiàn)。
我們可以看一下,為什么Flutter可以實(shí)現(xiàn)高性能:
原生的native組件渲染以IOS為例,蘋果的UIKit通過(guò)調(diào)用平臺(tái)自己的繪制框架QuaztCore來(lái)實(shí)現(xiàn)UI的繪制,圖形繪制也是調(diào)用底層的API,比如OpenGL、Metal等。
而Flutter也是和原生API邏輯一致,也是通過(guò)調(diào)用底層的繪制框架層SKIA實(shí)現(xiàn)UI層。這樣相當(dāng)于Flutter他自己實(shí)現(xiàn)了一套UI框架,提供了一種性能超越原生API的跨平臺(tái)可能性。
但是我們說(shuō)一個(gè)框架最終性能怎樣,其實(shí)取決于設(shè)計(jì)者和開(kāi)發(fā)者。至于現(xiàn)在到底是一個(gè)什么狀況:
在閑魚的實(shí)踐中,我們發(fā)現(xiàn)在正常的開(kāi)發(fā)沒(méi)有特意的去優(yōu)化UI代碼的情況下,在一些低端機(jī)上,F(xiàn)lutter界面的流暢性是比Native界面要好的。
雖然現(xiàn)在閑魚某些場(chǎng)景下會(huì)有卡頓閃退等情況,但是這是一個(gè)新事物發(fā)展過(guò)程中的必然問(wèn)題,我們相信未來(lái)性能肯定不會(huì)成為限制Flutter發(fā)展的瓶頸的。
在閑魚實(shí)踐Flutter的過(guò)程中,混合棧和音視頻是其中比較難解決的兩個(gè)問(wèn)題,混合棧是指一個(gè)APP在Flutter過(guò)程中不可能一口氣將所有業(yè)務(wù)全部重寫為Flutter,所以這是一個(gè)逐步迭代的過(guò)程,這期間原生native界面與Flutter界面共存的狀態(tài)就稱之為混合棧。閑魚在混合棧上也有一些比較好的輸出,例如FlutterBoost。
外接紋理
在講音視頻之前需要簡(jiǎn)要介紹一下外接紋理的概念,我們將它稱之為是Flutter和Frame之間的橋梁。
Flutter渲染一幀屏幕數(shù)據(jù)首先要做的是,GPU發(fā)出的VC信號(hào)在Flutter的UI線程,通過(guò)AOT編譯的機(jī)器碼結(jié)合當(dāng)前Dart Runtime,生成Layer Tree UI樹(shù),Layer Tree上每一個(gè)葉子節(jié)點(diǎn)都代表了當(dāng)前屏幕上所需要渲染的每一個(gè)元素,包含了這些元素渲染所需要的內(nèi)容。將Layer Tree拋給GPU線程,在GPU線程內(nèi)調(diào)用Skia去完成整個(gè)UI的渲染過(guò)程。Layer Tree中有PictureLayer和TextureLayer兩個(gè)比較重要的節(jié)點(diǎn)。PictureLayer主要負(fù)責(zé)屏幕圖片的渲染,F(xiàn)lutter內(nèi)部實(shí)現(xiàn)了一套圖片解碼邏輯,在IO線程將圖片讀取或者從網(wǎng)絡(luò)上拉取之后,通過(guò)解碼能夠在IO線程上加載出紋理,交給GPU線程將圖片渲染到屏幕上。但是由于音視頻場(chǎng)景下系統(tǒng)API太過(guò)繁多,業(yè)務(wù)場(chǎng)景過(guò)于復(fù)雜。Flutter沒(méi)有一套邏輯去實(shí)現(xiàn)跨平臺(tái)的音視頻組件,所以說(shuō)Flutter提出了一種讓第三方開(kāi)發(fā)者來(lái)實(shí)現(xiàn)音視頻組件的方式,而這些音視頻組件的視頻渲染出口,就是TextureLayer。
在整個(gè)Layer Tree渲染的過(guò)程中,TextureLayer的數(shù)據(jù)紋理需要由外部第三方開(kāi)發(fā)者來(lái)指定,可以把視頻數(shù)據(jù)和播放器數(shù)據(jù)送到TextureLayer里,由Flutter將這些數(shù)據(jù)渲染出來(lái)。
TextureLayer渲染過(guò)程:首先判斷Layer是否已經(jīng)初始化,如果沒(méi)有就創(chuàng)建一個(gè)Texture,然后將Texture Attach到一個(gè)SufaceTexture上。
這個(gè)SufaceTexture是音視頻的native代碼可以獲取到的對(duì)象,通過(guò)這個(gè)對(duì)象創(chuàng)建的Suface,我們可以將視頻數(shù)據(jù)、攝像頭數(shù)據(jù)解碼放到Suface中,然后Flutter端通過(guò)監(jiān)聽(tīng)SufaceTexture的數(shù)據(jù)更新就可以順利把剛才創(chuàng)建的數(shù)據(jù)更新到它的紋理中,然后再將紋理交給SKIA渲染到屏幕上。
然而我們?nèi)绻枰肍lutter實(shí)現(xiàn)美顏,濾鏡,人臉貼圖等等功能,就需要將視頻數(shù)據(jù)讀取出來(lái),更新到紋理中,再將GPU紋理經(jīng)過(guò)美顏濾鏡處理后生成一個(gè)處理后的紋理。按Flutter提供的現(xiàn)有能力,必須先將紋理中的數(shù)據(jù)從GPU讀出到CPU中,生成Bitmap后再寫入Surface中,這樣在Flutter中才能順利的更新到視頻數(shù)據(jù),這樣做對(duì)系統(tǒng)性能的消耗很大。
通過(guò)對(duì)Flutter渲染過(guò)程分析,我們知道Flutter底層需要渲染的數(shù)據(jù)就是GPU紋理,而我們經(jīng)過(guò)美顏濾鏡處理完成以后的結(jié)果也是GPU紋理,如果可以將它直接交給Flutter渲染,那就可以避免GPU-CPU-GPU這樣的無(wú)用循環(huán)。這樣的方法是可行的,但是需要一個(gè)條件,就是OpenGL上下文共享。
OpenGL
在說(shuō)上下文之前,得提到一個(gè)和上線文息息相關(guān)的概念:線程。
Flutter引擎啟動(dòng)后會(huì)啟動(dòng)四個(gè)線程:
第一個(gè)線程是UI線程,這是Flutter自己定義的UI線程,主要負(fù)責(zé)GPU發(fā)出的VSync信號(hào)時(shí)候用當(dāng)前Dart編譯的機(jī)器碼和當(dāng)前運(yùn)行環(huán)境創(chuàng)建出Layer Tree。
還有就是IO線程和GPU線程。和大部分OpenGL處理解決方案中一樣,F(xiàn)lutter也采取一個(gè)線程責(zé)資源加載,一部分負(fù)責(zé)資源渲染這種思路。
兩個(gè)線程之間紋理共享有兩種方式。一種是EGLImage(IOS是 CVOpenGLESTextureCache)。一種是OpenGL Share Context。Flutter通過(guò)Share Context來(lái)實(shí)現(xiàn)紋理共享,將IO線程的Context和GPU線程的Context進(jìn)行Share,放到同一個(gè)Share Group下面,這樣兩個(gè)線程下資源是互相可見(jiàn)可以共享的。
Platform線程是主線程,F(xiàn)lutter中有一個(gè)很奇怪的設(shè)定,GPU線程和主線程共用一個(gè)Context。并且在主線程也有很多OpenGL 操作。
這樣的設(shè)計(jì)會(huì)給音視頻開(kāi)發(fā)帶來(lái)很多問(wèn)題,后面會(huì)詳細(xì)說(shuō)。
音視頻端美顏處理完成的OpenGL紋理能夠讓Flutter直接使用的條件就是Flutter的上下文需要和平臺(tái)音視頻相關(guān)的OpenGL上下文處在一個(gè)Share Group下面。
由于Flutter主線程的Context就是GPU的Context,所以在音視頻端主線程中有一些OpenGL操作的話,很有可能使Flutter整個(gè)OpenGL被破壞掉。所以需要將所有的OpenGL操作都限制在子線程中。
通過(guò)上述這兩個(gè)條件的處理,我們就可以在沒(méi)有增加GPU消耗的前提下實(shí)現(xiàn)美顏和濾鏡等等功能。
TPM
在經(jīng)過(guò)demo驗(yàn)證之后,我們將這個(gè)方案應(yīng)用到閑魚音視頻組件中,但改造過(guò)程中發(fā)現(xiàn)了一些問(wèn)題。
上圖是攝像頭采集數(shù)據(jù)轉(zhuǎn)換為紋理的一段代碼,其中有兩個(gè)操作:首先是切進(jìn)程,將后面的OpenGL操作都切到cameraQueue中。然后是設(shè)置一次上下文。然后這種限制條件或者說(shuō)是潛規(guī)則往往在開(kāi)發(fā)過(guò)程中容易被忽略的。而這個(gè)條件一旦忽略后果就是出現(xiàn)一些莫名其妙的詭異問(wèn)題極難排查。因此我們就希望能抽象出一套框架,由框架本身實(shí)現(xiàn)線程的切換、上下文和模塊生命周期等的管理,開(kāi)發(fā)者接入框架以后只需要安心實(shí)現(xiàn)自己的算法,而不需要關(guān)心這些潛規(guī)則還有其他一些重復(fù)的邏輯操作。
在引入Flutter之前閑魚的音視頻架構(gòu)與大部分音視頻邏輯一樣采用分層架構(gòu):
1:底層是一些獨(dú)立模塊
2:SDK層是對(duì)底層模塊的封裝
3:最上層是UI層。
引入Flutter之后,通過(guò)分析各個(gè)模塊的使用場(chǎng)景,我們可以得出一個(gè)假設(shè)或者說(shuō)是抽象:音視頻應(yīng)用在終端上可以歸納為視頻幀解碼之后視頻數(shù)據(jù)幀在各個(gè)模塊之間流動(dòng)的過(guò)程,基于這種假設(shè)去做Flutter音視頻框架的抽象。
咸魚Flutter多媒體開(kāi)源組件
整個(gè)Flutter音視頻框架抽象分為管線和數(shù)據(jù)的抽象、模塊的抽象、線程統(tǒng)一管理和上下文同一管理四部分。
管線,其實(shí)就是視頻幀流動(dòng)的管道。數(shù)據(jù),音視頻中涉及到的數(shù)據(jù)包括紋理、Bit Map以及時(shí)間戳等。結(jié)合現(xiàn)有的應(yīng)用場(chǎng)景我們定義了管線流通數(shù)據(jù)以Texture為主數(shù)據(jù),同時(shí)可以選擇性的添加Bit Map等作為輔助數(shù)據(jù)。這樣的數(shù)據(jù)定義方式,避免重復(fù)的創(chuàng)建和銷毀紋理帶來(lái)的性能開(kāi)銷以及多線程訪問(wèn)紋理帶來(lái)的一些問(wèn)題。也滿足一些特殊模塊對(duì)特殊數(shù)據(jù)的需求。同時(shí)也設(shè)計(jì)了紋理池來(lái)管理管線中的紋理數(shù)據(jù)。
模塊:如果把管線和數(shù)據(jù)比喻成血管和血液,那框架音視頻的場(chǎng)景就可以比喻成器官,我們根據(jù)模塊所在管線的位置抽象出采集、處理和輸出三個(gè)基類。這三個(gè)基類里實(shí)現(xiàn)了剛才說(shuō)的線程切換,上下文切換,格式轉(zhuǎn)換等等共同邏輯,各個(gè)功能模塊通過(guò)集成自這些基類,可以避免很多重復(fù)勞動(dòng)。
線程:每一個(gè)模塊初始化的時(shí)候,初始化函數(shù)就會(huì)去線程管理的模塊去獲取自己的線程,線程管理模塊可以決定給初始化函數(shù)分配新的線程或者已經(jīng)分配過(guò)其他模塊的線程。
這樣有三個(gè)好處:
一是可以根據(jù)需要去決定一個(gè)線程可以掛載多少模塊,做到線程間的負(fù)載均衡。第二,多線程并發(fā)式能夠保證模塊內(nèi)的OpenGL操作是在當(dāng)前線程內(nèi)而不會(huì)跑到主線程去,徹底避免Flutter的OpenGL 環(huán)境被破壞。第三,多線程并行可以充分利用CPU多核架構(gòu),提升處理速度。
從Flutter端修改Flutter引擎將Context取出后,根據(jù)Context創(chuàng)建上下文的統(tǒng)一管理模塊,每一個(gè)模塊在初始化的時(shí)候會(huì)獲取它的線程,獲取之后會(huì)調(diào)用上下文管理模塊獲取自己的上下文。這樣可以保證每一個(gè)模塊的上下文都是與Flutter的上下文進(jìn)行Share的,每個(gè)模塊之間資源都是共享可見(jiàn)的,F(xiàn)lutter和音視頻native之間也是互相共享可見(jiàn)的。
基于上述框架如果要實(shí)現(xiàn)一個(gè)簡(jiǎn)單的場(chǎng)景,比如畫面實(shí)時(shí)預(yù)覽和濾鏡處理功能,
1:需要選擇功能模塊,功能模塊包括攝像頭模塊、濾鏡處理模塊和Flutter畫面渲染模塊,
2:需要配置模塊參數(shù),比如采集分辨率、濾鏡參數(shù)和前后攝像頭設(shè)置等,
3:在創(chuàng)建視頻管線后使用已配置的參數(shù)創(chuàng)建模塊
4:最后管線搭載模塊,開(kāi)啟管線就可以實(shí)現(xiàn)這樣簡(jiǎn)單的功能。
上圖為整個(gè)功能實(shí)現(xiàn)的代碼和結(jié)構(gòu)圖。
結(jié)合上述音視頻框架,閑魚實(shí)現(xiàn)了Flutter多媒體開(kāi)源組件。
組要包含四個(gè)基本組件分別是:
1:視頻圖像拍攝組件
2:播放器組件
3:視頻圖像編輯組件
4:相冊(cè)選擇組件
現(xiàn)在這些組件正在走內(nèi)部開(kāi)源流程。預(yù)計(jì)9月份,相冊(cè)和播放器會(huì)實(shí)現(xiàn)開(kāi)源。
后續(xù)展望和規(guī)劃
1:實(shí)現(xiàn)開(kāi)頭所說(shuō)的從底層SDK到UI的全鏈路的跨端開(kāi)發(fā)。目前底層框架層和模塊層都是各個(gè)平臺(tái)各自實(shí)現(xiàn),反而是Flutter的UI端進(jìn)行了跨平臺(tái)的統(tǒng)一,所以后續(xù)會(huì)將底層也按照音視頻常用做法把邏輯下沉到C++層,盡可能的實(shí)現(xiàn)全鏈路跨平臺(tái)。
2:第二部分內(nèi)容為開(kāi)源共建,閑魚開(kāi)源的內(nèi)容不僅包括拍攝、編輯組件,還包括了很多底層模塊,希望有開(kāi)發(fā)者在基于Flutter開(kāi)發(fā)音視頻應(yīng)用時(shí)可以充分利用閑魚開(kāi)源出的音視頻模塊能力,搭建APP框架,開(kāi)發(fā)者只要去負(fù)責(zé)實(shí)現(xiàn)特殊需求模塊就可以,盡可能的減少重復(fù)勞動(dòng)。
描述了屏幕上指針(觸摸、鼠標(biāo)、觸控筆)的位置和移動(dòng)。
Flutter中可以使用Listener(功能性組件)來(lái)監(jiān)聽(tīng)原始觸摸事件
例1
例2
例3
忽略PointerEvent
手勢(shì): 描述由一個(gè)或多個(gè)指針移動(dòng)組成的語(yǔ)義動(dòng)作,如拖動(dòng)、縮放、雙擊等。
Material大多數(shù)widget已經(jīng)對(duì)tap或手勢(shì)做出了響應(yīng)。 例如 IconButton和 FlatButton 響應(yīng)單擊,ListView響應(yīng)滑動(dòng)事件觸發(fā)滾動(dòng)。
用于手勢(shì)識(shí)別的功能性組件,通過(guò)它可以來(lái)識(shí)別各種手勢(shì)。
例(單擊)
例(添加Material觸摸水波效果 InkWell組件)
例(滑動(dòng)關(guān)閉 Dismissable組件)
例(單擊、雙擊、長(zhǎng)按)
例(滑動(dòng))
例(掃動(dòng)---單一方向)
例(縮放)
GestureRecognizer是一個(gè)抽象類。
一種手勢(shì)的識(shí)別器對(duì)應(yīng)一個(gè)GestureRecognizer的子類。
例
由于手勢(shì)競(jìng)爭(zhēng)最終只有一個(gè)勝出者,所以,當(dāng)有多個(gè)手勢(shì)識(shí)別器時(shí),可能會(huì)產(chǎn)生沖突。
例
例
在APP中經(jīng)常會(huì)需要一個(gè)廣播機(jī)制,用以跨頁(yè)面通知。比如一個(gè)需要登錄的APP中,頁(yè)面會(huì)關(guān)注用戶登錄或注銷事件,來(lái)進(jìn)行一些狀態(tài)更新。
這時(shí)候,一個(gè)事件總線便會(huì)非常有用,事件總線通常實(shí)現(xiàn)了訂閱者模式,訂閱者模式包含發(fā)布者和訂閱者兩種角色,可以通過(guò)事件總線來(lái)觸發(fā)事件和監(jiān)聽(tīng)事件。
對(duì)于一些簡(jiǎn)單的應(yīng)用,事件總線是足以滿足業(yè)務(wù)需求的,如果決定使用狀態(tài)管理包的話,一定要想清楚APP是否真的有必要使用它,防止“化簡(jiǎn)為繁”、過(guò)度設(shè)計(jì)。
例
在widget樹(shù)中,每一個(gè)節(jié)點(diǎn)都可以分發(fā)通知,通知會(huì)沿著當(dāng)前節(jié)點(diǎn)向上傳遞,所有父節(jié)點(diǎn)都可以通過(guò)NotificationListener來(lái)監(jiān)聽(tīng)通知。
Flutter中將這種由子向父的傳遞通知的機(jī)制稱為通知冒泡(Notification Bubbling)。
通知冒泡和用戶觸摸事件冒泡是相似的,但有一點(diǎn)不同:通知冒泡可以中止,但用戶觸摸事件不行。
通知冒泡和Web開(kāi)發(fā)中瀏覽器事件冒泡原理是相似的,都是事件從出發(fā)源逐層向上傳遞,可以在上層節(jié)點(diǎn)任意位置來(lái)監(jiān)聽(tīng)通知/事件,也可以終止冒泡過(guò)程,終止冒泡后,通知將不會(huì)再向上傳遞。
Flutter的UI框架實(shí)現(xiàn)中,除了在可滾動(dòng)組件在滾動(dòng)過(guò)程中會(huì)發(fā)出ScrollNotification之外,還有一些其它的通知,如SizeChangedLayoutNotification、KeepAliveNotification 、LayoutChangedNotification等,F(xiàn)lutter正是通過(guò)這種通知機(jī)制來(lái)使父元素可以在一些特定時(shí)機(jī)來(lái)做一些事情。
例
例
例
阻止冒泡
通知冒泡原理
Flutter是谷歌公司推出的跨終端的開(kāi)發(fā)框架,支持Android、iOS和WEB終端。1.0版在2018年12月5日發(fā)布,目前的最新版本是1.5,它采用的開(kāi)發(fā)語(yǔ)言是Dart,Dart也是谷歌開(kāi)發(fā)的計(jì)算機(jī)編程語(yǔ)言,語(yǔ)法類似C,是編譯型語(yǔ)言:
hello world例子,打印字符串“Hello World!”:
1、沒(méi)有橋接層
React Native、Weex等技術(shù)都是跨終端的框架,然而性能跟原生App存在很大差距。這是由于它們的工作原理決定的:
React Native、Weex等技術(shù)多了一個(gè)橋接層,所以界面渲染會(huì)慢一些,由于UI渲染非常頻繁,想要不卡頓,基本上比較難,性能和用戶體驗(yàn)跟原生代碼有差距。而這恰恰是Flutter的優(yōu)勢(shì)所在:
Dart可以被編譯成不同平臺(tái)的本地代碼,讓Flutter不通過(guò)橋接層直接跟平臺(tái)通信,自然性能會(huì)快一些。
2、編譯執(zhí)行
JavaScript是解釋執(zhí)行的,Dart是編譯執(zhí)行的,性能誰(shuí)好一目了然。
3、Flutter Engine虛擬機(jī)
Flutter是依靠Flutter Engine虛擬機(jī)在iOS和Android上運(yùn)行的,F(xiàn)lutter Engine使用C/C++編寫,開(kāi)發(fā)人員通過(guò)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ā)生更改時(shí),窗口小部件會(huì)重建其描述,框架將根據(jù)前面的描述進(jìn)行區(qū)分,以確定底層呈現(xiàn)樹(shù)從一個(gè)狀態(tài)轉(zhuǎn)換到下一個(gè)狀態(tài)所需的最小更改??梢灾苯釉贠S平臺(tái)提供的畫布上進(jìn)行描繪,也就是一些核心類庫(kù)直接放到虛擬機(jī)里面,調(diào)用起來(lái)更快。
從它的系統(tǒng)結(jié)構(gòu)可以看出,類似安卓的ART(Android Run Time)虛擬機(jī),同樣采用AOT(Ahead of TIme)技術(shù),會(huì)在APP安裝時(shí)就編譯成機(jī)器語(yǔ)言,不再解釋執(zhí)行,從而優(yōu)化了APP運(yùn)行的性能。
4、自帶渲染引擎
Flutter使用谷歌自己的Skia渲染引擎,而Android系統(tǒng)自帶Skia引擎,iOS平臺(tái)上Flutter也會(huì)把Skia引擎打包到APP中,從而實(shí)現(xiàn)了高效渲染。而React Native通過(guò)橋接層訪問(wèn)原生UI,操作頻繁就容易出性能問(wèn)題。
綜合所述,F(xiàn)lutter 是性能最接近原生代碼 的一種開(kāi)發(fā)框架,未來(lái)也會(huì)是構(gòu)建谷歌Fuchsia應(yīng)用的主要方式,前途不可限量,唯一的問(wèn)題就是需要學(xué)習(xí)一門新的語(yǔ)言:Dart,而有Java或者C#語(yǔ)言基礎(chǔ)的程序員會(huì)比較容易學(xué)習(xí)。