本文是我去年首发于稀土掘金平台的文章
全文较长:共1万5千字,适合有耐心❤️的人学习
有些概念不懂的可以去4.部分概念详解这个目录先稍微学习一下Compose源码基于最新的Compose 版本:1.0.1
系统源码基于最新的Android11 版本
注意: 后续Compose版本升级之后,有可能内部代码会稍微有些不同,但是原理都是差不多的。
因文章字数上限10万字符,所以有部分代码换成了图片
1.创建一个简单示例
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text("测试文本")
}
}
2.源码分析准备工作
为了避免其他内容干扰,只在Activity里面创建一个文本内容
先看上面的示例代码用到的类
setContent(): 设置Activity的根视图,首次会调用 Activity.setContentView()
@Composable Text():显示文本并提供语义/可访问性信息的元素
//代码来自ComponentActivity.kt
//implementation 'androidx.activity:activity-compose:1.3.0-alpha08'
fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit
)
3.源码分析
3.1-示例入口
setContent {
Text("测试文本")
}
3.2-Activity.setContent
我们先看看 初始化ComposeView(this)
3.3-SnapshotState和ComposeView
//代码来自ComposeView.android.kt
class ComposeView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr) {
//使用的是 androidx.compose.runtime.SnapshotStateKt#mutableStateOf()
private val content = mutableStateOf<(@Composable () -> Unit)?>(null)
@Composable
override fun Content() {
//请找到目录3.7,再打开子目录13.ComposeView#content里面有这段代码的分析
content.value?.invoke()
}
fun setContent(content: @Composable () -> Unit) {
this.content.value = content
....
}
....
}
接着看一下这段代码content = mutableStateOf(null)
执行到下面:
//代码来自SnapshotState.kt
fun <T> mutableStateOf(
value: T,
//处理快照变更的策略
policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()
): MutableState<T> = createSnapshotMutableState(value, policy)
internal expect fun <T> createSnapshotMutableState(
value: T,
policy: SnapshotMutationPolicy<T>
): SnapshotMutableState<T>
createSnapshotMutableState
调用后,最终执行到:
//代码来自ActualAndroid.android.kt
internal actual fun <T> createSnapshotMutableState(
value: T,
policy: SnapshotMutationPolicy<T>
): SnapshotMutableState<T> = ParcelableSnapshotMutableState(value, policy)
大家把目光往上移动一下
我们发现 createSnapshotMutableState() 返回类型都是:SnapshotMutableState
ParcelableSnapshotMutableState,继承 SnapshotMutableStateImpl
我们再看一下:SnapshotMutableStateImpl,继承 SnapshotMutableState
//代码来自SnapshotState.kt
internal open class SnapshotMutableStateImpl<T>(
value: T,
override val policy: SnapshotMutationPolicy<T>
) : StateObject, SnapshotMutableState<T> {
override var value: T
get() = next.readable(this).value
set(value) = next.withCurrent {
//比较状态值是否相等,不相等则更新状态值
if (!policy.equivalent(it.value, value)) {
//覆盖写入状态,最终调用notifyWrite()
next.overwritable(this, it) { this.value = value }
//notifyWrite() 内部调用【写观察者 writeObserver,可能是null】
//snapshot.writeObserver?.invoke(state)
}
}
....
}
internal inline fun <T : StateRecord, R> T.overwritable(
state: StateObject,
candidate: T,
block: T.() -> R
): R {
var snapshot: Snapshot = snapshotInitializer
return sync {
snapshot = Snapshot.current
this.overwritableRecord(state, snapshot, candidate).block()
}.also {
notifyWrite(snapshot, state)
}
}
看到这里,我相信大部分刚刚接触这方面知识的应该还是不太懂,没关系,继续往下看
刚刚上面提到【快照的写观察者】(可能为null),那么在哪儿执行到这里呢?
答案就在下面: 文章最上面我们从源码中得知:ComposeView内部有个setContent
//代码来自ComposeView.android.kt
fun setContent(content: @Composable () -> Unit) {
this.content.value = content
....
}
这个时候,会执行到下面,往上翻代码可以查看更多代码:
//代码来自SnapshotState.kt
internal open class SnapshotMutableStateImpl<T>(
value: T,
override val policy: SnapshotMutationPolicy<T>
) : StateObject, SnapshotMutableState<T> {
override var value: T
set(value) = next.withCurrent {
....
}
....
}
🤔为什么会执行到这里? 看一下下面的伪代码顺序:
ComposeView.setContent(){....}
this.content.value = content 中的 content是成员变量
val content = mutableStateOf<(@Composable () -> Unit)?>(null)
mutableStateOf = ParcelableSnapshotMutableState()
ParcelableSnapshotMutableState 继承 SnapshotMutableStateImpl
当this.content.value = content执行之后,会调用到:
SnapshotMutableStateImpl类的set(value)
看到这里应该清楚是怎么回事了吧,接下来继续看:3.2 setContent做了什么事情?
没有分析完的代码。
3.4-Activity.setContentView
我们上面分析完ComposeView.setContent
,接着分析ComponentActivity.setContentView()
由于内部执行了getWindow().setContentView()
,所以会触发ComposeView
的onAttachedToWindow()
//代码来自ComposeView.android.kt
override fun onAttachedToWindow() {
super.onAttachedToWindow()
previousAttachedWindowToken = windowToken
if (shouldCreateCompositionOnAttachedToWindow) {
ensureCompositionCreated()
}
}
我们来看看ensureCompositionCreated()
里面干了什么:
//代码来自ComposeView.android.kt
private fun ensureCompositionCreated() {
if (composition == null) {
try {
//设置当前正在创建compose content,后面用于checkAddView()判断
creatingComposition = true
composition = setContent(resolveParentCompositionContext()) {
Content()
}
} finally {
//compose content创建完成
creatingComposition = false
}
}
}
继续分析,上面代码的resolveParentCompositionContext()
//代码来自ComposeView.android.kt
private fun resolveParentCompositionContext() = parentContext
?: findViewTreeCompositionContext()?.also { cachedViewTreeCompositionContext = it }
?: cachedViewTreeCompositionContext
?: windowRecomposer.also { cachedViewTreeCompositionContext = it }
如果parentContext为空,则去查找组合树上下文,没有找到则使用缓存记录的组合树上下文,缓存中也没有,则延迟创建一个Recomposer并记录到缓存中。
3.5-Recomposer如何延迟创建?
//代码来自WindowRecomposer.android.kt
internal val View.windowRecomposer: Recomposer
get() {
val rootView = contentChild
return when (val rootParentRef = rootView.compositionContext) {
null -> WindowRecomposerPolicy.createAndInstallWindowRecomposer(rootView)
is Recomposer -> rootParentRef
else -> error("root viewTreeParentCompositionContext is not a Recomposer")
}
}
延迟创建Recomposer的入口方法createAndInstallWindowRecomposer()
//代码来自WindowRecomposer.android.kt
internal fun createAndInstallWindowRecomposer(rootView: View): Recomposer {
val newRecomposer = factory.get().createRecomposer(rootView)
rootView.compositionContext = newRecomposer
//省略了部分代码,视图分离之后,取消设置,移除Listener防止内存泄漏
return newRecomposer
}
我们先看上面代码的第一行:val newRecomposer = factory.get().createRecomposer(rootView)
factory是用AtomicReference修饰的WindowRecomposerFactory
//代码来自WindowRecomposer.android.kt
private val factory = AtomicReference<WindowRecomposerFactory>(
WindowRecomposerFactory.LifecycleAware
)
//初始化WindowRecomposerFactory
val LifecycleAware: WindowRecomposerFactory = WindowRecomposerFactory { rootView ->
rootView.createLifecycleAwareViewTreeRecomposer()
}
继续看createLifecycleAwareViewTreeRecomposer()
,如何延迟创建Recomposer的,请看下方:
创建一个Recomposer(),在Lifecycle.Event.ON_CREATE这个Event事件中,使用协程,在协程内部执行:recomposer.runRecomposeAndApplyChanges(),我们看到UNDISPATCHED这样的启动模式就说明,需要立即在当前线程执行协程体。
runRecomposeAndApplyChanges()
:等待任何关联的Composer失效并重新组合,如果重新组合成功,则将它们的更改应用于其关联的组合。
runRecomposeAndApplyChanges()
分析前,先看一下recompositionRunner
//代码来自Recomposer.kt
suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock ->
....
}
先看一下recompositionRunner
里面做了什么:
//代码来自Recomposer.kt
private suspend fun recompositionRunner(
block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit) {
withContext(broadcastFrameClock) {
....
val unregisterApplyObserver = Snapshot.registerApplyObserver{...}
...
try {
synchronized(stateLock) {
//knownCompositions在Recomposer#composeInitial里面添加的
//it.invalidateAll()将会触发CompositionImpl#invalidateAll
knownCompositions.fastForEach { it.invalidateAll() }
}
coroutineScope {
block(parentFrameClock)
}
} finally {
unregisterApplyObserver.dispose()
...
}
}
}
Snapshot.registerApplyObserver
注册快照观察者
fun registerApplyObserver(observer: (Set<Any>, Snapshot) -> Unit): ObserverHandle {
advanceGlobalSnapshot(emptyLambda)
....
}
private fun <T> advanceGlobalSnapshot(block: (invalid: SnapshotIdSet) -> T): T {
val previousGlobalSnapshot = currentGlobalSnapshot.get()
val result = sync {
//获取新的全局快照
takeNewGlobalSnapshot(previousGlobalSnapshot, block)
}
//上一个全局快照有状态修改,则通知已经注册的观察者
val modified = previousGlobalSnapshot.modified
if (modified != null) {
val observers: List<(Set<Any>, Snapshot) -> Unit> = sync { applyObservers.toMutableList() }
observers.fastForEach { observer ->
//此方法将执行到SnapshotStateObserver的applyObserver: (Set<Any>, Snapshot)-> Unit = { applied, _ -> ...}
observer(modified, previousGlobalSnapshot)
}
}
....
return result
}
上面刚刚分析完recompositionRunner
,我们再回来接着分析runRecomposeAndApplyChanges()
//代码来自Recomposer.kt
suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock ->
....
while (shouldKeepRecomposing) {
awaitWorkAvailable()
if (
synchronized(stateLock) {
if (!hasFrameWorkLocked) {
recordComposerModificationsLocked()
!hasFrameWorkLocked
} else false
}
) continue
....
}
}
执行到awaitWorkAvailable()
此时会挂起阻塞,接下来会执行了哪些?
不知道大家有没有记起来,我们上面在分析createLifecycleAwareViewTreeRecomposer()
的时候,代码中有这样一段代码:
//用伪代码粗略表达一下,细节代码,可以全局搜索一下,在文章最上面
viewTreeLifecycleOwner.lifecycle.addObserver{
Lifecycle.Event.ON_CREATE -> recomposer.runRecomposeAndApplyChanges()
Lifecycle.Event.ON_START -> pausableClock?.resume()
Lifecycle.Event.ON_STOP -> pausableClock?.pause()
Lifecycle.Event.ON_DESTROY -> recomposer.cancel()
}
我们到现在只分析了Lifecycle.Event.ON_CREATE
,初始化的时候就暂停了帧生成,
当触发Lifecycle.Event.ON_START
的时候,恢复帧生成 pausableClock?.resume()
,
接下来又会触发什么操作呢?
大家可以到此处再回头回顾一下,我们在上面又只分析到: createAndInstallWindowRecomposer()
这个方法内部的第一行代码,
这里只是介绍:3.5-Recomposer如何延迟创建?
此方法内部的其他代码主要是针对后面取消操作来做的收尾工作,防止内存泄漏
到此: Recomposer已经延迟创建成功!✅
来了来了,又回到我们上面提到的 ensureCompositionCreated() 刚刚分析完里面的resolveParentCompositionContext(),并且知道了Recomposer的延迟创建,接下来接着分析ViewGroup.setContent()
3.6-ViewGroup.setContent
GlobalSnapshotManager.ensureStarted()
里面做了什么?
GlobalSnapshotManager是一个全局快照管理
//代码来自GlobalSnapshotManager.android.kt
fun ensureStarted() {
if (started.compareAndSet(false, true)) {
//创建一个指定缓冲区的通道,溢出时删除缓冲区中最旧的值,将新值添加到缓冲区中
val channel = Channel<Unit>(Channel.CONFLATED)
CoroutineScope(AndroidUiDispatcher.Main).launch {
channel.consumeEach {
Snapshot.sendApplyNotifications()
}
}
//注册全局写观察者
Snapshot.registerGlobalWriteObserver {
channel.trySend(Unit)
}
}
}
当Channel调用trySend()
之后,在主线程触发Snapshot.sendApplyNotifications()
fun sendApplyNotifications() {
val changes = sync {
currentGlobalSnapshot.get().modified?.isNotEmpty() == true
}
//判断当前快照是否发现修改
if (changes) advanceGlobalSnapshot()
}
当调用链执行到 advanceGlobalSnapshot()
,这里面实现了数据更新监听回调
//代码来自Snapshot.kt
private fun <T> advanceGlobalSnapshot(block: (invalid: SnapshotIdSet) -> T): T {
//当前快照为上一个快照
val previousGlobalSnapshot = currentGlobalSnapshot.get()
val result = sync {
//获取新的快照
takeNewGlobalSnapshot(previousGlobalSnapshot, block)
}
//上一个快照状态是否有变更,如果有变更则通知观察者
val modified = previousGlobalSnapshot.modified
if (modified != null) {
val observers: List<(Set<Any>, Snapshot) -> Unit> = sync { applyObservers.toMutableList() }
observers.fastForEach { observer ->
observer(modified, previousGlobalSnapshot)
}
}
....
return result
}
我们继续分析:ViewGroup.setContent
里面的代码
//代码来自Wrapper.android.kt
internal fun ViewGroup.setContent(
parent: CompositionContext,
content: @Composable () -> Unit
): Composition {
//注册全局写观察者
GlobalSnapshotManager.ensureStarted()
//分析此处
val composeView =
if (childCount > 0) {
getChildAt(0) as? AndroidComposeView
} else {
removeAllViews(); null
} ?: AndroidComposeView(context).also { addView(it.view, DefaultLayoutParams) }
return doSetContent(composeView, parent, content)
}
首次创建UI,会触发这一行代码:
AndroidComposeView(context).also { addView(it.view, DefaultLayoutParams) }
我们看一下AndroidComposeView
//代码来自AndroidComposeView.android.kt
internal class AndroidComposeView(context: Context) :
ViewGroup(context), Owner, ViewRootForTest, PositionCalculator {
init {
....
//root = LayouNode()
root.attach(this)
}
}
我们看一下root.attach(this)
代码:
//代码来自AndroidComposeView.android.kt
val root = LayoutNode().also {
//定义了布局的测量和行为
it.measurePolicy = RootMeasurePolicy
//合并多个修饰符Modifier
it.modifier = Modifier
.then(semanticsModifier)
.then(_focusManager.modifier)
.then(keyInputModifier)
}
measurePolicy 获取到的measurable能够进行:测量、布局
感兴趣的童鞋可以自行查看RootMeasurePolicy里面的实现
modifier 组合多个修饰符,可以修改可组合项的大小、布局、行为和外观等等功能(注意:Modififer,修饰符函数的顺序非常重要,顺序会影响最终结果。)
LayoutNode内部的modifier、measurePolicy都是继承自ComposeUiNode类中的成员变量
接着看 fun attach(owner: Owner)
方法,注意一下入参 Owner:
Owner 实现与底层视图系统的连接。在 Android 上,这连接到 Android 视图,所有布局、绘制、输入和可访问性都通过它们挂钩。
//androidx.compose.ui.node.LayoutNode#attach
internal fun attach(owner: Owner) {
....
owner.onAttach(this)
_foldedChildren.forEach { child ->
child.attach(owner)
}
//由 LayoutNode 调用以请求所有者新的 measurement + layout
//符合条件的话会执行到:
//AndroidComposeView#scheduleMeasureAndLayout(layoutNode)里面
requestRemeasure()
//首次parent = null
parent?.requestRemeasure()
//LayoutNode包装器
//val innerLayoutNodeWrapper = InnerPlaceable(this)
innerLayoutNodeWrapper.attach()
forEachDelegate { it.attach() }
onAttach?.invoke(owner)
}
上面分析的代码都是在初始化AndroidComposeView
执行完AndroidComposeView(context)
初始化,就会执行addView()
AndroidComposeView(context).also { addView(it.view, DefaultLayoutParams) }
执行完addView()
,自然会触发AndroidComposeView
的onAttachedToWindow()
执行
//代码来自:AndroidComposeView.android.kt
override fun onAttachedToWindow() {
super.onAttachedToWindow()
//遍历所有LayoutNode,标记需要重新测量
invalidateLayoutNodeMeasurement(root)
//遍历所有LayoutNode,标记需要重新绘制
invalidateLayers(root)
//观察状态提交
snapshotObserver.startObserving()
....
}
//代码来自:OwnerSnapshotObserver.kt
private val observer = SnapshotStateObserver(onChangedExecutor)
internal fun startObserving() {
observer.start()
}
//代码来自:SnapshotStateObserver.kt
fun start() {
//观察状态提交
applyUnsubscribe = Snapshot.registerApplyObserver(applyObserver)
}
我们继续分析ViewGroup.setContent,查看doSetContent
3.7-doSetContent
1.初始化CompositionImpl
//androidx.compose.ui.platform.Wrapper_androidKt#doSetContent
private fun doSetContent(
owner: AndroidComposeView,
parent: CompositionContext,
content: @Composable () -> Unit
): Composition {
val original = Composition(UiApplier(owner.root), parent)
val wrapped = owner.view.getTag(R.id.wrapped_composition_tag)
as? WrappedComposition
?: WrappedComposition(owner, original).also {
owner.view.setTag(R.id.wrapped_composition_tag, it)
}
wrapped.setContent(content)
return wrapped
}
第一行代码Composition(UiApplier(owner.root), parent)
内部初始化了CompositionImpl
//代码来自:Composition.kt
fun Composition(
applier: Applier<*>,
parent: CompositionContext
): Composition = CompositionImpl(
parent,
applier
)
点击查看 4.1-CompositionContext 介绍
点击查看 4.6-Applier 介绍
上面的 doSetContent() 内部初始化了一个WrappedComposition
2.wrapped.setContent(content)
3.lifecycle.addObserver(this)
执行完lifecycle.addObserver(this)
怎么又执行到第二个if
分支里面去的呢?
👇👇请看下面的代码👇👇:
看到这里,上面疑问就解决了,那么接下来,Composition#setContent
又会发生什么事情呢?
4.original.setContent
我们来看一下,上面的代码 original.setContent :
//代码来自:Wrapped.android.kt
if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
//Composition#setContent
original.setContent {
....
CompositionLocalProvider(LocalInspectionTables provides inspectionTable) {
ProvideAndroidCompositionLocals(owner, content)
}
}
//代码来自:Composition.kt
override fun setContent(content: @Composable () -> Unit) {
//this.composable => 用于HotReload重新设置content
this.composable = content
parent.composeInitial(this, composable)
}
5.composeInitial(…)
我们接着看 parent.composeInitial(this, composable)
做了什么事情:
6.doCompose(…)
上面注释2处的composition.composeContent(content)
,内部实现如下:
//androidx.compose.runtime.ComposerImpl#doCompose
private fun doCompose(
invalidationsRequested: IdentityArrayMap<RecomposeScopeImpl, IdentityArraySet<Any>?>,
content: (@Composable () -> Unit)?
) {
//trace(..) 内联函数,block()首尾插入Trace.xxx进行函数耗时统计
trace("Compose:recompose") {
snapshot = currentSnapshot()
....
try {
startRoot()
...
} finally {
if (!complete) abortRoot()
}
}
}
东西确实有点多,如果大家看的有点懵的话,不要急,先看下去,然后再回头看几遍。
有些方法我们紧接着就单独展开讲解了,可能看完之后,忘了上面的代码执行到哪里了,所以需要大家再上下翻看几遍,建议跟踪源码同步一起看
7.ComposerImpl.startRoot()
如果想看更多细节可以断点调试
跟踪代码看数据,下面我们来看看startRoot():
建议大家先到4.5这个目录下面学习一下:点击查看SlotTable介绍
8.SlotWrite.startGroup(…)
我们接着,分析上面的代码 writer.startGroup(key, objectKey ?: Composer.Empty)
//androidx.compose.runtime.SlotWriter#startGroup(int, java.lang.Object, boolean, java.lang.Object)
private fun startGroup(key: Int, objectKey: Any?, isNode: Boolean, aux: Any?) {
//writer.beginInsert()执行之后,insertCount就大于0了
val inserting = insertCount > 0
//更新当前组中节点数量
nodeCountStack.push(nodeCount)
//groupSize
currentGroupEnd = if (inserting) {
//可以先去看一下4.5目录下面的SlotTable,然后再来理解它里面做的事情
insertGroups(1)
....
val dataSlotsNeeded = (if (isNode) 1 else 0) +
(if (hasObjectKey) 1 else 0) +
(if (hasAux) 1 else 0)
if (dataSlotsNeeded > 0) {
//dataSlotsNeeded表示size
//首先将Gap移动到currentSlot,然后减小gap size slots
//如果Gap小于size,则Gap增大来容纳size slots
insertSlots(dataSlotsNeeded, current)
....
}
....
} else {
....
}
}
startRoot()代码分析完
,我们继续分析上面子目录6.doCompose(...)
里面剩下的代码:
9.invokeComposable
点击查看 4.4-ComposableLambdaImpl
10.ComposerImpl.endRoot()
//androidx.compose.runtime.ComposerImpl#endRoot
private fun endRoot() {
//startRoot()执行的方法,对应的endRoot要有相应的收尾方法与之顺序一一对应
endGroup()
//与parentContext.startComposing()对应
parentContext.doneComposing()
endGroup()
recordEndRoot()
//清除回收对象
finalizeCompose()
//关闭slot reader
reader.close()
}
private fun recordEndRoot() {
//true表示已经执行过startGroup了
if (startedGroup) {
//记录更改,保证它被应用时,写入的内容与阅读器中的当前插槽相匹配
recordSlotTableOperation(change = endGroupInstance)
//reset
startedGroup = false
}
}
11.CompositionImpl#applyChanges
子目录5.composeInitial(...)
里面的注释3:composition.applyChanges(),内部实现如下:
//androidx.compose.runtime.CompositionImpl#applyChanges
override fun applyChanges() {
synchronized(lock) {
//管理RememberObserver
val manager = RememberEventDispatcher(abandonSet)
try {
applier.onBeginChanges()
// Apply all changes
slotTable.write { slots ->
val applier = applier
//change类型:Function3<Applier, SlotWriter, kotlin.Unit>
changes.fastForEach { change ->
//1️⃣:往下翻,下面会有分析
//调用后会执行,执行invoke方法
change(applier, slots, manager)
}
changes.clear()
}
//内部代码执行AndroidComposeView#clearInvalidObservations
applier.onEndChanges()
//触发onRemembered()和onForgotten()回调
manager.dispatchRememberObservers()
//触发SideEffect的block方法回调
manager.dispatchSideEffects()
...
}
} finally {
//移除abandoning集合,同时触发onAbandoned()回调
manager.dispatchAbandons()
}
//在释放锁之前,应用更改后,任何设置的内容都将发送到 recordModificationsOf
//Recomposer#invalidate 里面满足条件会释放锁
//可以看一下这里的代码:Recomposer#runRecomposeConcurrentlyAndApplyChanges
drainPendingModificationsLocked()
}
}
我们看一下上面注释1处的代码,执行invoke
之后,触发下面lambda代码:
//androidx.compose.runtime.ComposerImpl#recordInsert
private fun recordInsert(anchor: Anchor) {
....
val fixups = insertFixups.toMutableList()
insertFixups.clear()
....
recordSlotEditingOperation { applier, slots, rememberManager ->
//触发这里的代码调用
insertTable.write { writer ->
//fixup类型:Function3<Applier, SlotWriter, kotlin.Unit>
fixups.fastForEach { fixup ->
//2️⃣:这里又将触发什么执行呢?
fixup(applier, writer, rememberManager)
}
}
....
}
}
接着看一下注释2处,同样会执行invoke,这个时候将会触发recordFixup里面的lambda表达式被执行:
//androidx.compose.runtime.ComposerImpl#createNode
override fun <T> createNode(factory: () -> T) {
...
recordFixup { applier, slots, _ ->
//触发这里的代码调用
val node = factory()
slots.updateNode(groupAnchor, node)
...
}
...
}
点击查看更详细的ComposerImpl#createNode代码说明
执行完上面的代码之后,会接着触发,下面代码的change的lambda代码执行:
//androidx.compose.runtime.ComposerImpl#apply
override fun <V, T> apply(value: V, block: T.(V) -> Unit) {
val operation: Change = { applier, _, _ ->
//在ComposeUiNode.SetMeasurePolicy里面给measurePolicy赋值
(applier.current as T).block(value)
}
if (inserting) recordFixup(operation)
else recordApplierOperation(operation)
}
12.CompositionLocalProvider包裹content
点击查看4.2-CompositionLocalProvider用法
我们在original.setContent{}
函数中,我们看到了CompositionLocalProvider来隐士传递数据
//androidx.compose.ui.platform.WrappedComposition#setContent
CompositionLocalProvider(LocalInspectionTables provides inspectionTable) {
ProvideAndroidCompositionLocals(owner, content)
}
//androidx.compose.runtime.CompositionLocalKt#CompositionLocalProvider
fun CompositionLocalProvider(vararg values: ProvidedValue<*>, content: @Composable () -> Unit) {
currentComposer.startProviders(values)
content() //就是我们上面的:ProvideAndroidCompositionLocals(owner, content)
currentComposer.endProviders()
}
展开看一下ProvideAndroidCompositionLocals
内部代码:
上面的代码中使用到了by remember
,它内部方法实现如下:
//androidx.compose.runtime.ComposablesKt#remember
@Composable
inline fun <T> remember(calculation: @DisallowComposableCalls () -> T): T =
currentComposer.cache(false, calculation)
//androidx.compose.runtime.ComposerKt#cache
@ComposeCompilerApi
inline fun <T> Composer.cache(invalid: Boolean, block: () -> T): T {
//rememberedValue() = nextSlot()
return rememberedValue().let {
if (invalid || it === Composer.Empty) {
val value = block()
//在使用了by remember之后,会把block()块中的函数设置到当前的data slot中
//此方法会调用到:ComposerImpl#updateValue
//我们上面分析过这个updateValue了,如果你没有印象,自己往上翻一翻
updateRememberedValue(value)
value
} else it
} as T
}
我们简单的看一下SavedStateRegistryOwner
里面做的事情,更多细节方面大家自己看代码去品味品味:
public interface SavedStateRegistryOwner extends LifecycleOwner {
SavedStateRegistry getSavedStateRegistry();
}
//androidx.activity.ComponentActivity#ComponentActivity()
public ComponentActivity(){
...
getSavedStateRegistry().registerSavedStateProvider(ACTIVITY_RESULT_TAG,
() -> {
Bundle outState = new Bundle();
mActivityResultRegistry.onSaveInstanceState(outState);
return outState;
});
...
}
//androidx.savedstate.SavedStateRegistry#registerSavedStateProvider
@MainThread
public void registerSavedStateProvider(@NonNull String key,
@NonNull SavedStateProvider provider) {
SavedStateProvider previous = mComponents.putIfAbsent(key, provider);
...
}
打开SavedStateProvider
看到内部有一个saveState()
方法返回Bundle
我们发现 SavedStateHandle实例中,维护一个SavedStateProvider匿名内部类
实例,而SavedStateHandle的读写和SavedStateProvider实例的数据读取
操作,都是对实际数据容器mRegular读写。
protected void onSaveInstanceState(@NonNull Bundle outState) {
...
super.onSaveInstanceState(outState);
//把数据存储到bundle中
mSavedStateRegistryController.performSave(outState);
}
protected void onCreate(@Nullable Bundle savedInstanceState) {
//恢复bundle中的数据
mSavedStateRegistryController.performRestore(savedInstanceState);
...
}
13.ComposeView#Content
上面的ProvideCommonCompositionLocals里面的ComposableLambdaImpl
执行invoke
,里面触发ComposeView#Content
方法
//androidx.compose.ui.platform.ComposeView#Content
@Composable
override fun Content() {
content.value?.invoke()
}
这里的content
是什么呢?
我们在文章最上面 这个目录里面:3.3-SnapshotState和ComposeView 我们已经分析过了,从SnapshotMutableStateImpl
去获取value
,这个value
我们也提到过了,它其实就是一个ComposableLambdaImpl
执行invoke()
,会去执行这个ComposableLambdaImpl
的invoke
如果还是不懂ComposableLambdaImpl干什么的?
请点击查看 4.4-ComposableLambdaImpl 学习一下
我们在setContent
的时候传入的是@Composable Text(...)
,所以这个content.value
也就是我们的@Composable Text(...)
,所以接下来我们来分析一下它里面接下来会做什么?
3.8-@Composable Text(…)
看一下Text(...)
代码,它最终会调用到CoreText
//androidx.compose.foundation.text.CoreTextKt
internal fun CoreText(text: AnnotatedString,modifier: Modifier = Modifier,...) {
...
Layout(
//如果不是inline修饰的composable方法,返回一个空的lambda
content = if (inlineComposables.isEmpty()) {
{}
} else {
{ InlineChildren(text, inlineComposables) }
},
modifier = modifier........,
//TextController
measurePolicy = controller.measurePolicy
)
...
}
1.Layout可组合项
我们简单看一下Layout函数:
//androidx.compose.ui.layout.LayoutKt#Layout(...)
//如果需测量和布置多个可组合项,我们需要使用 Layout 可组合项。
//这个可组合项允许我们手动测量和布置子项
inline fun Layout(content: @Composable () -> Unit,
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy)
{
...
ReusableComposeNode<ComposeUiNode, Applier<Any>>(
//返回一个LayoutNode()
factory = ComposeUiNode.Constructor,
update = {
//设置measurePolicy
set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
set(density, ComposeUiNode.SetDensity)
set(layoutDirection, ComposeUiNode.SetLayoutDirection)
},
//往下翻,子目录『5.materializerOf』有代码分析
skippableUpdate = materializerOf(modifier),
content = content
)
}
2.ReusableComposeNode
继续看这里面要做什么事情?
//androidx/compose/runtime/Composables.kt
inline fun <T, reified E : Applier<*>> ReusableComposeNode(
noinline factory: () -> T,
update: @DisallowComposableCalls Updater<T>.() -> Unit,
noinline skippableUpdate: @Composable SkippableUpdater<T>.() -> Unit,
content: @Composable () -> Unit
) {
if (currentComposer.applier !is E) invalidApplier()
//启动一个可重用的Node
currentComposer.startReusableNode()
if (currentComposer.inserting) {
//1️⃣.创建一个Node
currentComposer.createNode(factory)
} else {
//调用ComposerImpl#recordDown()
//从slot reader读取groups的index(=parent)处的节点,并push到downNodes栈中
//在ComposerImpl#realizeDowns被调用的时候:遍历downNodes,用Applier#down进行树向下遍历
currentComposer.useNode()
}
//临时禁用重用
currentComposer.disableReusing()
//2️⃣.回调ReusableComposeNode内部的update
Updater<T>(currentComposer).update()
//启用重用
currentComposer.enableReusing()
//3️⃣.回调:触发materializerOf(modifier)调用
SkippableUpdater<T>(currentComposer).skippableUpdate()
currentComposer.startReplaceableGroup(0x7ab4aae9)
content()
currentComposer.endReplaceableGroup()
currentComposer.endNode()
}
3.ComposerImpl#createNode
我们来看一下『注释1』处要做什么?
//androidx.compose.runtime.ComposerImpl#createNode
override fun <T> createNode(factory: () -> T) {
...
//返回栈顶index
val insertIndex = nodeIndexStack.peek()
//返回当前组的锚点
val groupAnchor = writer.anchor(writer.parent)
groupNodeCount++
/*
* 问题:怎么触发这里的Lambda执行?
* 触发的位置:CompositionImpl#applyChanges方法
* changes.fastForEach里面,执行change(applier, slots, manager)之后
* 执行Function3的invoke之后 -> ComposerImpl#recordInsert方法里面:
* recordSlotEditingOperation的lambda被执行,执行fixup(applier, writer, rememberManager)
* 执行Funtion3的invoke之后-> 就会触发此处的lambda代码块执行
*/
recordFixup { applier, slots, _ ->
//LayouNode
val node = factory()
//将node设置到slot data中
slots.updateNode(groupAnchor, node)
val nodeApplier = applier as Applier<T>
//在UiApplier中是空实现...
nodeApplier.insertTopDown(insertIndex, node)
applier.down(node)
}
//和上面的注释一样,触发Lambda执行的来源一致
recordInsertUpFixup { applier, slots, _ ->
//返回锚点处的node,slot data中存储的数据
val nodeToInsert = slots.node(groupAnchor)
//表示此时正在“向上”遍历树,返回集合中最新的node
applier.up()
val nodeApplier = applier as Applier<Any?>
//从底部向上构建树,会触发:LayoutNode#attach方法被执行
nodeApplier.insertBottomUp(insertIndex, nodeToInsert)
}
}
4.Updater.update()
我们来看一下『注释2』处的Updater<T>(currentComposer).update()
它回调到这里
//androidx.compose.ui.layout.LayoutKt#Layout(...)
ReusableComposeNode<ComposeUiNode, Applier<Any>>(
...
update = {
//measurePolicy是从TextController中获取到的,忘了的话:自己打开源码或者往上翻一下,就在当前目录的开头地方
set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
...
},
...
)
我们看一下这个set(...)
方法里面要做什么?
//androidx.compose.runtime.Updater
fun <V> set(value: V, block: T.(value: V) -> Unit) = with(composer) {
if (inserting || rememberedValue() != value) {
//这个我们也分析过了,见:ComposerImpl#updateValue
//4.3目录下面有updateValue分析,不在重复分析
updateRememberedValue(value)
//value = measurePolicy,block = ComposeUiNode.SetMeasurePolicy
composer.apply(value, block)
}
}
//androidx.compose.runtime.ComposerImpl#apply
override fun <V, T> apply(value: V, block: T.(V) -> Unit) {
val operation: Change = { applier, _, _ ->
//在ComposeUiNode.SetMeasurePolicy里面给measurePolicy赋值
(applier.current as T).block(value)
}
if (inserting) recordFixup(operation)
else recordApplierOperation(operation)
}
5.materializerOf
我们来看一下『注释3』处回调执行的方法:materializerOf(modifier)
//androidx.compose.ui.layout.LayoutKt#materializerOf
internal fun materializerOf(
modifier: Modifier
): @Composable SkippableUpdater<ComposeUiNode>.() -> Unit = {
//拆开看源码
val materialized = currentComposer.materialize(modifier)
update {
//将modifier设置为materialized,触发LayouNode的成员变量modifier的set方法
//在触发LayoutNode#Modifier.set(value)之后,内部会使用foldOut方法,
//进行遍历从而生成 LayoutNodeWrapper链
set(materialized, ComposeUiNode.SetModifier)
}
}
来看看这个Composer.materialize:
fun Composer.materialize(modifier: Modifier): Modifier {
if (modifier.all { it !is ComposedModifier }) return modifier
//看注释写到这是个假的可组合函数
//此处调用的作用就是为了方便下面的element.factory调用
startReplaceableGroup(0x48ae8da7)
//foldIn和foldOut在下面有介绍
val result = modifier.foldIn<Modifier>(Modifier) { acc, element ->
acc.then(
if (element is ComposedModifier) {
val factory = element.factory as Modifier.(Composer, Int) -> Modifier
val composedMod = factory(Modifier, this, 0)
//递归调用
materialize(composedMod)
} else element
)
}
//endGroup
endReplaceableGroup()
return result
}
6.foldIn、foldOut函数
我们先看一下foldIn例子
:
//size、padding、offset内部都使用了then
//最终会调用CombinedModifier,来连接两个Modifier
val modifier = Modifier.size(100.dp).padding(10.dp).offset()
val resultModifier = modifier.foldIn("initial") { str, element ->
println("str:$str,element:$element")
"$str『和』$element"
}
println("打印结果:$resultModifier")
输出的结果如下:
//调用:
str:initial,element:SizeModifier
str:initial『和』SizeModifier,element:PaddingModifier
str:initial『和』SizeModifierr『和』PaddingModifier,element:OffsetModifier
打印结果:initial『和』SizeModifier『和』PaddingModifier『和』OffsetModifier
foldIn最终遍历调用的顺序:是一个正向的链式Modifier调用,按照添加的顺序依次有序的调用。
我们把上面的foldIn例子修改一下,替换foldIn为foldOut,看看最后输出的结果:
//调用:
str:OffsetModifier,element:initial
str:PaddingModifier,element:OffsetModifier『和』initial
str:SizeModifier,element:PaddingModifier『和』OffsetModifier『和』initial
打印结果:SizeModifier『和』PaddingModifier『和』OffsetModifier『和』initial
foldOut最终遍历调用的顺序:是一个反向的链式Modifier调用,按照添加的顺序从后往前调用。
3.9-onMeasure
我们先简单看一下ComposeView#onMeasure 内部的调用的时序图如下(省略细节,更多细节大家可以去看源码,大概意思如下👇👇
)
简单的看完上面的时序图之后,我们来看这个方法AbstractComposeView#onMeasure
//androidx.compose.ui.platform.AbstractComposeView#onMeasure
final override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int){
ensureCompositionCreated()
internalOnMeasure(widthMeasureSpec, heightMeasureSpec)
}
我们看下internalOnMeasure(widthMeasureSpec, heightMeasureSpec)
方法:
//androidx.compose.ui.platform.AbstractComposeView#internalOnMeasure
internal open fun internalOnMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
...
//子view执行measure方法,内部会触发onMeasure回调(即:AndroidComposeView#onMeasure)
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.getMode(widthMeasureSpec)),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.getMode(heightMeasureSpec)),
)
setMeasuredDimension(
child.measuredWidth + paddingLeft + paddingRight,
child.measuredHeight + paddingTop + paddingBottom
)
}
看了上面的代码:此处为什么会触发AndroidComposeView#onMeasure?
再来简单的回顾一下:
public fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit
) {
...
//继承自AbstractComposeView
ComposeView(this).apply {
...
ViewGroup.setContent(...){
...
AndroidComposeView(context).also { addView(it.view, DefaultLayoutParams) }
...
}
...
setContentView(this, DefaultActivityContentLayoutParams)
}
}
1.child.measure
我们来看看这个AndroidComposeView#onMeasure
//androidx.compose.ui.platform.AndroidComposeView
override val root = LayoutNode().also {
it.measurePolicy = RootMeasurePolicy
it.modifier = Modifier.then(semanticsModifier)
.then(_focusManager.modifier)
.then(keyInputModifier)
}
//初始化并传入LayoutNode
private val measureAndLayoutDelegate = MeasureAndLayoutDelegate(root)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
trace("AndroidOwner:onMeasure") {
...
measureAndLayoutDelegate.measureAndLayout()
//设置View的宽和高
setMeasuredDimension(root.width, root.height)
if (_androidViewsHandler != null) {
//主要是用于AndroidView的,Compose里面添加xml的View的时候需要AndroidView
androidViewsHandler.measure(
MeasureSpec.makeMeasureSpec(root.width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(root.height, MeasureSpec.EXACTLY)
)
}
}
}
2.measureAndLayout
上面的measureAndLayout内部的实现如下:
//androidx.compose.ui.node.MeasureAndLayoutDelegate#measureAndLayout
fun measureAndLayout(): Boolean {
...
//如果没有rootConstraints的话就不需要执行下面的测量
val rootConstraints = rootConstraints ?: return false
var rootNodeResized = false
if (relayoutNodes.isNotEmpty()) {
//flag
duringMeasureLayout = true
try {
//在MeasureAndLayoutDelegate#requestRemeasure里面,使用了relayoutNodes添加了待测量和布局的layouNode
//relayoutNodes(DepthSortedSet按照树深度排序)
relayoutNodes.popEach { layoutNode ->
if (layoutNode.isPlaced ||
layoutNode.canAffectParent ||
layoutNode.alignmentLines.required
) {
if (layoutNode.layoutState == NeedsRemeasure) {
//1️⃣:往下翻有代码分析
if (doRemeasure(layoutNode, rootConstraints)) {
rootNodeResized = true
}
}
if (layoutNode.layoutState == NeedsRelayout && layoutNode.isPlaced) {
if (layoutNode === root) {
//在父坐标的(x, y)处放置一个Placeable
//内部会执行到LayoutNode#layoutChildren
//2️⃣:往下翻有代码分析
layoutNode.place(0, 0)
} else {
//相同位置替换节点
layoutNode.replace()
}
//跟踪正在摆放位置的节点
onPositionedDispatcher.onNodePositioned(layoutNode)
}
...
if (postponedMeasureRequests.isNotEmpty()) {
//延迟的测量请求列表
postponedMeasureRequests.fastForEach {
if (it.isAttached) {
//请求重新测量此layoutNode以及受其测量结果影响的节点
requestRemeasure(it)
}
}
//清空列表
postponedMeasureRequests.clear()
}
}
}
} finally {
duringMeasureLayout = false
}
...
}
return rootNodeResized
}
3.doRemeasure(layoutNode,constraints)
我们来看一下上面注释1的doRemeasure(...)
实现
//androidx.compose.ui.node.MeasureAndLayoutDelegate#doRemeasure
private fun doRemeasure(layoutNode: LayoutNode, rootConstraints: Constraints): Boolean {
val sizeChanged = if (layoutNode === root) {
//如果测量大小已更改返回true
//1️⃣:往下翻有代码分析
layoutNode.remeasure(rootConstraints)
} else {
layoutNode.remeasure()
}
val parent = layoutNode.parent
if (sizeChanged) {
if (parent == null) {
return true
} else if (layoutNode.measuredByParent == InMeasureBlock) {
//请求重新测量此layoutNode以及受其测量结果影响的节点
requestRemeasure(parent)
} else {
require(layoutNode.measuredByParent == InLayoutBlock)
//为这个layoutNode(即parent)和受其位置影响的节点请求【重新布局】
requestRelayout(parent)
}
}
return false
}
4.layoutNode.remeasure
我们来分析上面注释1代码:
//androidx.compose.ui.node.LayoutNode#remeasure
//这里我们看到引入一个OuterMeasurablePlaceable,我们先去看一下这个概念
internal fun remeasure(
constraints: Constraints? = outerMeasurablePlaceable.lastConstraints
): Boolean {
return if (constraints != null) {
//进去看里面的细节实现
outerMeasurablePlaceable.remeasure(constraints)
} else {
false
}
}
5.OuterMeasurablePlaceable
我们在LayouNode
中发现,成员变量的地方初始化了OuterMeasurablePlaceable
//androidx.compose.ui.node.LayoutNode
internal val innerLayoutNodeWrapper: LayoutNodeWrapper = InnerPlaceable(this)
//初始化并传入layoutNode和innerLayoutNodeWrapper
private val outerMeasurablePlaceable = OuterMeasurablePlaceable(this, innerLayoutNodeWrapper)
6.outerMeasurablePlaceable.remeasure
//androidx.compose.ui.node.OuterMeasurablePlaceable#remeasure
fun remeasure(constraints: Constraints): Boolean {
//AndroidComposeView
val owner = layoutNode.requireOwner()
val iteration = owner.measureIteration
val parent = layoutNode.parent
layoutNode.canMultiMeasure = layoutNode.canMultiMeasure ||
(parent != null && parent.canMultiMeasure)
//防止同一个Measurable多次调用measure()
check(measureIteration != iteration || layoutNode.canMultiMeasure) {
"measure() may not be called multiple times on the same Measurable"
}
measureIteration = owner.measureIteration
if (layoutNode.layoutState == LayoutState.NeedsRemeasure ||
measurementConstraints != constraints
) {
...
//Placeable测量的大小
val outerWrapperPreviousMeasuredSize = outerWrapper.size
//执行到:Snapshot#enter触发block回调
owner.snapshotObserver.observeMeasureSnapshotReads(layoutNode) {
//此处会进入了DelegatingLayoutNodeWrapper#measure方法里面
//1️⃣:往下翻有分析
outerWrapper.measure(constraints)
}
...
return sizeChanged
}
return false
}
注释1处为什么代码会进入委托的layoutNode包装器
里面的measure
方法?
我们看一下,这里的代码:
//androidx.compose.ui.node.LayoutNode
override var modifier: Modifier = Modifier
//只要是从文章上面一直看到这里的,应该没有人不知道这个什么时候触发的吧
//再啰嗦一下,不懂的可以再去看一下:SkippableUpdater<T>(currentComposer).skippableUpdate()
set(value) {
//创建一个新的LayoutNodeWrappers链
val outerWrapper = modifier.foldOut(innerLayoutNodeWrapper) { mod, toWrap ->
...
//返回的就是 DelegatingLayoutNodeWrapper
val delegate = reuseLayoutNodeWrapper(mod, toWrap)
...
wrapper = delegate
...
}
//InnerPlaceable
outerWrapper.wrappedBy = parent?.innerLayoutNodeWrapper
//🐂🍺 替换了我们的layouNodeWrapper
outerMeasurablePlaceable.outerWrapper = outerWrapper
}
我们再继续看DelegatingLayoutNodeWrapper里面的measure方法:
//androidx.compose.ui.node.DelegatingLayoutNodeWrapper#measure
override fun measure(constraints: Constraints): Placeable = performingMeasure(constraints) {
//这里后面会触发InnerPlaceable#measure
val placeable = wrapped.measure(constraints)
...
return this
}
//androidx.compose.ui.node.LayoutNodeWrapper#performingMeasure
protected inline fun performingMeasure(
constraints: Constraints,
block: () -> Placeable
): Placeable {
measurementConstraints = constraints
val result = block()
//更改绘制区域大小
layer?.resize(measuredSize)
return result
}
我们继续来分析上面目录2.measureAndLayout() 里面layoutNode.place(0, 0)
的内部实现,最终会调用到:
7.layoutChildren
//androidx.compose.ui.node.LayoutNode#layoutChildren
internal fun layoutChildren() {
...
if (layoutState == NeedsRelayout) {
layoutState = LayingOut
val owner = requireOwner()
owner.snapshotObserver.observeLayoutSnapshotReads(this) {
...
//内部实现:Placeable.PlacementScope.executeWithRtlMirroringValues(....)
//最终会触发:LayoutNodeWrapper#placeAt
innerLayoutNodeWrapper.measureResult.placeChildren()
...
}
}
...
}
//androidx.compose.ui.node.LayoutNodeWrapper#onLayerBlockUpdated
fun onLayerBlockUpdated(layerBlock: (GraphicsLayerScope.() -> Unit)?) {
...
if (isAttached && layerBlock != null) {
if (layer == null) {
//创建一个OwnedLayer,绘制传递的drawBlock,往下翻有详细代码分析
//返回用于分离绘制内容的图层
layer = layoutNode.requireOwner().createLayer(
this,
invalidateParentLayer
).apply {
//更改图层绘制区域的大小
resize(measuredSize)
//更改图层内容位置
move(position)
}
...
}
...
} else {
layer?.let {
//重置标记,清除无效的观察者
it.destroy()
layoutNode.innerLayerWrapperIsDirty = true
...
}
layer = null
lastLayerDrawingWasSkipped = false
}
}
LayoutNodeWrapper#placeAt调用之后,placeAt方法内部会执行onLayerBlockUpdated方法的调用
8.createLayer
override fun createLayer(
drawBlock: (Canvas) -> Unit,
invalidateParentLayer: () -> Unit
): OwnedLayer {
//此处注释说:Android Q+以上的系统都是支持RenderNode的,大于Android M且低于Android Q系统版本的会尝试,失败会使用ViewLayer
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isRenderNodeCompatible) {
//6.0以上系统会尝试去创建RenderNodeLayer,出错会使用ViewLayer
try {
//1️⃣:往下翻有代码分析
return RenderNodeLayer(this,drawBlock,invalidateParentLayer)
} catch (_: Throwable) {
isRenderNodeCompatible = false
}
}
//👇👇下面是只有RenderNodeLayer创建失败的时候才会执行到下面这种兼容模式👇👇
if (viewLayersContainer == null) {
if (!ViewLayer.hasRetrievedMethod) {
//【仅测试】是否可以正常调用updateDisplayList(view),内部通过反射调用updateDisplayListIfDirty和mRecreateDisplayList方法
//如果失败,ViewLayer.shouldUseDispatchDraw将被设置为true
ViewLayer.updateDisplayList(View(context))
}
viewLayersContainer = if (ViewLayer.shouldUseDispatchDraw) {
//2️⃣:往下翻有代码分析
DrawChildContainer(context)
} else {
//3️⃣:往下翻有代码分析
ViewLayerContainer(context)
}
//添加到AndroidComposeView里面
addView(viewLayersContainer)
}
//返回一个ViewLayer
return ViewLayer(this, viewLayersContainer!!, drawBlock, invalidateParentLayer)
}
9.RenderNodeLayer
注释1处初始化RenderNodeLayer
//androidx.compose.ui.platform.RenderNodeLayer
//注解表示:Android 6.0以上才可以使用
@RequiresApi(Build.VERSION_CODES.M)
internal class RenderNodeLayer(
val ownerView: AndroidComposeView,
val drawBlock: (Canvas) -> Unit,
val invalidateParentLayer: () -> Unit
) : OwnedLayer {
//RenderNodeLayer失效且还没有绘制的时候为true
private var isDirty = false
set(value) {.....}
//可以使用Outline实现多种形状效果
private val outlineResolver = OutlineResolver(ownerView.density)
//不在需要这个Layer的时候,设置为true
private var isDestroyed = false
//是否启用画布的Z轴
private var drawnWithZ = false
//矩阵缓存类,避免重复调用android.graphics.Matrix.getValues,矩阵没有变化就可以重复使用,不需要读取和更新
private val matrixCache = RenderNodeMatrixCache()
//维护一份AndroidCanvas,CanvasHolder对外提供drawInto方法
private val canvasHolder = CanvasHolder()
//维护了原点位置
private var transformOrigin: TransformOrigin = TransformOrigin.Center
private val renderNode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
//使用android.graphics.RenderNode这个包下面的类
//初始化val renderNode = RenderNode("Compose")
RenderNodeApi29(ownerView)
} else {
//使用android.view.RenderNode 这个包下面的类
//初始化val renderNode = RenderNode.create("Compose", ownerView)
RenderNodeApi23(ownerView)
}.apply {
//表示此时是否存在内容重叠,true表示存在内容重叠
setHasOverlappingRendering(true)
}
....
override fun updateLayerProperties(scaleX: Float,scaleY: Float,........) {
....
//返回Outline数据是否变更
val shapeChanged = outlineResolver.update(...)
//设置轮廓裁剪
renderNode.setOutline(outlineResolver.outline)
val isClippingManually = renderNode.clipToOutline && outlineResolver.clipPath != null
if (wasClippingManually != isClippingManually || (isClippingManually && shapeChanged)) {
//通知AndroidComposeView调用invalidate
invalidate()
} else {
triggerRepaint()
}
if (!drawnWithZ && renderNode.elevation > 0f) {
//回调block,最终是wrappedBy?.invalidateLayer()调用
invalidateParentLayer()
}
//用于下一次访问matrixCache的时候刷新,新的Matrix缓存
matrixCache.invalidate()
}
...
override fun resize(size: IntSize) {
val width = size.width
val height = size.height
//中心点
renderNode.pivotX = transformOrigin.pivotFractionX * width
renderNode.pivotY = transformOrigin.pivotFractionY * height
//设置渲染节点的位置,返回true表示节点发生变更
if (renderNode.setPosition(renderNode.left,renderNode.top,.....)) {
outlineResolver.update(Size(width.toFloat(), height.toFloat()))
renderNode.setOutline(outlineResolver.outline)
invalidate()
matrixCache.invalidate()
}
}
...
override fun invalidate() {
if (!isDirty && !isDestroyed) {
//通知AndroidComposeView调用invalidate
ownerView.invalidate()
isDirty = true
}
}
private fun triggerRepaint() {
// onDescendantInvalidated is only supported on O+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WrapperRenderNodeLayerHelperMethods.onDescendantInvalidated(ownerView)
} else {
ownerView.invalidate()
}
}
//LayoutNodeWrapper#draw 里面OwnedLayer不为空的话,会调用drawLayer
override fun drawLayer(canvas: Canvas) {
//从AndroidCanvas里面获取的internalCanvas
val androidCanvas = canvas.nativeCanvas
//true:画布启用硬件加速
if (androidCanvas.isHardwareAccelerated) {
//更新画布上的图形,满足内部条件会调用clipPath
updateDisplayList()
//是否支持Z轴投影
drawnWithZ = renderNode.elevation > 0f
if (drawnWithZ) {
//允许渲染带有提升或平移渲染节点的投影。
canvas.enableZ()
}
//内部最终会调用到一个native方法
//native void nDrawRenderNode(long renderer, long renderNode)
//下面有这个native代码的分析
renderNode.drawInto(androidCanvas)
if (drawnWithZ) {
//禁用Z轴支持
canvas.disableZ()
}
} else {
//会执行到这个方法LayoutNodeWrapper#invoke(canvas)
drawBlock(canvas)
isDirty = false
}
}
override fun updateDisplayList() {
if (isDirty || !renderNode.hasDisplayList) {
isDirty = false
val clipPath = if (renderNode.clipToOutline) outlineResolver.clipPath else null
//更新画布上的图形,满足内部条件会调用clipPath
//里面的drawBlock(canvas),会执行到这个方法LayoutNodeWrapper#invoke(canvas)
renderNode.record(canvasHolder, clipPath, drawBlock)
}
}
override fun destroy() {
isDestroyed = true
isDirty = false
//清除无效的观察者
ownerView.requestClearInvalidObservations()
}
...
}
10.nDrawRenderNode
我们分析一下上面renderNode.drawInto(androidCanvas) 里面的native方法
我们是怎么找到源码位置的?
1.文件肯定是android/graphics/
包下面的
2.nDrawRenderNode
方法是在RecordingCanvas
里面
3.注释中有Critical JNI
4.肯定要找jni目录
下面的文件
5.系统负责硬件加速的模块主要是HWUI
点击查看DisplayListCanvas.cpp源码
//platform/frameworks/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
//大家可以去【DisplayListCanvas.cpp源码】对比【RecordingCanvas】的native方法包括注释都是一一对应的,我们找的没有问题就是这个文件
static JNINativeMethod gMethods[] = {
// ------------ @FastNative ------------------
{ "nCallDrawGLFunction", "(JJLjava/lang/Runnable;)V",
(void*) android_view_DisplayListCanvas_callDrawGLFunction },
// ------------ @CriticalNative --------------
{ "nCreateDisplayListCanvas", "(JII)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
...
//我们要找的就是它:nDrawRenderNode
{ "nDrawRenderNode", "(JJ)V", (void*) android_view_DisplayListCanvas_drawRenderNode },
...
};
// ---------------- @CriticalNative -------------------------
static jlong android_view_DisplayListCanvas_createDisplayListCanvas(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
jint width, jint height) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
//这个canvas是RecordingCanvas吗?不是的,原本以为它是RecordingCanvas
//后来发现不是,因为没有在RecordingCanvas.cpp里面找到drawRenderNode,哈哈哈哈,挺尴尬的
//此时我就想找一下这个Canvas是什么,然后我们发现头文件用的是这个:#include <hwui/Canvas.h>
return reinterpret_cast<jlong>(Canvas::create_recording_canvas(width, height, renderNode));
}
...
//nDrawRenderNode对应的就是这个方法
static void android_view_DisplayListCanvas_drawRenderNode(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong renderNodePtr) {
//看下面的代码分析,最终我们知道‘它’原来是SkiaRecordingCanvas
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
canvas->drawRenderNode(renderNode);
}
我们找一下这个头文件#include <hwui/Canvas.h>
点击查看hwui/hwui/Canvas.cpp源码
//platform/frameworks/libs/hwui/hwui/Canvas.cpp
//原来是在这里创建了一个SkiaRecordingCanvas
Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) {
return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
}
原来如此,这一下我们就知道上面android_view_DisplayListCanvas_drawRenderNode里面初始化的Canvas原来是SkiaRecordingCanvas
点击查看SkiRecordingCanvas.cpp源码
我们简简单单的看一下这里面做了什么:
//platform/frameworks/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) {
//记录子节点。当mChildNodes双端队列被清除时,Drawable dtor 将被调用。
//mDisplayList => SkiaDisplayList
//std::deque<RenderNodeDrawable> mChildNodes;
mDisplayList->mChildNodes.emplace_back(renderNode, asSkCanvas(), true, mCurrentBarrier);
auto& renderNodeDrawable = mDisplayList->mChildNodes.back();
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex());
}
drawDrawable(&renderNodeDrawable);
if (renderNode->stagingProperties().isProjectionReceiver()) {
//mProjectionReceiver指向作为投影接收器的子节点(存储在mChildNodes中)
mDisplayList->mProjectionReceiver = &renderNodeDrawable;
}
}
drawRenderNode对DisplayList做合并和优化
11.DrawChildContainer
目录8.createLayer,里面的:注释2处返回DrawChildContainer
internal open class DrawChildContainer(context: Context) : ViewGroup(context) {
init {
//表示子视图可以超出边界
clipChildren = false
//这里确实有点秀我一脸,还可以这样操作:inspector中隐藏此视图及子视图
setTag(R.id.hide_in_inspector_tag, true)
}
//不操作子视图的摆放
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
}
//不测量子视图
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
setMeasuredDimension(0, 0)
}
override fun dispatchDraw(canvas: android.graphics.Canvas) {
var doDispatch = false
for (i in 0 until childCount) {
val child = getChildAt(i) as ViewLayer
if (child.isInvalidated) {
doDispatch = true
break
}
}
//存在无效图层的情况下,需要调用:super.dispatchDraw,否则就是空实现
if (doDispatch) {
super.dispatchDraw(canvas)
}
}
//对外提供drawChild方法,绘制子视图,具体细节大家可以看View#drawChild内部实现
internal fun drawChild(canvas: Canvas, view: View, drawingTime: Long) {
super.drawChild(canvas.nativeCanvas, view, drawingTime)
}
}
12.ViewLayerContainer
目录8.createLayer,里面的:注释3处返回ViewLayerContainer
internal class ViewLayerContainer(context: Context) : DrawChildContainer(context) {
//空实现
override fun dispatchDraw(canvas: android.graphics.Canvas) {
}
//不调用父类的实现,自己控制子视图,我们不希望视图系统强制更新displayList
protected fun dispatchGetDisplayList() {
}
}
3.10-onLayout
我们来看这个方法AbstractComposeView#onLayout
//androidx.compose.ui.platform.AbstractComposeView#onLayout
final override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) =
internalOnLayout(changed, left, top, right, bottom)
internal open fun internalOnLayout(changed: Boolean,left: Int,top: Int,right: Int,bottom: Int) {
//子View就是AndroidComposeView
getChildAt(0)?.layout(
paddingLeft,paddingTop,
right - left - paddingRight,
bottom - top - paddingBottom
)
}
我们看一下AndroidComposeView的onLayout
//androidx.compose.ui.platform.AndroidComposeView#onLayout
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
onMeasureConstraints = null
updatePositionCacheAndDispatch()
if (_androidViewsHandler != null) {
androidViewsHandler.layout(0, 0, r - l, b - t)
}
}
3.11-dispatchDraw
我们来看这个方法AndroidComposeView#dispatchDraw
//androidx.compose.ui.platform.AndroidComposeView#dispatchDraw
override fun dispatchDraw(canvas: android.graphics.Canvas) {
if (!isAttachedToWindow) {//视图还没有添加到窗口
//遍历整个LayoutNode,并将所有层标记为需要重新绘制
invalidateLayers(root)
}
measureAndLayout()
isDrawingContent = true
//我们文章最开头传入了一个@Composable Text(...)
//我们从代码分析得出:drawBlock(canvas) -> 会触发LayoutNodeWrapper#invoke方法的调用
//LayoutNodeWrapper#invoke最终执行里面的performDraw,大概调用顺序如下:
//ModifiedDrawNode#performDraw -> DrawBackgroundModifier#draw里面的block代码块onDraw() ...-> Layout#draw 最终会在这里调用drawText来绘制文本内容
//执行完DrawBackgroundModifier#draw里面的block代码块之后,会执行下面的drawContent() -> LayoutNodeDrawScope#drawContent -> LayoutNodeWrapper#draw
//最终都要往这个canvas上画图
canvasHolder.drawInto(canvas) { root.draw(this) }
if (dirtyLayers.isNotEmpty()) {
//重新绘制dirtyLayers
...
}
//只有ViewLayer#updateDisplayList(view)测试方法调用失败,这个变量才会为true
if (ViewLayer.shouldUseDispatchDraw) {
val saveCount = canvas.save()
//裁剪画布,只显示裁剪区域,区域之外的都不显示,可想而知:没有内容能显示
canvas.clipRect(0f, 0f, 0f, 0f)
super.dispatchDraw(canvas)
canvas.restoreToCount(saveCount)
}
dirtyLayers.clear()
isDrawingContent = false
if (postponedDirtyLayers != null) {
//需要延迟重新绘制的dirtyLayers
val postponed = postponedDirtyLayers!!
dirtyLayers.addAll(postponed)
postponed.clear()
}
}
我们简单看一下,dispatchDraw内部执行了如下流程(细节太多😂,省略大部分细节):
4.部分概念详解
在看代码学习的时候,发现有些概念,当时看的不是很明白,深入代码仔细阅读了一些代码之后才明白的,所以这里也给大家整理一下,但是大家在学习新东西的时候,不太明白的概念可以先跳过,尝试去看下面的代码,有些代码不懂,多看几遍之后就可能明白是怎么回事了
4.1-CompositionContext
组合上下文
,我们看看这个抽象类里面提供的方法:
4.2-CompositionLocalProvider
我们先看一眼CompositionLocalProvider
方法:
//代码来自:CompositionLocal.kt
fun CompositionLocalProvider(vararg values: ProvidedValue<*>, content: @Composable () -> Unit) {
currentComposer.startProviders(values)
content()
currentComposer.endProviders()
}
用法类似下面这种的方式:
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
...
}
LocalContentAlpha
通过 infix fun provides(value: T)
中缀providers,将ContentAlpha.medium
值提供给LocalContentAlpha
我们可以通过LocalContentAlpha.current
获取数据。
1.自定义CompositionLocal
CompositionLocal是一种通过 Composition 隐式传递数据的工具。
我们看一下上面提到的LocalContentAlpha
是个什么?
val LocalContentAlpha = compositionLocalOf { 1f }
fun <T> compositionLocalOf(
//快照的变更策略
policy: SnapshotMutationPolicy<T> =
structuralEqualityPolicy(),
defaultFactory: () -> T
): ProvidableCompositionLocal<T> = DynamicProvidableCompositionLocal(policy, defaultFactory)
internal class DynamicProvidableCompositionLocal<T> constructor(
private val policy: SnapshotMutationPolicy<T>,
defaultFactory: () -> T
) : ProvidableCompositionLocal<T>(defaultFactory) {
//此处的mutableStateOf大家自己打开源码查看一下
//androidx.compose.runtime.SnapshotMutableStateImpl
//如果是新值会触发:next.overwritable(this, it) { this.value = value }
//执行get()的时候会拿新的值
@Composable
override fun provided(value: T): State<T> = remember { mutableStateOf(value, policy) }.apply {
this.value = value //最新的值
}
}
那么我们开始自己定义一个CompositionLocal
data class LayoutData(val elevation: Dp = 0.dp,val background:Color = Color.White)
val LocalLayoutData = compositionLocalOf { LayoutData() }
//用法如下:
val layoutData = LayoutData(elevation = 10.dp,background = Color.Blue)
CompositionLocalProvider(LocalLayoutData provides layoutData) {
val currentLayoutData:LayoutData = LocalLayoutData.current
....
}
2.compositionLocalOf和staticCompositionLocalOf区别
staticCompositionLocalOf: 用于存储很少更改状态的变量数据。它的任何更改都会重新组合的整个树。
compositionLocalOf: 用于存储可以更改状态的变量数据。它的任何更改只会导致相应的可组合函数进行重新组合。
我们来看一下测试效果:
compositionLocalOf 效果
staticCompositionLocalOf 效果
示例代码如下:
//xxxTest.kt
val ColorLocalStatic = staticCompositionLocalOf {
Color.Black
}
val ColorLocalDynamic = compositionLocalOf {
Color.Black
}
@Composable
fun InnerBox(color: Color,boxValue:Int,
content: @Composable BoxScope.() -> Unit) {
Column (
Modifier
.fillMaxWidth()
.height(150.dp)
.background(color)) {
Text("BoxValue:$boxValue")
Box(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),content = content)
}
}
@Preview
@Composable
fun TestCompositionLocalProvider(){
var color by remember { mutableStateOf(Color.Green) }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
CompositionLocalProvider(ColorLocalDynamic provides color) {
MainActivity.boxValue1++
InnerBox(color = Color.Gray, boxValue = MainActivity.boxValue1) {
MainActivity.boxValue2++
InnerBox(color = ColorLocalDynamic.current, boxValue = MainActivity.boxValue2) {
MainActivity.boxValue3++
InnerBox(color = Color.Yellow, boxValue = MainActivity.boxValue3) {
}
}
}
}
Button(onClick = {
color = if(color == Color.Green){
Color.Red
}else{
Color.Green
}
},modifier = Modifier.padding(10.dp)) {
Text(text = "测试一下: compositionLocalOf")
}
}
}
mBoxValue1、mBoxValue2、mBoxValue3
三个静态变量
//MainActivity.kt
companion object{
var boxValue1 = 0
var boxValue2 = 0
var boxValue3 = 0
}
有了上面的经验,我们来看看下面这个问题
3.为什么能用current获取值?
我们看到CompositionLocalProvider入参ProvidedValue<*>
//androidx.compose.runtime.ProvidedValue
class ProvidedValue<T> internal constructor(
val compositionLocal: CompositionLocal<T>,
val value: T,
val canOverride: Boolean
)
当执行了currentComposer.startProviders(values)
之后
override fun startProviders(values: Array<out ProvidedValue<*>>) {
//先获取当前的CompositionLocalMap
val parentScope = currentCompositionLocalScope()
...
val currentProviders = invokeComposableForResult(this) {
compositionLocalMapOf(values, parentScope)
}
...
val providers: CompositionLocalMap
val invalid: Boolean
if (inserting) {
//返回新的parentScope赋值给providers
providers = updateProviderMapGroup(parentScope, currentProviders)
invalid = false
hasProvider = true
} else {
...
}
if (invalid && !inserting) {
//更新providerUpdates当前group位置的providers
providerUpdates[reader.currentGroup] = providers
}
...
//把providers数据写入到group中,最终保存在SlotTable的slots数组中
start(compositionLocalMapKey, compositionLocalMap, false, providers)
}
此时我们再来看一下current如何读取数据的
//androidx.compose.runtime.CompositionLocal
sealed class CompositionLocal<T> constructor(defaultFactory: () -> T) {
....
@OptIn(InternalComposeApi::class)
inline val current: T
@ReadOnlyComposable
@Composable
get() = currentComposer.consume(this)
}
这里调用了Composer#consume
//androidx.compose.runtime.ComposerImpl
override fun <T> consume(key: CompositionLocal<T>): T =
resolveCompositionLocal(key, currentCompositionLocalScope())
private fun <T> resolveCompositionLocal(
key: CompositionLocal<T>,
scope: CompositionLocalMap
): T = if (scope.contains(key)) {
//从CompositionLocalMap中获取值
scope.getValueOf(key)
} else {
key.defaultValueHolder.value
}
重点看一下这个currentCompositionLocalScope()
//androidx.compose.runtime.ComposerImpl#currentCompositionLocalScope
private fun currentCompositionLocalScope(): CompositionLocalMap {
if (inserting && hasProvider) {
//slot writer返回当前group的索引
var current = writer.parent
while (current > 0) {
if (writer.groupKey(current) == compositionLocalMapKey &&
writer.groupObjectKey(current) == compositionLocalMap
) {
//匹配成功,返回数据
return writer.groupAux(current) as CompositionLocalMap
}
//返回索引处group的父级索引
current = writer.parent(current)
}
}
if (slotTable.groupsSize > 0) {
var current = reader.parent
while (current > 0) {
if (reader.groupKey(current) == compositionLocalMapKey &&
reader.groupObjectKey(current) == compositionLocalMap
) {
//我们从startProviders方法中知道,可以通过此数组获取provider
return providerUpdates[current]
?: reader.groupAux(current) as CompositionLocalMap
}
//返回索引处group的父级索引
current = reader.parent(current)
}
}
//返回父级provider
return parentProvider
}
分析完上面的代码,我们才知道CompositionLocal#current从currentCompositionLocalScope() 里面获取Map数据的
4.3-Composition
1.ControlledComposition
This is the interface used by the Recomposer to control how and when a composition is invalidated and subsequently recomposed.
看了官方的解释,我们大概知道它的作用:
它是:Recomposer用来控制composition失效并重组的【接口】
2.CompositionImpl和WrappedComposition
问题: CompositionImpl和WrappedComposition是在哪儿初始化的?
//androidx.compose.ui.platform.Wrapper_androidKt
private fun doSetContent(
owner: AndroidComposeView,
parent: CompositionContext,
content: @Composable () -> Unit
): Composition {
...
//初始化CompositionImpl
val original = Composition(UiApplier(owner.root), parent)
//初始化WrappedComposition
val wrapped = owner.view.getTag(R.id.wrapped_composition_tag)
as? WrappedComposition
?: WrappedComposition(owner, original).also {
owner.view.setTag(R.id.wrapped_composition_tag, it)
}
wrapped.setContent(content)
return wrapped
}
//androidx.compose.runtime.CompositionKt
//此处初始化了CompositionImpl
fun Composition(
applier: Applier<*>,
parent: CompositionContext
): Composition =
CompositionImpl(parent,applier)
结论:原来在doSetContent
方法里面就初始化好了
回到正题:我们从上面的类关系图,知道CompositionImpl
实现了ControlledComposition接口
,WrappedComposition
实现了Composition
并且构造函数内部还传入了CompositionImpl
,所以它们内部是分工合作:
//androidx.compose.runtime.CompositionImpl
internal class CompositionImpl(private val parent: CompositionContext,
private val applier: Applier<*>,
recomposeContext: CoroutineContext? = null) : ControlledComposition {
....
//🔥slotTable不懂的,可以去4.5目录下面了解一下:4.5-SlotTable 🔥
private val slotTable = SlotTable()
//由Composer调用record添加进来的
//record怎么调用的?请往下翻一下,在这个代码区域结束的后面有分析。
private val changes = mutableListOf<Change>()
//从slotTable中删除RecomposeScope的时候就会设置为true
internal var pendingInvalidScopes = false
//如果键对象被修改,关联的范围RecomposeScope无效
private val observations = IdentityScopeMap<RecomposeScopeImpl>()
//这个集合维护了一组已经被组合解除的重组范围
//在下一次调用recordModificationsOf时会被忽略
private val observationsProcessed = IdentityScopeMap<RecomposeScopeImpl>()
//失效的RecomposeScope,需要调用recompose()进行重组
private var invalidations = IdentityArrayMap<RecomposeScopeImpl, IdentityArraySet<Any>?>()
private val composer: ComposerImpl = ComposerImpl(...){
//大家可以看源码自己前后分析一下
//后面用于移除LocalInspectionTables添加的CompositionData
//composer可以获取slotTable,slotTable是CompositionData子类
parent.registerComposer(it)
}
....
override fun setContent(content: @Composable () -> Unit) {
this.composable = content
//初始化compose并应用更改,此处的parent就是Recomposer
parent.composeInitial(this, composable)
}
//重组
override fun recompose(): Boolean = synchronized(lock) {
drainPendingModificationsForCompositionLocked()
composer.recompose(takeInvalidations()).also { shouldDrain ->
if (!shouldDrain) drainPendingModificationsLocked()
}
}
//请打开源码【Recomposer.kt】查看:recompositionRunner(...)
//里面有这样一段代码会触发此处:knownCompositions.fastForEach { it.invalidateAll() }
//knownCompositions就是在composeInitial里面添加的
override fun invalidateAll() {
synchronized(lock) {
slotTable.slots.forEach { (it as? RecomposeScopeImpl)?.invalidate() }
}
}
}
//androidx.compose.ui.platform.WrappedComposition
private class WrappedComposition(
val owner: AndroidComposeView,
val original: Composition
) : Composition, LifecycleEventObserver {
...
override fun setContent(content: @Composable () -> Unit) {
...
//original = CompositionImpl
original.setContent {
....
inspectionTable.add(currentComposer.compositionData)
....
//🔥不懂的可以去4.2目录看:4.2-CompositionLocalProvider 🔥
CompositionLocalProvider(LocalInspectionTables provides inspectionTable) {
ProvideAndroidCompositionLocals(owner, content)
}
}
}
}
上面的一段代码:val changes = mutableListOf<Change>()
我们在注释中提到一个问题:Composer#record 什么时候调用的?
Composer调用了 record(change) 才会往changes列表中添加change
大家打开代码:androidx.compose.runtime.ComposerImpl#updateValue
//androidx.compose.runtime.ComposerImpl#updateValue
internal fun updateValue(value: Any?) {
if (inserting) {
//把value 设置当前的data slot中
writer.update(value)
if (value is RememberObserver) {
//原来是这里调用了record方法
record { _, _, rememberManager -> rememberManager.remembering(value) }
}
}
....
}
4.4-ComposableLambdaImpl
看到此处的应该是从文章上面提到:invokeComposable(this, content)
方法调用看过来的,我们来看看这个方法:
//androidx.compose.runtime.ComposerKt
internal fun invokeComposable(composer: Composer, composable: @Composable () -> Unit) {
val realFn = composable as Function2<Composer, Int, Unit>
realFn(composer, 1)
}
这里会执行Function2的invoke
方法,composable
指的就是ComposableLambdaImpl
,所以会执行到下面的invoke
方法:
//androidx.compose.runtime.internal.ComposableLambdaImpl
override operator fun invoke(c: Composer, changed: Int): Any? {
//重新启动Group并调用addRecomposeScope()
//如果RecomposeScope无效Group将被重组
val c = c.startRestartGroup(key)
//跟踪:找到第一个无效范围并替换它,如果没有无效范围则记录它
trackRead(c)
//数据有变更 = 脏数据
val dirty = changed or if (c.changed(this)) differentBits(0) else sameBits(0)
val result = (_block as (c: Composer, changed: Int) -> Any?)(c, dirty)
//lambda传递给updateScope,【按需】进行范围重组
c.endRestartGroup()?.updateScope(this as (Composer, Int) -> Unit)
return result
}
原来ComposableLambdaImpl
实现了Funtion2....Function21
的所有invoke
方法
class ComposableLambdaImpl:ComposableLambda {...}
interface ComposableLambda:Function2,....Function21
看一下上面的Composer#startRestartGroup
内部实现:
//androidx.compose.runtime.ComposerImpl#startRestartGroup
override fun startRestartGroup(key: Int): Composer {
start(key, null, false, null)
addRecomposeScope()
return this
}
private fun addRecomposeScope() {
if (inserting) {
//初始化重组范围
val scope = RecomposeScopeImpl(composition as CompositionImpl)
//往栈中添加scope
//ComposerImpl#getCurrentRecomposeScope 会从栈顶获取scope
//往上翻一翻,上面的trackRead(..)方法中就使用到了这个栈顶的scope:『composer.recomposeScope』
invalidateStack.push(scope)
//把scope设置到当前data slot中
updateValue(scope)
//更新currentToken=snapshotID并设置skipped为false表示范围没有被跳过
scope.start(snapshot.id)
} else {
//CompositionImpl#invalidate触发后会把scope添加到invalidations集合中
//addRecomposeScope的前面执行了start(...),parent=currentGroup
//返回移除的元素Invalidation
val invalidation = invalidations.removeLocation(reader.parent)
//slot reader获取slots数组中currentSlot数据
val scope = reader.next() as RecomposeScopeImpl
//用于判断是否需要进行重组
scope.requiresRecompose = invalidation != null
invalidateStack.push(scope)
scope.start(snapshot.id)
}
}
4.5-SlotTable
SlotTable是composition slot space的Gap Buffer实现
与Gap Buffer 相似,这是一个在连续空间中存储数据的类型,底层采用数组实现,区别于数组的地方是,它的剩余空间,称为 Gap,可以根据需要移动到 Slot Table 中的任一区域,
这种实现有助于重组过程,可以轻松插入和删除元素。我们打开Composition.kt文件查看,CompositionImpl内部的成员变量中初始化了一个SlotTable,上面的注释写到它的作用:【存储重组所需要的组合信息】,
可组合函数的创建存储在SlotTable中。
我给大家找到几个介绍 GapBuffer 的链接,建议先去学习一下:
Gap Buffer Data Structure
https://www.geeksforgeeks.org/gap-buffer-data-structure/GapBuffer
https://en.wikipedia.org/wiki/Gap_buffer
https://blog.csdn.net/ai_xiangjuan/article/details/79254691
我们来看看 SlotTable (插槽表
) 里面的部分代码:
//省略部分代码...
internal class SlotTable : CompositionData, Iterable<CompositionGroup> {
//存储group信息的数组
var groups = IntArray(0)
private set
//存储group插槽的数组
var slots = Array<Any?>(0) { null }
private set
//当前活动的锚点列表
internal var anchors: ArrayList<Anchor> = arrayListOf()
}
interface CompositionGroup : CompositionData {
//同级中,标识group的值
//如果使用composable key则可以是对象
val key: Any
//SlotTreeKt#sourceInformationContextOf()中使用了sourceInfo数据
val sourceInfo: String?
//如果group表示的是node则是非空,否则为空
//上面的groups.isNode(group)可以判断是否是node
//androidx.compose.runtime.GroupIterator#next 里面node获取的方法就说明了一切
val node: Any?
//存储在此group槽表中的数据
//包含:更改的参数值、记忆参数传递的值、记忆返回的最后一个值
val data: Iterable<Any?>
}
//composer在合成过程中跟踪的数据
interface CompositionData {
//迭代group内部的composition data
val compositionGroups: Iterable<CompositionGroup>
//groups数组没有数据的时候为true
val isEmpty: Boolean
}
SlotTable 可能长下面这样,我们用下划线 _
表示Gap
H l l o _ _ _ _ _ _
我们要在H
后面插入新的数据,那么就需要把Gap移动到H
后面
H _ _ _ _ _ _ _ l l o
移动完成之后,我们在H
后面插入我们的新值e
H e _ _ _ _ _ _ l l o
4.6-Applier
Applier 是一个接口,提供回调
//代码来自:Applier.kt
interface Applier<N> {
val current: N // 当前处理的节点
fun onBeginChanges() {}
fun onEndChanges() {}
fun down(node: N)
fun up()
fun insertTopDown(index: Int, instance: N) // 添加节点(自顶向下)
fun insertBottomUp(index: Int, instance: N)// 添加节点(自底向上)
fun remove(index: Int, count: Int) //删除节点
fun move(from: Int, to: Int, count: Int) // 移动节点
fun clear()
}
添加节点
有两种方法,示例图如下:
4.7-这几种setContent分别是做什么的?
//创建ComposeView设置为Activity的根视图
ComponentActivity.setContent(){}
//设置ComposeUI,调用 createComposition时发生组合
ComposeView.setContent(){}
//这是kotlin的扩展函数,返回Composition,用ViewGroup作为前缀,函数内部可以使用this使用ViewGroup
ViewGroup.setContent(){}
//内部监听AndroidComposeView是否执行完onAttachedToWindow,内部调用invoke触发内部回调
Wrapper.setContent(){}
//内部会调用Recomposer#composeInitial 初始化compose
Composition.setContent(){}
5.总结
简单的总结一下步骤如下:
(1). ComponentActivity.setContent内部:
初始化ComposeView并调用setContentView添加到根视图,当触发ComposeView#onAttachedToWindow,会添加一个childView『AndroidComposeView』
(2). 接下来会:
初始化CompositionImpl(UiApplier(layoutNode), …),初始化WrappedComposition,CompositionImpl传入WrappedComposition,再调用WrappedComposition#setContent
(3). 当执行到CompositionImpl#setContent
会触发Recomposer#composeInitial
调用,
执行到doCompose(...)
内部会调用invokeComposable(this, content)
经过一些列调用,
后面会执行到content.value?.invoke() => ComposableLambdaImpl#invoke
触发@Composable Text
加载,Text控件内部也是用了Layout可组合项
,
在ReusableComposeNode(...)
内部会执行ComposerImpl#createNode
创建一个LayoutNode并设置到slot data中
(4). 当执行到composeInitial(...)
里面的CompositionImpl#applyChanges
,内部会通过invoke
触发ComposerImpl#createNode
方法里面的UiApplier#insertBottomUp
调用,从底部向上构建树,最终会触发:LayoutNode#attach
(5). 当触发AbstractComposeView#onMeasure
调用之后,内部会执行child.measure(...)『child 即 AndroidComposeView』
,触发AndroidComposeView#onMeasure
,
调用measureAndLayoutDelegate.updateRootConstraints(...)
更新root LayoutNode约束,并把LayoutNode添加到relayoutNodes的TreeSet中;
接着measureAndLayoutDelegate.measureAndLayout()
就会遍历relayoutNodes,内部仅遍历,符合如下条件的LayoutNode:layoutNode.isPlaced || layoutNode.canAffectParent || layoutNode.alignmentLines.required
如果LayoutNode的layoutState == NeedsRemeasure
会执行doMeasure
内部根据条件执行LayoutNode#remeasure
判断Size是否发生变化,如果LayoutNode的 measuredByParent==InMeasureBlock
会执行requestRemeasure(...)
请求重新测量此节点和受其影响的节点;
如果measuredByParent == InLayoutBlock
会执行requestRelayout(...)
,请求此layoutNode和受其位置影响的节点的重新布局;
如果LayoutNode的layoutState == NeedsRelayout && layoutNode.isPlaced
会执行LayoutNode#place
,内部会触发LayoutNodeWrapper#placeAt
,然后会回调onLayerBlockUpdated
,方法内部会执行:LayoutNode#createLayer
来创建OwnedLayer
,当触发OwnedLayer#resize
会调用RenderNodeLayer#invalidate
,符合条件会触发AndroidComposeView#invalidate
measureAndLayoutDelegate.measureAndLayout()
执行完,调用setMeasuredDimension(...)
此时AndroidComposeView#onMeasure结束,此时再执行ComposeView#onMeasure的setMeasuredDimension(...)
(6). 执行AbstractComposeView#onLayout -> AndroidComposeView#onLayout
(7). 执行AndroidComposeView#dispatchDraw绘制,绘制LayoutNode数据的地方在这里:canvasHolder.drawInto(canvas) { root.draw(this) }
,
点击查看3.11-dispatchDraw代码分析文章来源:https://www.toymoban.com/news/detail-410234.html
精简版-UI创建布局绘制流程图如下:文章来源地址https://www.toymoban.com/news/detail-410234.html
到了这里,关于Jetpack Compose UI创建布局绘制流程+原理 —— 内含概念详解(手撕源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!