docs/architecture/render/android/native-render.md
Flatten UI是一种终端视图层级优化的方法,在Hippy 2.0的基础上实现这种优化方案,并不是简单的改造渲染逻辑就可以实现的,这里还依赖整体UI结构调整,缓存池改造,以及细粒度复用等重构完成后才能支持Flatten UI最终优化的实现,所以Flatten UI在3.0可以理解为一整套解决方案的升级,而不是单一的局部优化。
在2.0中通过针对只参与排版的节点不创建实体view的方法来减少终端UI层级,在此基础上,3.0 Flatten UI采用子元素直接在父view上完成渲染的方法将UI层级进一步打平,通过下面这个demo我们可以看到不同阶段优化效果对比:
<View onClick={...}>
<View style={{margin: 10}}>
<Image {...} />
<Text {...}>hello world</Text>
</View>
</View>
在2.0中只针对预创建view,base64与本地图片的数据做了缓存,既没有抽象出统一的缓存访问接口,也无法做很好的扩展来支持其它流程的缓存优化,所以在3.0定义了一套标准的缓存访问接口,重构了所有缓存相关的代码实现结构,并扩展了基于view的复用池与常用object的复用池。
PreCreateViewPool
预创建view缓存池,当在dom线程接收到create node指令时,会创建对应的create render node task并放入队列中,等batch的时候把队列中的task放到UI线程批量执行,在此之前还需要执行排版相关的逻辑,为了让UI线程能提前处理一部分任务,当接收到create node指令时,就直接在UI线程创建对应的view并放到缓存池,等到UI 线程执行batch的时候就可以直接从缓存池取出使用,这样可以最大化UI线程和dom线程task的并行执行,减少整个流程的耗时该缓存池对于开发者来说是透明的,每次batch执行结束后就会清空,内部使用SparseArray<View>的数据结构进行存储,直接使用view id做key
RecycleViewPool
基于view type的view缓存池,主要使用在Recycler view滚动时候,recycler view item子view的回收,具体回收机制可以参考下一节细粒度复用的介绍该缓存池内部使用Map<String, SimplePool<View>>进行存储,view的class name作为key值,每个类型的view pool default pool size为8
ImageDataPool
Image data缓存池,该缓存池内部使用LruCache<Integer,ImageDataSupplier>进行存储,key值由image uri后32位做hashcode计算得出,当前设置的default pool size为24
RecycleObjectPool
常用频繁创建对象的缓存池,类似于系统Message的使用,使用完成后做recycle,获取的时候直接obtain from pool,避免临时对象的频繁创建和销毁带来GC问题,该缓存池内部使用Map<CustomObjectType, SimplePool<Object>>进行存储,当前设置的default pool size为12,缓存池定义为static对象,也就是所有Hippyengine和页面都共享同一个缓存池,目前VFS ResourceDataHolder与ImageDataHolder对象的使用已经接入,后续其它常用对象的创建接入会继续扩展
在2.0中主要通过recycler view自身item pool对ViewHolder进行缓存,这里的缓冲池分类主要依赖item type,也就是每个item的UI样式,由前端开发者自行分类定义,由于很多开发者对item type分类不精准甚至根本就没有去设置item type,再加上本身SDK的diff机制不够最优,导致这种强依赖item type的缓存复用机制效率很低,有概率会引起列表滚动的掉帧不流畅。
3.0增加以view为单位的缓存池,在list view滚动过程中不论是item被淘汰还是被复用,都会先拆解item,将子view放到复用池彻底解决同样type item内部由于子节点结构差异造成的复用率低的问题,取消Patch机制,整个复用diff过程不产生任何多余的临时对象。
增加基于view type缓存池的细粒度复用机制以后,已经最大程度上减弱的对item type分类的依赖,也就是说即使前端item type不设置,也可以获得较好的复用性能。细粒度复用可以很好优化列表滚动性能,同时也是保证Flatten UI的体系的一个必要能力,因为在滚动的候,下一个即将展示的节点有可能是一个偏平化的节点,不需要创建view,如果没有view的缓存池,那么上一个从item pool取出的item会把子view全部delete掉,造成很大的性能损耗。
当view被复用时,view被设置的属性还是之前关联node所带的属性,需要把新关联node属性重新设置到view上,才能保证组件显示和行为的正确性。
2.0 属性Diff与重置主要步骤:
3.0 属性Diff与重置主要步骤:
对比3.0和2.0的属性Diff与重置实现,3.0具有以下优势:
FlatViewGroup直接派生于系统ViewGroup,作为 text view,imge view与普通view组件的基类,相对于原来suppor-ui AsyncImageView要轻量化很多,只包含子节点渲染顺序相关的处理逻辑,所以基于FlatViewGroup派生的组件不论在创建耗时上还是代码可维护方面比2.0都有明显提升。 子节点遍历与渲染最关键要支持扁平化元素与常规view混合的组合,例如:
使用SDK内置以上三种基础组件可以支持扁平化渲染,其它组件或者宿主自定义组件暂时还不支持
只有作为叶子节点的元素才会被扁平化渲染,text节点一定是叶子节点,虽然text可以嵌套,但text子节点在终端做了合并,不会生成实际的Render node节点,只会有VirtualNode与其对应
这里主要针对宿主自定义View,SDK核心组件List view item,View pager item以及Scroll view的第一个子节点都是从view组件派生出来,它们下面的子节点是都可以满足这个条件的
View属性主要包括click,touch事件监听,focus相关事件以及transform属性