背景
說到flutter的圖像渲染性能,首先我們來看一下iOS和Android的渲染過程
在安卓中耗時(shí)的操作是測(cè)量,布局,繪制等都是由view負(fù)責(zé)的,iOS也非常的相似由UIview負(fù)責(zé),大部分時(shí)間在遍歷UIview樹的不同節(jié)點(diǎn)
相比于原生Flutter在渲染的流程上并不會(huì)遜色
如果在fultter中找到和UIview和view相似的部件那就是RenderObject,有狀態(tài),有長(zhǎng)期生命,和原生不同的是他只有一個(gè)布局功能的函數(shù)performLayout();
? /// In implementing this function, you must call [layout] on each of your
? /// children, passing true for parentUsesSize if your layout information is
? /// dependent on your child's layout information. Passing true for
? /// parentUsesSize ensures that this render object will undergo layout if the
? /// child undergoes layout. Otherwise, the child can change its layout
? /// information without informing this render object.
? @protected
? void performLayout();
RenderObject擁有面積更小的API,相比于安卓的250和iOS的500個(gè),他只有50個(gè)比較簡(jiǎn)單
查看flutter的渲染性能.
在Android Studio 中使用Profile模式運(yùn)行(像碼表一樣的按鈕),如果使用command line 來運(yùn)行的話可以使用flutter run --profile來運(yùn)行,在查看性能時(shí)一定要使用真機(jī)來運(yùn)行,不要使用模擬器,第一模擬器使用的軟件渲染,第二模擬器是debug模式,所以并不能真實(shí)的反應(yīng)出每一幀的渲染時(shí)間
如果想顯示在手機(jī)上可以使用show performance overlay
有左邊的一個(gè)Container,從圖中可以看出wieget和element是一一對(duì)應(yīng)的,componetElement是沒有自己的RenderObnject的,他是用來做組合的,并不會(huì)參與布局和繪制,如果對(duì)componetElement再進(jìn)行分類就會(huì)出現(xiàn)StatelessElement和StatefulElement,對(duì)應(yīng)的widget就是?StatelessWidget和StatefulWidget
build階段
從text('data')如果變成text('A'),build的開始,widget是不可改變的,只能將widget拋棄創(chuàng)建新的樹(可以把widget看成是element的描述),下一步對(duì)上一幀的Element樹做遍歷,最重要的方法
Element.updateChild();
方法通過查看element的子節(jié)點(diǎn),如果子節(jié)點(diǎn)和之前的類型一樣那么就繼續(xù)向下遍歷,如果不一樣就會(huì)將element進(jìn)行更新,如果是Componet類型就可以statelessWidget和statefulWidget做更新叫build,他會(huì)提供一個(gè)DecorateBox,拿到decorateBox做進(jìn)一步的遍歷,decorateBox是一個(gè)RenderObject類型,讓RenderObjectwidget對(duì)于RenderObject做更新,通過updateRenderObject();方法,按照這樣的邏輯遍歷到最后只有Text需要被更新,那么減少遍歷就會(huì)優(yōu)化每一幀的時(shí)間
在Android Studio可以使用Open Observatory 打開觀測(cè)臺(tái)工具
通過觀測(cè)臺(tái)可以查看內(nèi)存,以及CPU的工作情況,通過timeline功能可以查看各階段耗時(shí)情況,在任意位置添加debugProfileBuildsEnabled = true;
可以在timeline中看到我們遍歷了很多節(jié)點(diǎn),我們只改變了一個(gè)text卻要編了非常龐大的樹,確實(shí)存在效率問題
那么如何能提高Build的遍歷效率呢?
1.降低遍歷的出發(fā)點(diǎn)
沒有必要從頂層開始遍歷,應(yīng)該是從text出現(xiàn)的地方開始遍歷,那么我們修改代碼,將text單獨(dú)提取出來
那么再來看一下遍歷的效率,非常好的降低了遍歷的數(shù)量提高效率
2.停止樹的遍歷
? ?將上一幀中的子樹從上一幀切下來直接放到新的一幀上,SlideTransition的每一幀都會(huì)對(duì)自己做比較更新做改變,如果加入了child那么他的子樹就不會(huì)跟著,SlideTransition做比較更新
SlideTransition(
...
child: Row(
children: <Widget>[],
))
提高Paint流程效率
在非常復(fù)雜的頁面情況下,會(huì)形成多個(gè)圖層的疊加,那么圖層也會(huì)形成自己的樹,在繪制一個(gè)widget時(shí),整個(gè)界面的圖層都會(huì)跟著重新繪制
在iOS重每個(gè)UIview都擁有自己的CALayer,雖然會(huì)增加內(nèi)存但是卻可以提高效率,安卓中每一幀都將會(huì)重新繪制所有的節(jié)點(diǎn)
Flutter則會(huì)在每個(gè)分界線都形成自己的圖層如果一個(gè)圖層只是自己更新而不會(huì)影響其他圖層那么可以增加一個(gè)RepaintBoundary來創(chuàng)造一個(gè)自己的圖層
RepaintBoundary(
child: Text('A'),
)
這時(shí)候我們來看圖層的更新只會(huì)更新自己節(jié)點(diǎn)的圖層