概述
- 背景:布局层级过多、布局适度的嵌套会导致测量工夫呈指数级增长,最终影响布局性能。
- 现状:Compose没有上述布局嵌套问题,因为它基本上解决了布局层级对布局性能的影响。
- 解决原理:Compose界面只容许一次测量,即随着布局层级的加深,测量工夫仅线性增长。
本文将次要阐明:
- 布局嵌套过多如何影响布局性能?
- ComposeUI如何解决嵌套问题?
- 为什么ComposeUI能够只容许一次测量?
- ComposeUI测量过程的源码剖析
1. 布局嵌套过多如何影响布局性能?
起因次要是:ViewGroup会对子View进行屡次测量。假如:父布局的布局属性是wrap_content、子布局是match_parent,此时的布局过程是:
- 父布局先以0为强制宽度测量子View、而后持续测量剩下的其余子View
- 再用其余子View里最宽的宽度,二次测量这个match_parent的子 View,最终得出它的尺寸,并把这个宽度作为本人最终的宽度。
即 这个过程就对单个子View进行了二次测量。
「而布局嵌套对性能影响则是指数模式的」,即:父布局会对每个子view做两次测量,子view也会对上面的子view进行两次测量,即相当于是 O(2ⁿ)测量。
2. ComposeUI如何解决嵌套问题?
ComposeUI规定:只容许一次测量,不容许反复测量。即每个父布局只对每个子组件测量一次,即测量复杂度变成了:O(n)。
3. 为什么ComposeUI能够只容许一次测量?
ComposeUI引入了:固有个性测量(Intrinsic Measurement)。即 Compose 容许父组件在对子组件测量前先测量子组件的“固有尺寸”,这相当于下面说的两次测量的 第一次 “粗略测量“。
而这种固定个性测量是对整个组件布局树进行一次测量即可,从而防止了随着层级的加深而减少测量次数。
4. ComposeUI测量过程的源码剖析
此处次要剖析:固定个性测量的测量过程。此处先介绍LayoutNodeWrapper链构建
4.1 LayoutNodeWrapper
先来看两个外围论断:
- 子View都是以LayoutNode的模式,存在于Parent – children中的
- 给Layout的设置的modifier会以LayoutNodeWrapper链的模式存储在LayoutNode中,而后后续做相应变换
上面阐明LayoutNodeWrapper的构建:
- 默认的LayoutNodeWrapper链即由LayoutNode , OuterMeasurablePlaceable, InnerPlaceable 组成
- 当增加了modifier时,LayoutNodeWrapper链会更新,modifier会作为一个结点插入到其中
internal val innerLayoutNodeWrapper: LayoutNodeWrapper = InnerPlaceable(this)private val outerMeasurablePlaceable = OuterMeasurablePlaceable(this, innerLayoutNodeWrapper)override fun measure(constraints: Constraints) = outerMeasurablePlaceable.measure(constraints)override var modifier: Modifier = Modifier set(value) { // …… code field = value // …… code // 创立新的 LayoutNodeWrappers 链 // foldOut 相当于遍历 modifier val outerWrapper = modifier.foldOut(innerLayoutNodeWrapper) { mod /*📍 modifier*/ , toWrap -> var wrapper = toWrap if (mod is OnGloballyPositionedModifier) { onPositionedCallbacks += mod } if (mod is RemeasurementModifier) { mod.onRemeasurementAvailable(this) } val delegate = reuseLayoutNodeWrapper(mod, toWrap) if (delegate != null) { wrapper = delegate } else { // …… 省略了一些 Modifier判断 if (mod is KeyInputModifier) { wrapper = ModifiedKeyInputNode(wrapper, mod).assignChained(toWrap) } if (mod is PointerInputModifier) { wrapper = PointerInputDelegatingWrapper(wrapper, mod).assignChained(toWrap) } if (mod is NestedScrollModifier) { wrapper = NestedScrollDelegatingWrapper(wrapper, mod).assignChained(toWrap) } // 布局相干的 Modifier if (mod is LayoutModifier) { wrapper = ModifiedLayoutNode(wrapper, mod).assignChained(toWrap) } if (mod is ParentDataModifier) { wrapper = ModifiedParentDataNode(wrapper, mod).assignChained(toWrap) } } wrapper } outerWrapper.wrappedBy = parent?.innerLayoutNodeWrapper outerMeasurablePlaceable.outerWrapper = outerWrapper …… }// 假如:给Layout设置一些modifierModifier.size(100.dp).padding(10.dp).background(Color.Blue)
对应的LayoutNodeWrapper链如下图所示
<figcaption style=”margin: 5px 0px 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; text-align: center; font-size: 13px;”>image.png</figcaption>
这样始终链式调用下一个的measure,直到最初一个结点InnerPlaceable,最终调用到了自定义Layout时写的measure()
4.2 固有个性测量-实现原理
论断:LayoutNodeWrapper链中插入了一个Modifier
@Stablefun Modifier.height(intrinsicSize: IntrinsicSize) = when (intrinsicSize) { IntrinsicSize.Min -> this.then(MinIntrinsicHeightModifier) IntrinsicSize.Max -> this.then(MaxIntrinsicHeightModifier)}private object MinIntrinsicHeightModifier : IntrinsicSizeModifier { override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { //正式测量前先依据固有个性测量取得一个束缚 val contentConstraints = calculateContentConstraints(measurable, constraints) //正式测量 val placeable = measurable.measure( if (enforceIncoming) constraints.constrain(contentConstraints) else contentConstraints ) return layout(placeable.width, placeable.height) { placeable.placeRelative(IntOffset.Zero) } } override fun MeasureScope.calculateContentConstraints( measurable: Measurable, constraints: Constraints ): Constraints { val height = measurable.minIntrinsicHeight(constraints.maxWidth) return Constraints.fixedHeight(height) } override fun IntrinsicMeasureScope.maxIntrinsicHeight( measurable: IntrinsicMeasurable, width: Int ) = measurable.minIntrinsicHeight(width)}
汇总阐明:
- IntrinsicSize.Min其实也是个Modifier
- MinIntrinsicHeightModifier会在测量之间,先调用calculateContentConstraints计算束缚
- calculateContentConstraints中则会递归地调用子项的minIntrinsicHeight,并找出最大值,这样父项的高度就确定了
- 固有个性测量实现后,再调用measurable.measure,开始真正的递归测量
至此,对于Compose UI解决布局嵌套层级问题及其原理解说结束。
最初
给大家分享我收集的Android源码解析学习材料,心愿对你有用,期待与大家一起提高
1.深刻解析微信 MMKV 源码
2.深刻解析阿里巴巴路由框架 ARouter
源码
3.深刻解析 AsyncTask 源码(一款
Android 内置的异步工作执行库)
4.深刻解析 Volley 源码(一款 Google
推出的网络申请框架)
5.深刻解析 Retrofit 源码
6.深刻解析 OkHttp 源码
因为文章篇幅无限,内容细节比拟多,须要学习材料的敌人能够点击这里收费获取哦!