背景
说到flutter的图像渲染性能,首先我们来看一下iOS和Android的渲染过程
在安卓中耗时的操作是测量,布局,绘制等都是由view负责的,iOS也非常的相似由UIview负责,大部分时间在遍历UIview树的不同节点
相比于原生Flutter在渲染的流程上并不会逊色
如果在fultter中找到和UIview和view相似的部件那就是RenderObject,有状态,有长期生命,和原生不同的是他只有一个布局功能的函数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个,他只有50个比较简单
查看flutter的渲染性能.
在Android Studio 中使用Profile模式运行(像码表一样的按钮),如果使用command line 来运行的话可以使用flutter run --profile来运行,在查看性能时一定要使用真机来运行,不要使用模拟器,第一模拟器使用的软件渲染,第二模拟器是debug模式,所以并不能真实的反应出每一帧的渲染时间
如果想显示在手机上可以使用show performance overlay
有左边的一个Container,从图中可以看出wieget和element是一一对应的,componetElement是没有自己的RenderObnject的,他是用来做组合的,并不会参与布局和绘制,如果对componetElement再进行分类就会出现StatelessElement和StatefulElement,对应的widget就是 StatelessWidget和StatefulWidget
build阶段
从text('data')如果变成text('A'),build的开始,widget是不可改变的,只能将widget抛弃创建新的树(可以把widget看成是element的描述),下一步对上一帧的Element树做遍历,最重要的方法
Element.updateChild();
方法通过查看element的子节点,如果子节点和之前的类型一样那么就继续向下遍历,如果不一样就会将element进行更新,如果是Componet类型就可以statelessWidget和statefulWidget做更新叫build,他会提供一个DecorateBox,拿到decorateBox做进一步的遍历,decorateBox是一个RenderObject类型,让RenderObjectwidget对于RenderObject做更新,通过updateRenderObject();方法,按照这样的逻辑遍历到最后只有Text需要被更新,那么减少遍历就会优化每一帧的时间
在Android Studio可以使用Open Observatory 打开观测台工具
通过观测台可以查看内存,以及CPU的工作情况,通过timeline功能可以查看各阶段耗时情况,在任意位置添加debugProfileBuildsEnabled = true;
可以在timeline中看到我们遍历了很多节点,我们只改变了一个text却要编了非常庞大的树,确实存在效率问题
那么如何能提高Build的遍历效率呢?
1.降低遍历的出发点
没有必要从顶层开始遍历,应该是从text出现的地方开始遍历,那么我们修改代码,将text单独提取出来
那么再来看一下遍历的效率,非常好的降低了遍历的数量提高效率
2.停止树的遍历
将上一帧中的子树从上一帧切下来直接放到新的一帧上,SlideTransition的每一帧都会对自己做比较更新做改变,如果加入了child那么他的子树就不会跟着,SlideTransition做比较更新
SlideTransition(
...
child: Row(
children: <Widget>[],
))
提高Paint流程效率
在非常复杂的页面情况下,会形成多个图层的叠加,那么图层也会形成自己的树,在绘制一个widget时,整个界面的图层都会跟着重新绘制
在iOS重每个UIview都拥有自己的CALayer,虽然会增加内存但是却可以提高效率,安卓中每一帧都将会重新绘制所有的节点
Flutter则会在每个分界线都形成自己的图层如果一个图层只是自己更新而不会影响其他图层那么可以增加一个RepaintBoundary来创造一个自己的图层
RepaintBoundary(
child: Text('A'),
)
这时候我们来看图层的更新只会更新自己节点的图层