写点什么

使用 Flutter 之后,我们的 CPU 占用率降了 50%

2019 年 2 月 22 日

使用Flutter之后,我们的CPU占用率降了50%

近年来,移动互联网迅猛发展,业务需求频繁更新,业务内容动态化需求急剧增加,纯原生开发已经无法满足业务快速增长的需求,因此诞生了多种跨平台开发框架,如 H5+ 原生开发、React Native 和 Weex ,但这两年最受开发者青睐的莫过于 Flutter。目前,很多应用都集成了 Flutter,我们团队也在涨乐财富通上实现了完整 Flutter 的集成过程,以下篇幅会具体介绍整个集成过程。


涨乐 Flutter 实践(以 iOS 为例)

此次实践主要是为了验证整个流程,为后续大规模应用 Flutter 做铺垫,因此我们选择了一个业务相对简单的“技术论市”页面进行改造,该页面之前是 H5 实现的列表页,点击栏目会跳转到另一个 H5 页面详情页。


改造之后,原生界面点击按钮会打开 Flutter 列表页,点击 Flutter 页面的栏目会跳转到 H5 页面,点击返回可依次返回到上一个界面。从图中可以看出,整个流程的使用体验非常流畅。



组件化集成

如何将 Flutter 代码集成进现有工程是我们遇到的第一个挑战。Flutter 官网提供了一种解决方案,但存在以下缺陷:


1.需要修改主工程的配置,入侵原有工程;


2.工程运行需要 Flutter 环境,而在实际开发中并不是所有的团队成员都会参与到 Flutter 的开发,安装 Flutter 环境对于那些不需要 Flutter 开发的成员来说显然不合理。


官网的方案行不同,我们必须另辟蹊径。研究 Flutter 的编译脚本xcode_backend.sh发现,只要将 Flutter 编译产物放入主工程就能运行 Flutter 模块。为了便于管理,涨乐财富通采用私有 pod 管理编译产物的方式来集成 Flutter。


Flutter 工程的编译产物包含三个部分,分别是:


1.App.framework:所有的 Dart 代码,包括业务代码和依赖的第三方 package 代码,在 Debug 模式下只是一个空壳,在 Release 模式下是所有代码生成的机器码。


2.Flutter.framework:Flutter 的 SDK。


3.flutter_assets:Flutter 资源文件,包括字体、图片等。


创建私有 pod 用来管理这些编译产物,podspec 的核心内容如下:


  s.source_files = 'htflutter_product_debug/Plugin/**/*'
s.vendored_frameworks = 'Framework/*.framework', 'Framework/engine/*.framework' s.resources = 'Framework/flutter_assets'
复制代码


主工程需要集成 Flutter 模块时,只需要在 podfile 中依赖该私有 pod 即可。


混合栈管理

引入 Flutter 模块后,需要考虑的就是如何管理混合栈。在现有应用中,已经存在原生 + 网页的混合栈,如今引入了 Flutter 需要解决这三者如何嵌套。


混合栈管理的方案必须具备以下特点:


1.原生、H5、Flutter 页面三者能相互调用,并且用户感觉不到差异;


2.尽量减少资源消耗;


3.每个页面的生命周期保持完整。


为此,我们借鉴了闲鱼团队开源的混合栈管理方案,并与我们现有的路由管理方案相结合,在涨乐上实现了混合栈管理,具体架构图如下:



  • 页面跳转使用统一的路由管理。


涨乐财富通使用路由管理器来统一管理页面。当需要打开一个 Flutter 页面时,只需要像原来一样,发送一个打开 Flutter 的路由,并携带参数用来标识具体的页面。路由管理器识别到是 Flutter 路由后会创建新的WrapFlutterViewController并压栈。WrapFlutterViewController会使用 FlutterViewController单例作为其子 VC,利用传递过来在参数在FlutterViewController内部打开具体的 Flutter 页面。


  • 所有的 Flutter 页面共用一个 Flutter 实例,iOS 使用 FlutterViewController,Android 使用 FlutterNativeView


共用一个 Flutter 实例,既可以使得 Flutter 页面之间实现数据通信和共享,也可以减少额外的资源消耗。因为每一个 Flutter 实例会启动三个线程,分别是 UI 线程、GPU 线程和 IO 线程,只创建一个 Flutter 实例减少了资源的使用。


  • 每一个 Flutter 页面对应一个原生页面。


每次 push/pop 一个 Flutter 页面,一方面会操作 Flutter 实例内部的导航栈,另一方面在外部会 push/pop 一个原生的页面,这样可以确保 Flutter 页面和原生页面的同步。


自动化

整个 Flutter 的开发过程分为以下两大步骤:


1.编写 dart 和 plugin 代码并生成 App.framework,Flutter_Asset 文件夹和 Flutter.framework;


2.将编译产物集成到 iOS 主工程;


自动化需要解决几个关键问题:


1.如何区分 debug 和 release 模式下的产物包


2.自动化的流程应该如何控制


针对第一个问题,我们的解决办法是创建两个 repo,htflutter_product_debughtflutter_product_release,开发使用 debug 产物,生产使用 release 产物。


第二个问题,我们参考的是 CocoaPods 的 pod 发布流程,将 Flutter 主工程作为一个私有 repo 来看待,通过 tag 触发脚本生成产物,再 push 到htflutter_product_debughtflutter_product_release。具体流程如下图:



1.首先htcftflutter是我们的 Flutter 主工程,包含所有的 Dart 源代码和 plugin 代码。


2.通过 tag 名触发脚本,编译出两种模式的产物,例如 tag:debug_1.0则编译出 debug prudoct。


3.将产物推送到远端产物 pod repo(这一步实际上类似 pod repo push)。这一步相对复杂一点,


首先需要 clone 远端的产物 pod 到当前的某个临时文件夹,然后将 Flutter 主工程中编译的产物拷贝到临时文件夹中,其中包含 App.framework,flutter_assets 文件夹以及 Flutter.framework,另外还有 plugin 相关文件。前面三个都好办,直接拷贝即可,plugin 比较麻烦,plugin 的代码通过 package 的形式引入到工程中,并不在 Flutter 主工程,需要从.flutter-plugins文件中读取到各个 plugin 到路径,然后到对应到路径进行拷贝。拷贝完成之后,再通过脚本完成git的相关操作即可,最后 push 完成,删除临时文件夹,这样htcftflutter不感知整个脚本执行过程。


4.iOS 主工程集成 Flutter 产物 pod,默认情况下podfile中依赖htflutter_product_debug,主工程打 release tag 时,触发脚本将依赖修改成htflutter_product_release,并执行pod update


相关脚本如下:


 tool = HTTool.new(mode) //debug/release tool.build_ios() #编译产物 tool.clone_flutter_product_repo() #clone product repo tool.copy_products()   #拷贝产物 tool.copy_plugin_code() #拷贝plugin 原生代码 tool.updateSpecVersion() #更新版本 tool.push_flutter_product_repo() #push to product repo tool.remove_product_repo()#delete product repo after push
复制代码


降级策略

Flutter 还处于快速迭代发展的阶段,正式上线可能存在不确定的风险,为此我们设计了具体的降级方案,应对 Flutter 发生异常的情况。



1.应用启动时,服务器会下发 Flutter 降级配置表,key 是需要降级的 Flutter 页面路径,value 是需要执行的降级路由操作;


2.路由管理器响应 Flutter 路由时,会首先判断需要打开的 Flutter 页面是否需要降级,若需要,则会执行配置表中的路由操作,降级到网页;反之则正常跳转到 Flutter 页面。


实践结果

1.安装包大小


引入 Flutter 之前,涨乐财富通的安装包为 94MB,引入之后大小为 100MB,发现增大了 6MB,这其中主要是引入了 Flutter 的 SDK,增加的大小在可以接受的范围。


2.FPS 和 GPU



从上图可以看出,Flutter 的 FPS 接近 60,和原生效果基本一致,而 H5 的 FPS 在 50 左右,远不如 Flutter 优秀。两者的 GPU 使用率基本相同。


3.内存



内存表现方面,H5 页面使用的内存要小于 Flutter。


4.CPU



从 CPU 的占用率来看,Flutter 占用的 CPU 要远远小于 H5 页面。


总结

从我们的实践结果来看,Flutter 在性能方面拥有绝对优秀的体验,但 Flutter 的开发生态还不够成熟,完全取代原生开发实现跨平台为时尚早,但对于一些追求一致性、高性能的界面可以尝试采用 Flutter 实现。我们涨乐财富通开发团队也会持续跟进 Flutter 的发展,将 Flutter 推广应用到更多业务场景中。


更多内容,请关注前端之巅。



2019 年 2 月 22 日 00:006484

评论 2 条评论

发布
用户头像
额,还以为是对比原生,结果是和H5对比。
2019 年 04 月 28 日 17:07
回复
O(∩_∩)O哈哈~
2020 年 05 月 27 日 15:04
回复
没有更多了
发现更多内容

学写PEP,参与Python语言的设计

早睡蟒

Python Python PEP PEP

架构师训练营第四周总结

hiqian

在以后的高校招生中,应用区块链技术,可以防、治冒名顶替吗?

CECBC区块链专委会

区块链技术 高考 信息防篡改

ARTS - Week 4

Khirye

ARTS 打卡计划 arts

限频/限流的一些思考

i风语

Java redis 微服务 sentinel ratelimiter

软件架构语录

hiqian

IM聊天教程:发送图片/视频/语音/表情

GoEasy消息推送

websocket 即时通讯 聊天室 聊天

ARTS打卡-04

Geek_yansheng25

游戏夜读 | 这是一款情绪游戏

game1night

疫情常态下区块链构建分布式数字身份

CECBC区块链专委会

疫情 区块链技术 分布式数字身份

JDBC 批量插入:MyBatis、PostgreSQL

羊八井

postgresql mybatis JDBC

架构师训练营 - 第 3 周命题作业

红了哟

揭秘!中国人一定要知道的北斗卫星系统

程序那些事

北斗卫星 北斗系统 卫星定位 卫星授时 黑科技

玩转Java8中的 Stream 之从零认识 Stream

Java小咖秀

学习 stream java8 Java 面试 经验

那些会阻碍程序员成长的细节[3]

MavenTalker

程序员 职业规划 职业成长

架构师训练营 - 第 3 周学习总结

红了哟

Github仓库如何选择开源许可证

早睡蟒

GitHub 开源许可证 GitHub license

ARTS 第五周 6.21-6.28

我笔盒呢

势能造就下的互联网大厂程序员为什么去开滴滴了?

非著名程序员

程序员 程序人生 提升认知 程序员成长

serverless之一:入门

毛佳伟🐳

Serverless

mysql字符匹配批量修改

毛佳伟🐳

MySQL

Week2:作业二

车小勺的男神

ARTS 第四周 6.15-6.21

我笔盒呢

如何搭建一个Hadoop集群

Rayjun

大数据 hadoop

架构师训练营第四周作业

hiqian

iOS 动画 - 窗景篇(三·完结)

柯烂

ios swift 动画 移动互联网 动效

为什么建议你使用枚举?

王磊

Java 枚举

区块链≠分布式账本,别再傻傻分不清

CECBC区块链专委会

区块链技术 高考 信息防篡改

重学 Java 设计模式:实战中介者模式「按照Mybatis原理手写ORM框架,给JDBC方式操作数据库增加中介者场景」

小傅哥

设计模式 小傅哥 代码优化 代码规范 中介者模式

一群龙舟划手 “拍了拍” 你:端午节安康~

博睿数据

[深入理解Redis]读取RDB文件

老胡爱分享

redis 源码阅读

使用Flutter之后,我们的CPU占用率降了50%-InfoQ