备注:本帖得出结论的前提是采用原生框架,使用flutter module进行开发的项目,但同时具有一定普适性;
最近项目1.0开发进入尾声,从忙碌的开发阶段即将转入维护期,疯狂输出后必定有需要细化的地方,首当其冲就是CPU和内存管理;
CUP简单总结一下,就是频繁渲染造成的,比如setstate和animation(包含自带动画的组件,实话实说,flutter的动画刷屏真的烂,一个跑马灯就要吃掉30%),这块我们无法完全防止,目前通过局部刷新或者采用原生view(AndroidView或者UiKitView)来尽量降低CPU的消耗;局部刷新的思想要保持,不管是builder还是RepaintBoundary都是有必要合理地用一用的;
下面说一下内存管理踩坑...抛砖引玉下,求交流...
纯flutter框架对性能检查的需求比较友好,原生打底的项目就不太行了,基础的observatory一定要会看,一些tracker插件可以适当用一用.另外flutterboost有些内存机制上的缺陷,目前暂且忽略吧...
1.图片缓存.
- 这块还是因为flutter框架,由于内存管理机制是MRC,所以框架上一直对图片缓存执行retain动作,想及时清除的话,要么通过三方插件手动clear,要么就得改flutterboost框架,后者维护成本非常高不建议,那就只能使用插件(图片问题在非原生框架项目下产生的影响比较小,按需慎选);
- 第二个就是学习京东模式得来的经验,即在缓存图片的时候对图片进行resize处理,将原图进行适当压缩,得到体积更小的缓存文件,具体方法建议插件打底并且做一下封装(有小坑多测试优化即可),效果非常明显,无图无真相,谁试谁知道;
2.对象管理.
- Controller 在项目中我们会经常使用stateful进行页面的开发,免不了使用对象,int,string,map...这些都还好,大坑就是controller一类,比如pagecontroller,swipercontroller等等这一类可以.dispose()的, 在dispose方法里,这些controller在.dispose()后一定一定一定要=null(想验证的童鞋可以打印一下),并且加上addListen的一定要remove()掉,开发习惯上属性对象尽量使用?来修饰,final可用可不用,并且及时释放,如果你愿意,GlobalKey也是可以打?且=null的;
-
Timer 这个类很神奇,通过试验得到的结论是,Timer在dispose中调用cancel()和=null以后,内存监控显示非常有规律的波动(如图1),而不是静态页面的波动(如图2),这个问题仍在探索中,目前我的临时办法是使用StreamBuilder进行局部刷新来替代Timer,也不需要管理释放问题,效果很好,当然如果想用Timer也没有什么问题,内存上的影响很小.
附带streamBuilder方法使用
StreamBuilder(
stream: counter(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
return Text(
countDownStr ?? "",
overflow: TextOverflow.ellipsis,
style: TextStyle(color: Colors.white, fontSize: 13.sl()),
);
},
),
Stream<int> counter() {
return Stream.periodic(const Duration(seconds: 1), (i) {
/// i会自增1
return i;
});
}
- 匿名函数(闭包/block/Closure) 用问号修饰,及时置空;
- const常量 没什么问题,该用就用吧;
- 自定义对象 处理同匿名函数,包含且不限于单例(内的属性和对象),注意单例弱引用的使用;
- Async 通常泄漏是发生在异步调用时,对象被销毁的情况下,(比如一个耗时任务或者用了Future.delayed),研究state周期后个人理解是因为异步下调用的方法引用了context,当页面释放后找不到context产生了内存泄漏,这个没有什么太好的解决办法,延时大法好,使用需谨慎;
3.开发习惯
- 注意平时没有使用过的自定义变量,一定要注掉;
- 对于状态变化需要更新UI的地方,如果有使用 const SizeBox()或者Container()的,换成null吧;
待续...
4.列表
- 第一是大家比较熟悉的,使用.builder修饰列表;
- 第二个比较好,拿listview举栗子,由于渲染机制,当上拉后会产生很多的数据,你以为它不渲染,它确实不渲染,但是也确实占用内存了,我们这时候可以用下面两个属性来修饰,这样内存就不会拉着涨着了
addAutomaticKeepAlives: false,
addRepaintBoundaries: false,