研发
exping 开发之 Flutter 优化篇
tl;dr 使用 Flutter,花了大半年的时间开发了exping。上线后,当周登上了 Apple Store 推荐榜单。
使用 Flutter,花了大半年的时间开发了 exping。上线后,当周登上了 Apple Store 推荐榜单。
感谢大家的厚爱。但同时也暴露出它的不足: 页面浏览出现卡顿,掉帧,有了这样的类似评价。
Flutter 体验真差,比原生差得远。
当初选择 Flutter,就是看中它的跨平台优势,极大降低开发成本,(两个开发童鞋,前后端(打杂)全包~)。
现在上线了,深受大家喜爱,那就必须,撸起袖子,搞它!!!(避免饭碗不保)
APP 首次运行出现各种卡顿
收到最多反馈,就是各种页面卡顿... 产品,测试,开发 全都疑惑: ~为啥我们没觉得卡😅...
经过跟用户大佬们的再三讨教(掰扯),原来是首次打开卡顿,重新进入又又正常了。原来是我们被“卡”习惯了🥶。
之所以会出现这类问题,套用官方解释:SkSL 着色器编译卡顿
首次运行 App,进入页面时,需要先对页面所使用着色器进行编译,再渲染页面。那要解决这个问题,就是对页面的所用着色器进行预热(预编译)。
Flutter 官网给出了具体解决方案:
Shader compilation jank
操作步骤
- 使用 --cache-sksl 运行,用于捕获着色器 (直接命令行运行)
flutter run --profile --cache-sksl
-
运行后,对 APP 有卡顿的页面,都浏览一遍。元素比较多的页面,可以多打开几次,特别有复杂动画的页面。
-
打开页面后,按下大写的
M
(注意是大写~),会在项目目录生成flutter_01.sksl.json
,这个文件记录捕获到的着色器。 -
修改运行指令,将着色器文件打包进 APP,告诉 APP 那些着色器需要预热,那 APP 在首次运行时,就会对指定着色器进行编译后,再进入 APP。
# 运行时指定
flutter run --profile --bundle-sksl-path flutter_01.sksl.json
# 打包时指定
flutter build apk --bundle-sksl-path flutter_01.sksl.json
卸载原来 APP,安装打开新 APP,眼前一亮,马上发给产品童鞋,立马收获一排大拇指👍🏻
此方案带来了极大的优化体验,但是也有弊端:
-
对 APP 体积大小有一点影响,毕竟多了一个 json 文件打包进去。但大小能接受。1MB 左右。
-
影响首次打开 APP 的速度。如果页面全覆盖,打开 APP 会有 3-5 秒的延迟。但与每个页面都给用户带来卡顿的体验,这性价比还是比较高。
部分页面进入时卡顿
完成上面优化后,部分页面在访问时,仍会有卡顿现象。排查着色器问题后,从自身代码上进行排查。
由于是进入页面瞬间卡顿,结合 DevTools
分析,注意到部分卡顿页面,有一个共性是: 在 initState
中,进行接口网络请求或者数据组装后,通知页面刷新。 如果这里的逻辑比较复杂或者频繁刷新Widget,就会出现严重的卡帧。由于Dart是单线程,就算是异步操作,当操作过于复杂耗时,仍会造成页面卡顿。(60hz下,要求不超过16ms)
解决方案其实也比较简单,避开在 initState
直接进行复杂操作:
/// 在下一帧再进行
WidgetsBinding.instance!.addObserver((_){
// ... 具体操作
});
对相关页面调整后,进入页面时,卡帧问题已经明显得到改善。
继续加把劲,让页面看起来丝滑点: 数据请求完成后,loading 页面转场过于死板,一闪而过,容易造成卡顿假象.. 那就加个转场透明渐变:
AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: child,
transitionBuilder: (Widget child,Animation<double> animation) {
// 透明渐变
return FadeTransition(opacity: animation,child: child);
});
相册选择器优化
某个小伙伴反馈,我要选择照片,但相册一直加载不出来!!!
A: 你手机相册中有多少张照片?
B: 10 万张左右吧
🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️🧎♂️
请收下我的膝盖... 立马抓来测试童鞋,去给测试机搞10 万张图片,复现一下问题!
目前 APP 对图片的操作,使用插件: photo_manager
photo_manager | Flutter Package
相册选择器则是参考 wechat_assets_picker 的部分逻辑,重新封装成更符合 APP 需求的照片选择器:
https://github.com/iFREEGROUP/photo_picker
通过调试发现,当相册照片在 5 万张左右时,在调用PhotoManager.getAssetPathList
耗费时间大概10 秒左右,如果照片在 iCloud,那耗费的时间更长。
PhotoManager.getAssetPathList
默认参数中 hasAll
= true,onlyAll
= false,通过分析源码发现,onlyAll
为 false 时,需要等待获取到所有相册路径,才会返回。
而 onlyAll
为 true 时,只获取 最近照片
的相册路径,耗时不超过 500ms。
按照这思路,修改下代码:
// 优先取出 "全部",加速照片显示
pathList = await PhotoManager.getAssetPathList(
hasAll: true,
onlyAll: true,
type: config.requestType,
filterOption: options,
);
if (!config.onlyAll) {
// 再去异步获取其他文件夹
PhotoManager.getAssetPathList(
hasAll: false,
onlyAll: false,
type: config.requestType,
filterOption: options,
).then((value) {
config.getPhotoSortPathDelegate.sort(value);
_allPathList.addAll(value);
_cacheFirstThumbFromPathEntity(_allPathList);
});
}
打开相册,优先获取最近照片显示出来,再去获取其他相册。
虽然无法加快 获取其他相册 的速度,但能快速呈现出照片给用户选择,减少等待时间,操作体验上提升了一大步。而异步去获取其他相册,则是无感知的。
优化完相册打开速度,需要再看看照片列表卡顿问题.
从查看 DevTools
发现,当页面同一帧加载 40 张(4*10) 时,出现红色警告:
那就避免一帧同时渲染太多图片,这里使用社区大佬的分帧插件:
经过修改,从 DevTools
上可以看到,加载速度提升明显:
相册选择器的优化工作基本就这两项。而分帧加载的方案,也同时应用在元素多且复杂的页面上,有效降低卡帧现象。
除了前面提到的三点优化工作,同时也对部分不规范的或者有优化空间的代码逻辑进行修改调整,比如 封装弹框,重写 emoji 评分选择器,对地图 Maker 刷新显示规则进行调整 等等..
而针对 iPhone 高刷问题,目前 Flutter master 分支已经支持,可以通过调整 Flutter 版本分支,体验下高刷效果。我们也等 Flutter 更新稳定版本后,第一时间更新 APP。
https://github.com/flutter/flutter/issues/90675
目前 exping 1.0.2 版本已发布。欢迎下载体验。https://exping.world
分享