本篇文章里,和大家简单交流一下Flutter动态化中二级缓存的设计思路。
我们在之前的文章Flutter 动态化热更新的思考与实践(六)---- 动态列表滚动优化 中给出了整个动态渲染的流程图,其中就有本地cache的部分,只是当时没有讲其中的细节。本地cache就是一个二级缓存的设计,包含了两部分缓存:内存和数据库 ,只不过两部分缓存的数据不太一样,这个是和通常的二级缓存概念有点出入的地方,接下来会详细说明。
缓存的流程
以上流程图展示了二级缓存的整体流程,下面着重介绍下两个技术细节:
- 缓存的数据结构
- 内存缓存策略
1. 缓存数据结构
我们定义的缓存数据结构如下:
class AstCache {
//ast 数据唯一id
String astId;
//ast 内容的Hash值
String hash;
//ast 数据
Map ast;
}
逐一说明下:
- astId: ast id 的生成算法是对代码文件在项目中的相对路径与注解1声明的类名或全局方法的函数名做md5转换:
astId = md5({代码文件相对路径}+{类名或全局方法的函数名}),这样得到的ast id 基本上是项目范围内的唯一值。如果代码文件转移了目录位置或更改了类名或全局方法名,对应的ast id 都会有变化。 - hash:对注解1声明的代码中内容做的
md5 hash,如果代码有了修改变化,那么生成的hash也一定会变,根据此字段来判断ast 数据版本的一致性。 - ast:Ast 具体的数据,由于都是
json数据结构,所以直接可以转换成Map来使用。
2. 内存缓存策略
前面提到内存中缓存的数据和数据库的数据是不一样的,内存中缓存的是Widget对象,数据库中缓存的是Ast信息,其中的原因是进入动态化页面中最耗时的部分是Runtime动态解析Ast的过程,在一些稍微低端的老旧设备上,这一时长竟可以将近3s,所以如果只是把Ast数据缓存到内存中是没有多大意义的,我们使用内存缓存的目的是为了下一次更快的加载,所以只能是将第一次构建好的Widget缓存到内存中,再一次进入页面的时候,就可以将缓存的Widget直接渲染出来,减少等待的时间。
我们的内存缓存是以队列来存储的,这个队列会有如下规则:
- 队尾存放最近一次访问的元素;
- 队首存放容器大小范围内最久一次访问的元素;
在此规则之上,我们对内存缓存的操作就会做如下处理:
- 如果当前访问的Widget已经在缓存队列中,则取出
Widget对象的同时,将该Widget移至队尾; - 如果当前访问的Widget没有在缓存队列中,那么从数据库缓存或服务器中获取Ast构建
Widget对象,并将该对象添加至内存缓存队列队尾; - 如果内存缓存队列已满,则移除队首元素,并将新的
Widget对象添加至队尾;
这样设计的目的是为了保证高频次访问的页面能始终在内存缓存中,对用户的使用体验会是比较友好的。
3. 二级缓存带来的内存占用
我直接拿上一篇文章中的“消息列表”页面来看一下增加了二级缓存后内存的占用情况(运行模式为Profile)。
未缓存Widget 之前内存情况:
缓存Widget之后内存情况:
通过上面DevTools对比可以看到,未缓存之前的内存占用70M左右,缓存后内存占用86M左右,多了16M左右的占用空间,并且这还只是一个页面Widget的缓存占用的空间,还是不太理想的,我们目前定义内存缓存容器大小为10个页面,这样的话如果都占满会是多出来200M左右的内存占用,还是挺多的,好在现在手机的运存也比较大,动辄8G、12G的内存。。。不过对程序员来说代码性能的极致是永恒的追求(^ ^),后面如果找到优化的方法还会和大家分享(^ ^)。
写在最后
本篇文章应该是《Flutter动态化热更新的思考与实践》系列最后一篇了,至此我们实现整个Flutter动态化的技术要点都已经分享给了大家,回过头来再看整个方案的话,我个人觉得不完美的地方还是性能方面,虽然我们做了一些工作来缩小用户体验上与原生的差距,但是这个方案的天然技术瓶颈很难突破:Runtime是在单线程中执行,和UI是在一个线程中,如果遇到复杂的业务处理,可能会影响交互的体验,现在就有一个小问题,就是CircularProgressIndicator组件的动画不是很顺畅,在Loading完成将要渲染的最后阶段,这个圈圈动画基本上会卡住。单线程的限制也是无可奈何之举,因为在Flutter的设计里,是不允许多线程共享内存的,所以多线程间的数据传输只能是基本数据类型,而针对Widget这样的对象是无法传输的,这样的话如果在多线程中执行Runtime,那么解析出来的Widget对象就无法传给UI线程渲染。后面我们也会持续思考这个问题,尝试设计一些架构模式来支持Runtime多线程执行,有结果的话会回来再和大家分享~
明天就是除夕了,最后的最后给大家拜个早年:祝大家春节快乐,新一年牛气冲天🐂! (^_^)v
再来一个最后,发个我司的招聘信息,感兴趣的小伙伴让你们的简历飞过来吧~