• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

Android开发ComposeUI如何解决布局嵌套原理解析

android 搞代码 3年前 (2022-03-01) 19次浏览 已收录 0个评论
文章目录[隐藏]

概述

  • 背景:布局层级过多、布局适度的嵌套会导致测量工夫呈指数级增长,最终影响布局性能。
  • 现状:Compose没有上述布局嵌套问题,因为它基本上解决了布局层级对布局性能的影响。
  • 解决原理:Compose界面只容许一次测量,即随着布局层级的加深,测量工夫仅线性增长。

本文将次要阐明:

  1. 布局嵌套过多如何影响布局性能?
  2. ComposeUI如何解决嵌套问题?
  3. 为什么ComposeUI能够只容许一次测量?
  4. ComposeUI测量过程的源码剖析

1. 布局嵌套过多如何影响布局性能?

起因次要是:ViewGroup会对子View进行屡次测量。假如:父布局的布局属性是wrap_content、子布局是match_parent,此时的布局过程是:

  1. 父布局先以0为强制宽度测量子View、而后持续测量剩下的其余子View
  2. 再用其余子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)}

汇总阐明:

  1. IntrinsicSize.Min其实也是个Modifier
  2. MinIntrinsicHeightModifier会在测量之间,先调用calculateContentConstraints计算束缚
  3. calculateContentConstraints中则会递归地调用子项的minIntrinsicHeight,并找出最大值,这样父项的高度就确定了
  4. 固有个性测量实现后,再调用measurable.measure,开始真正的递归测量

至此,对于Compose UI解决布局嵌套层级问题及其原理解说结束。

最初

给大家分享我收集的Android源码解析学习材料,心愿对你有用,期待与大家一起提高

1.深刻解析微信 MMKV 源码

2.深刻解析阿里巴巴路由框架 ARouter
源码

3.深刻解析 AsyncTask 源码(一款
Android 内置的异步工作执行库)

4.深刻解析 Volley 源码(一款 Google
推出的网络申请框架)

5.深刻解析 Retrofit 源码

6.深刻解析 OkHttp 源码

因为文章篇幅无限,内容细节比拟多,须要学习材料的敌人能够点击这里收费获取哦!


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Android开发ComposeUI如何解决布局嵌套原理解析

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址