Flutter的原理及美团的实践(下)
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680
Flutter和原生性能对比
虽然使用原生实现(左)和Flutter实现(右)的全品类页面在实际使用过程中几乎分辨不出来:
但是我们还需要在性能方面有一个比较明确的数据对比。
我们最关心的两个页面性能指标就是页面加载时间和页面渲染速度。测试页面加载速度可以直接使用美团内部的Metrics性能测试工具,我们将页面Activity对象创建作为页面加载的开始时间,页面API数据返回作为页面加载结束时间。
从两个实现的页面分别启动400多次的数据中可以看到,原生实现(AllCategoryActivity)的加载时间中位数为210ms,Flutter实现(FlutterCategoryActivity)的加载时间中位数为231ms。考虑到目前我们还没有针对FlutterView做缓存和重用,FlutterView每次创建都需要初始化整个Flutter环境并加载相关代码,多出的20ms还在预期范围内:
因为Flutter的UI逻辑和绘制代码都不在主线程执行,Metrics原有的FPS功能无法统计到Flutter页面的真实情况,我们需要用特殊方法来对比两种实现的渲染效率。Android原生实现的界面渲染耗时使用系统提供的FrameMetrics接口进行监控:
public class AllCategoryActivity extends WmBaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { getWindow().addOnFrameMetricsAvailableListener(new Window.OnFrameMetricsAvailableListener() { List<Integer> frameDurations = new ArrayList<>(100); @Override public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, int dropCountSinceLastInvocation) { frameDurations.add((int) (frameMetrics.getMetric(TOTAL_DURATION) / 1000000)); if (frameDurations.size() == 100) { getWindow().removeOnFrameMetricsAvailableListener(this); L.w("AllCategory", Arrays.toString(frameDurations.toArray())); } } }, new Handler(Looper.getMainLooper())); } super.onCreate(savedInstanceState); // ... } }
Flutter在Framework层只能取到每帧中UI操作的CPU耗时,GPU操作在Flutter引擎内部实现,所以要修改引擎来监控完整的渲染耗时,在Flutter引擎目录下src/flutter/shell/common/http://rasterizer.cc文件中添加:
void Rasterizer::DoDraw(std::unique_ptr<flow::LayerTree> layer_tree) { if (!layer_tree || !surface_) { return; } if (DrawToSurface(*layer_tree)) { last_layer_tree_ = std::move(layer_tree); #if defined(OS_ANDROID) if (compositor_context_->frame_count().count() == 101) { std::ostringstream os; os << "["; const std::vector<TimeDelta> &engine_laps = compositor_context_->engine_time().Laps(); const std::vector<TimeDelta> &frame_laps = compositor_context_->frame_time().Laps(); size_t i = 1; for (auto engine_iter = engine_laps.begin() + 1, frame_iter = frame_laps.begin() + 1; i < 101 && engine_iter != engine_laps.end(); i++, engine_iter++, frame_iter++) { os << (*engine_iter + *frame_iter).ToMilliseconds() << ","; } os << "]"; __android_log_write(ANDROID_LOG_WARN, "AllCategory", os.str().c_str()); } #endif } }
即可得到每帧绘制时真正消耗的时间。测试时我们将两种实现的页面分别打开100次,每次打开后执行两次滚动操作,使其绘制100帧,将这100帧的每帧耗时记录下来:
for (( i = 0; i < 100; i++ )); do openWMPage allcategory sleep 1 adb shell input swipe 500 1000 500 300 900 adb shell input swipe 500 1000 500 300 900 adb shell input keyevent 4 done
将测试结果的100次启动中每帧耗时取平均値,得到每帧平均耗时情况(横坐标轴为帧序列,纵坐标轴为每帧耗时,单位为毫秒):
Android原生实现和Flutter版本都会在页面打开的前5帧超过16ms,刚打开页面时原生实现需要创建大量View,Flutter也需要创建大量Widget,后续帧中可以重用大部分控件和渲染节点(原生的RenderNode和Flutter的RenderObject),所以启动时的布局和渲染操作都是最耗时的。
10000帧(100次×100帧每次)中Android原生总平均値为10.21ms,Flutter总平均値为12.28ms,Android原生实现总丢帧数851帧8.51%,Flutter总丢帧987帧9.87%。在原生实现的触摸事件处理和过度绘制充分优化的前提下,Flutter完全可以媲美原生的性能。
总结
Flutter目前仍处于早期阶段,也还没有发布正式的Release版本,不过我们看到Flutter团队一直在为这一目标而努力。虽然Flutter的开发生态不如Android和iOS原生应用那么成熟,许多常用的复杂控件还需要自己实现,有的甚至会比较困难(比如官方尚未提供的ListView.scrollTo(index)功能),但是在高性能和跨平台方面Flutter在众多UI框架中还是有很大优势的。
开发Flutter应用只能使用Dart语言,Dart本身既有静态语言的特性,也支持动态语言的部分特性,对于Java和JavaScript开发者来说门槛都不高,3-5天可以快速上手,大约1-2周可以熟练掌握。
在开发全品类页面的Flutter版本时我们也深刻体会到了Dart语言的魅力,Dart的语言特性使得Flutter的界面构建过程也比Android原生的XML+JAVA更直观,代码量也从原来的900多行减少到500多行(排除掉引用的公共组件)。Flutter页面集成到App后APK体积至少会增加5.5MB,其中包括3.3MB的SO库文件和2.2MB的ICU数据文件,此外业务代码1300行编译产物的大小有2MB左右。
Flutter本身的特性适合追求iOS和Android跨平台的一致体验,追求高性能的UI交互效果的场景,不适合追求动态化部署的场景。Flutter在Android上已经可以实现动态化部署,但是由于Apple的限制,在iOS上实现动态化部署非常困难,Flutter团队也正在和Apple积极沟通。
原文作者:美团技术团队
原文链接:https://zhuanlan.zhihu.com/p/41732803
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
MaxCompute - ODPS重装上阵 第六弹 - User Defined Type
MaxCompute(原ODPS)是阿里云自主研发的具有业界领先水平的分布式大数据处理平台, 尤其在集团内部得到广泛应用,支撑了多个BU的核心业务。 MaxCompute除了持续优化性能外,也致力于提升SQL语言的用户体验和表达能力,提高广大ODPS开发者的生产力。 MaxCompute基于ODPS2.0新一代的SQL引擎,显著提升了SQL语言编译过程的易用性与语言的表达能力。我们在此推出MaxCompute(ODPS2.0)重装上阵系列文章 第一弹 - 善用MaxCompute编译器的错误和警告 第二弹 - 新的基本数据类型与内建函数 第三弹 - 复杂类型 第四弹 - CTE,VALUES,SEMIJOIN 第五弹 - SELECT TRANSFORM 第五弹向您介绍了MaxCompute如何嵌入其他语言的脚本。SELECT TRANSFORM的优势在于可以不创建function甚至不上传资源的情况下执行其他语言的脚本,而即使需要编写资源也没有任何由MaxCompute规定的格式要求和依赖。本文将介绍另一种将这一优势提升到更高层次的新功能:User Defined Type,简称UD...
- 下一篇
imi v1.0.22 发布,支持毫秒级注解热更新的 Swoole 框架
imi 是基于 PHP Swoole 的高性能协程应用开发框架,它支持 HttpApi、WebSocket、TCP、UDP 服务的开发。 在 Swoole 的加持下,相比 php-fpm 请求响应能力,I/O密集型场景处理能力,有着本质上的提升。 imi 框架拥有丰富的功能组件,可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。可以使企业 IT 研发团队的效率大大提升,更加专注于开发创新产品。 首创毫秒级的注解及代码热更新重启,让你在开发时只管一把梭,体验与 fpm 下开发并无二致,保存就刷新,立马看到效果。 与其他专注微服务领域的 Swoole 框架不同,imi 专注单体应用开发。原因很简单:大部分公司都不需要上微服务,单体应用足矣。 官方网站:https://www.imiphp.com/ 码云:https://gitee.com/yurunsoft/IMI Github:https://github.com/Yurunsoft/imi imi 框架第一个版本 v0.0.1 首发于 2018 年 6 月 21 日 日常水一下 本周...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS8安装Docker,最新的服务器搭配容器使用
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS关闭SELinux安全模块
- CentOS7设置SWAP分区,小内存服务器的救世主
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装