写在最后

在技术领域内,没有任何一门课程可以让你学完后一劳永逸,再好的课程也只能是“师傅领进门,修行靠个人”。“学无止境”这句话,在任何技术领域,都不只是良好的习惯,更是程序员和工程师们不被时代淘汰、获得更好机会和发展的必要前提。

如果你觉得自己学习效率低,缺乏正确的指导,可以一起学习交流!

加入我们吧!群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

好像还差点还有个update 还是没有摊平

inline class SkippableUpdater constructor(

@PublishedApi internal val composer: Composer

) {

inline fun update(block: Updater.() -> Unit) {

composer.startReplaceableGroup(0x1e65194f)

Updater(composer).block()

composer.endReplaceableGroup()

}

}

//结合SkippableUpdater 的update 函数,skippableUpdater.update { set(materialized, ComposeUiNode.SetModifier) }

// <=>等价于👇

composer.startReplaceableGroup(0x1e65194f)

// 2️⃣ 📍 modifier 最终传给了这个 set 方法

Updater(currentComposer).set(materialized, ComposeUiNode.SetModifier)

composer.endReplaceableGroup()

代码 2️⃣ 这里是调用了一个 set 方法,直接看有点晕,调来调用去,分析起来太多了,不能跑偏了,直接说重点。

companion object {

val Constructor: () -> ComposeUiNode = LayoutNode.Constructor

//ComposeUiNode.SetModifier

val SetModifier: ComposeUiNode.(Modifier) -> Unit = { this.modifier = it }

}

// ComposeUiNode.SetModifier 也是个函数类型,调用 set(materialized, ComposeUiNode.SetModifier)

//最终会触发SetModifier 函数的执行也就是

this.modifier=materialized //📍 modifier

// 👆 this是LayoutNode 对象 是通过触发 ComposeUiNode.Constructor创建的

关于从 set(materialized, ComposeUiNode.SetModifier) 是如何到触发 SetModifier 函数的,这里我就分析了,可以通过 debug 很容易验证这一结论。如果你真的想去分析如何执行的话,分析之前建议先看一下 深入详解 Jetpack Compose | 实现原理 这篇文章。(友情提醒,如何真要分析这段别陷进去了,别忘记我们看源码的目的。)

通过上面的分析,我们追踪的 modifier 被赋值给了 LayoutNode 成员的 modifier ,这种是个赋值语句,在 kotlin 相当于调用的成员变量的set 方法 LayoutNode.kt

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

// 代码 0️⃣

outerMeasurablePlaceable.outerWrapper = outerWrapper // 👈 📍 modifier

……

}

**👆 代码片段-1 **

上述代码主要是将Modifier 链转换LayoutNodeWrapper 链的过程,通过Modifier 的 foldOut 函数 遍历Modifier 链上的所有元素,并根据不同的Modifier 创建不同的 LayoutNodeWrapper。关于Modifier 的foldOut 函数的作用不懂的可以看我之前写的 Modifier源码,Kotlin高阶函数用的真6 这篇文章。

在上面的代码中根据 Modifier 类型创建不同的 LayoutNodeWrapper,这些不同的 Modifier 都是 Modifier.Element 的直接实现类或接口,如 KeyInputModifier、PointerInputModifier、LayoutModifier 等。上面代码都是 if 判断,没有else,也就是说如果 Modifier 不在这些类别范围内就没法创建对应的LayoutNodeWrapper,也就相等于我们设置的 Modifier 没有用。所以我自定义Modifer 一定要在这个类型范围内,否则是没有用的。在JetPack Compose 内置的Modifier.Element 子类或接口如下。(Tips. Android studio 查看类的继承关系 菜单栏Navgate-> Type Hierarchy ; 快捷键 Ctrl+H )

换个思路继续跟踪


上面分析到那里路好像断了, 没法继续了。思考一下这里只是分析了Layout 函数执行时,只是初始化的准备工作。它的大小和位置如果确认等操作这里似乎没有执行。我们刚才是把 ParentLayout 当做父容器来看待的,父容器一般是管理自己的 children 的大小和位置,换一种思路,ParentLayout 出来做父容器,它也可以作为 child 呀,如下面代码情况。

setContent {

ParentLayout{

Box() {}

// 👇 可以看做是 上面 ParentLayout 的 child,也可以看做是下面 ChildLayout 的父容器

ParentLayout(

Modifier

.size(100.dp)

.padding(10.dp)

.background(Color.Blue)

) {

ChildLayout(Modifier.size(100.dp)) {}

}

}

}

下面就从 ParentLayout 布局作为 child 的时候来分析一下,如果作为 child 那么分析入口就应该它从的父容器 MeasurePolicy 的 measure 函数开始分析了。

val measurePolicy = MeasurePolicy { measurables, constraints ->

val placeables = measurables.map { child ->

//代码 0️⃣

child.measure(constraints)

}

……

}

代码 0️⃣ 进行调用 child 的测量方法,从函数参数来看,只知道 child 是个 Measurable 的类型,但 Measurable 是个接口,我们需要知道 child 具体是 Measurable 那个实现类,我们才好分析 measure 函数的逻辑

👆 图片-0

通过debug 的方式,可以看出 child 是 LayoutNode 对象(为什么是LayoutNode 下面分析就知道了),那么就去看看 LayoutNode 的measure函数。

LayoutNode.kt → measure 函数

override fun measure(constraints: Constraints) =

outerMeasurablePlaceable.measure(constraints)

LayoutNode 的 measure 调用了 outerMeasurablePlaceable 的 measure 函数,这个 outerMeasurablePlaceable **代码片段-1 代码 0️⃣ **也出现了outerMeasurablePlaceable.outerWrapper = outerWrapper // 👈 📍 modifier 而且这个outerMeasurablePlaceable 的属性 outerWrapper 就包含 modifier 信息。我们又找到了 modifier 的藏身之处,好像又找到些线索。我们继续跟踪代码吧。

LayoutNodeWrapper 链中的测量流程分析⛓


OuterMeasurablePlaceable.kt

override fun measure(constraints: Constraints): Placeable {

……

remeasure(constraints)

return this

}

fun remeasure(constraints: Constraints): Boolean {

val owner = layoutNode.requireOwner()

……

if (layoutNode.layoutState == LayoutState.NeedsRemeasure ||

measurementConstraints != constraints

) {

measuredOnce = true

layoutNode.layoutState = LayoutState.Measuring

measurementConstraints = constraints

val outerWrapperPreviousMeasuredSize = outerWrapper.size

owner.snapshotObserver.observeMeasureSnapshotReads(layoutNode) {

outerWrapper.measure(constraints)// 0️⃣ 👈 📍 modifier

}

layoutNode.layoutState = LayoutState.NeedsRelayout

……

return sizeChanged

}

return false

}

👆 代码片段-2 代码 0️⃣ 处 我们看到包含 modifier 信息的 outerWrapper 调用了 它的 measure 方法。outerWrapper 是 LayoutNodeWrapper 类型的,它就是在代码片段1 处根据不同 Modifer 创建的 LayoutNodeWrapper 链。我们给 ParentLayout 的 Modifer 设置为 Modifier.size(100.dp).padding(10.dp).background(Color.Blue) 。那么对应的LayoutNodeWrapper 链如下图所示

Jetpack compose 测量流程-LayoutNodeWrapper链 (3)

👆 图-1

由 图-1 知代码片段-2 处的代码outerWrapper 为 ModifiedLayoutNode 类型。 ModifiedLayoutNode

internal class ModifiedLayoutNode(

wrapped: LayoutNodeWrapper,

modifier: LayoutModifier

) : DelegatingLayoutNodeWrapper(wrapped, modifier) {

override fun measure(constraints: Constraints): Placeable = performingMeasure(constraints) {

with(modifier) { 👈 📍 modifier

measureResult = measureScope.measure(wrapped, constraints)

this@ModifiedLayoutNode

}

}

protected inline fun performingMeasure(constraints: Constraints, block: () -> Placeable

): Placeable {

measurementConstraints = constraints

val result = block()

layer?.resize(measuredSize)

return result

}

……

}

由 图-1 知代码此时的 modifier 为 SizeModifier 类型 LayoutModifier.kt

interface LayoutModifier : Modifier.Element {

fun MeasureScope.measure(

measurable: Measurable,/下一个 LayoutNodeWrapper 节点/

constraints: Constraints/* 来着父容器或者来着上一个节点的约束 */

): MeasureResult

}

SizeModifier.kt

private class SizeModifier(

private val minWidth: Dp = Dp.Unspecified,

private val minHeight: Dp = Dp.Unspecified,

private val maxWidth: Dp = Dp.Unspecified,

private val maxHeight: Dp = Dp.Unspecified,

private val enforceIncoming: Boolean,

inspectorInfo: InspectorInfo.() -> Unit

) : LayoutModifier, InspectorValueInfo(inspectorInfo) {

private val Density.targetConstraints: Constraints

get() {/更加我们指定的大小生成对应的约束/}

override fun MeasureScope.measure(

measurable: Measurable,/下一个LayoutNodeWrapper/

constraints: Constraints/* 来着父容器或者来着上一个节点的约束 */

): MeasureResult {

val wrappedConstraints = targetConstraints.let { targetConstraints ->

if (enforceIncoming) {//当我们给控件指定大小时,这个值就为true

//结合父容器或者上一个节点的约束 和我们指定约束进行结合生成一个新的约束

constraints.constrain(targetConstraints)

} else {

……

}

}

//代码 0️⃣ 进行下一个 LayoutNodeWrapper 节点测量

val placeable = measurable.measure(wrappedConstraints)

//所有节点测量完,开始放置

return layout(placeable.width, placeable.height) {

placeable.placeRelative(0, 0)

}

}

代码 0️⃣ 继续进行下一个 LayoutNodeWrapper 节点的测量,一直到最后 InnerPlaceable 节点。

class LayoutNode{

internal val innerLayoutNodeWrapper: LayoutNodeWrapper = InnerPlaceable(this)

private val outerMeasurablePlaceable = OuterMeasurablePlaceable(this, innerLayoutNodeWrapper)

……

internal val children: List get() = _children.asMutableList()

}

InnerPlaceable

class InnerPlaceable{

override fun measure(constraints: Constraints): Placeable = performingMeasure(constraints) {

val measureResult = with(layoutNode.measurePolicy) {

// 这里就是Layout 的MeasurePolicy 的measure 执行的地方了

layoutNode.measureScope.measure(layoutNode.children, constraints)

}

layoutNode.handleMeasureResult(measureResult)

return this

}

}

这里就是 Layout 的 MeasurePolicy 的measure 执行的地方了,然后children 继续执行上述流程了如下图所示,这样“MeasurePolicy 接口的 measure 方法是怎么调用的?他的参数值是怎么来的呢?”这个问题也就解答了。

分析解答问题


通过上面的分析,我们大致可以回答“布局中的测量流程是什么样的?” 这个问题了。**

1 准备阶段:

child 在父容器在声明时,也就是调用了 Layout 函数,进行初始化准备操作,记录这个child 测测量策略,这个child 的children 等,根据设置的 Modifier 链创建对应的 LayoutNodeWrapper 链。

2 测量阶段

child 在父容器的测量策略 MeasurePolicy 的 measure 函数中执行 child 的 measure 函数。接着按照准备好的 LayoutNodeWrapper 链一步步的执行各个节点的 measure 函数,最终走到 InnerPlaceable 的 measure 函数,在这个又会继续它的 children 进行测量,此时它的 children 就会和它一样进行执行上述流程,一直到所有children 测量完成。 用下面这张图总结一下上述流程。

Jetpack compose 测量流程-测量流程 (1)

👆 图-2 测量流程图

还有一个最后一个问题 ParentLayout 中 通过 modifier 设置大小是如何起到作用的 ? 答:我们通过 Modifer.size() 函数 构建了一个SizeModifer 对象

fun Modifier.size(size: Dp) = this.then(

SizeModifier(

minWidth = size,maxWidth = size,

minHeight = size, maxHeight = size,

enforceIncoming = true,…

)

)

通过上面的分析我们知道,在测量流程中SizeModifer 的measure 函数会触发

SizeModifer部分源码

private class SizeModifier(

private val minWidth: Dp = Dp.Unspecified,

private val minHeight: Dp = Dp.Unspecified,

private val maxWidth: Dp = Dp.Unspecified,

private val maxHeight: Dp = Dp.Unspecified,

private val enforceIncoming: Boolean,

inspectorInfo: InspectorInfo.() -> Unit

) : LayoutModifier, InspectorValueInfo(inspectorInfo) {

// 0️⃣ 根据我们设置的大小生成一个约束对象

private val Density.targetConstraints: Constraints

get() {

//控制 maxWidth值范围

val maxWidth = if (maxWidth != Dp.Unspecified) {

maxWidth.coerceAtLeast(0.dp).roundToPx()

} else {

Constraints.Infinity

}

//maxHeight、minWidth、minHeight 也类似处理一下值的范围,防止我们瞎捣乱

…code…

return Constraints(

minWidth = minWidth,

minHeight = minHeight,

maxWidth = maxWidth,

maxHeight = maxHeight

)

}

override fun MeasureScope.measure(

measurable: Measurable,//LayoutNodeWrapper链上的下一个节点的

constraints: Constraints//父容器或者是LayoutNodeWrapper链上的上一个节点的constraints

): MeasureResult {

val wrappedConstraints = targetConstraints.let { targetConstraints ->

if (enforceIncoming) {

//1️⃣ 和我们设置的大小继续比较计算,得出一个新的Constraints对象

// constrain 函数作用是,在constraints范围内,得到一个尽可能满足targetConstraints的大小范围的约束

constraints.constrain(targetConstraints)

} else {

//暂不讨论

}

//2️⃣ 使用新的约束继续下一个测量

val placeable = measurable.measure(wrappedConstraints)

题外话

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多程序员朋友无法获得正确的资料得到学习提升,故此将并将重要的Android进阶资料包括自定义view、性能优化、MVC与MVP与MVVM三大框架的区别、NDK技术、阿里面试题精编汇总、常见源码分析等学习资料。

【Android思维脑图(技能树)】

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

希望我能够用我的力量帮助更多迷茫、困惑的朋友们,帮助大家在IT道路上学习和发展~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ts)

} else {

//暂不讨论

}

//2️⃣ 使用新的约束继续下一个测量

val placeable = measurable.measure(wrappedConstraints)

题外话

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多程序员朋友无法获得正确的资料得到学习提升,故此将并将重要的Android进阶资料包括自定义view、性能优化、MVC与MVP与MVVM三大框架的区别、NDK技术、阿里面试题精编汇总、常见源码分析等学习资料。

【Android思维脑图(技能树)】

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

[外链图片转存中…(img-5yvFDCor-1715122366446)]

希望我能够用我的力量帮助更多迷茫、困惑的朋友们,帮助大家在IT道路上学习和发展~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐