Jetpack Compose setContent 源碼分析
從何入手
先來(lái)了解一下Compose架構(gòu)的分層設(shè)計(jì)
| 由上至下 | 說(shuō)明 | 運(yùn)用 |
|---|---|---|
| material | 提供了Material Design一套風(fēng)格體系,包含主題系統(tǒng)、樣式化組件 | Button、AlertDialog等等 |
| foundation | 相當(dāng)于面向開(kāi)發(fā)者的跟基層,包含完整的UI系統(tǒng)和實(shí)用布局 | LazyList、Row、Column等等 |
| animation | 動(dòng)畫(huà)層,包含平移、漸變、縮放等等,并且提供了方便開(kāi)發(fā)者的動(dòng)畫(huà)組件 | animate**AsState、Transition、Animatable、AnimatedVisibility等等 |
| ui | ui相關(guān)的基礎(chǔ)功能,包括自定義布局、繪制、觸摸反饋等等 | ComposeView、Layout、LayoutNode等等 |
| runtime | 最底層的概念模型,包括數(shù)據(jù)結(jié)構(gòu)、狀態(tài)處理、數(shù)據(jù)同步等等 | mutableStateOf、remember等等 |
| compiler | 基于Kotlin的編譯器插件 | 處理@Composable函數(shù) |
了解完整體的分層設(shè)計(jì),而要分析的setContent()源碼是處于ui層和runtime層。
Compose版本號(hào)采用
1.0.1代碼塊頂部注釋為類位置
先看一段Compose代碼
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 入口函數(shù)
setContent { // content 函數(shù)對(duì)象
Text("Hello Compose")
}
}
}
寫(xiě)法很簡(jiǎn)單,所以我們的目的也很簡(jiǎn)單,就是看看setContent()函數(shù)講Text("Hello Compose")進(jìn)行怎么樣的邏輯傳遞。為了之后的代碼跟隨能夠更加的清晰,后續(xù)講到入口函數(shù)對(duì)象就等同于Text("Hello Compose")
ComponentActivity.setContent()
// androidx.activity.compose.ComponentActivity
public fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit
) {
// 1. 通過(guò)decorView找到ContentView,再獲得第一個(gè)ComposeView
val existingComposeView = window.decorView
.findViewById<ViewGroup>(android.R.id.content)
.getChildAt(0) as? ComposeView
// 2. 如果ComposeView為空則走初始化流程
if (existingComposeView != null) with(existingComposeView) {
setParentCompositionContext(parent)
setContent(content)
} else ComposeView(this).apply {
// 3. 初始化ComposeView肯定為空,則進(jìn)入這邊
setParentCompositionContext(parent)
// 4. 把入口函數(shù)對(duì)象傳入ComposeView
setContent(content)
setOwners()
// 5. 把ComposeView設(shè)置進(jìn)ContentView
setContentView(this, DefaultActivityContentLayoutParams)
}
}
從第一步可以了解到,原來(lái)在當(dāng)前窗口的decorView中的android.R.id.content中第0個(gè)位置,會(huì)存在一個(gè)ComposeView,所以ComposeView結(jié)構(gòu)圖可以理解成:

難道這個(gè)ComposeView肯定跟傳統(tǒng)的View/ViewGroup有關(guān)系嗎?遇事不決就看看Compose的繼承關(guān)系。

果真如此,也能驗(yàn)證Compose架構(gòu)的分層設(shè)計(jì)中上下關(guān)系,animation、foundation的工作也是在ComposeView中進(jìn)行,另外還能延伸出一個(gè)問(wèn)題,那是不是Compose和傳統(tǒng)View/ViewGroup能夠互相交互并且能在同一個(gè)activity混合開(kāi)發(fā)?這個(gè)目前沒(méi)有研究過(guò),就先略過(guò)了。。。
第二步的話,由于分析就是初始化狀態(tài)的流程且existingComposeView肯定為空,所以直接進(jìn)入到第三步,傳遞了parent到setParentCompositionContext方法,按照案例的入口函數(shù),
// 入口函數(shù)
setContent(parent = null) { // content 函數(shù)對(duì)象
Text("Hello Compose")
}
所以當(dāng)前parent為空,再看看方法里具體做了哪些。
// androidx.compose.ui.platform.AbstractComposeView
fun setParentCompositionContext(parent: CompositionContext?) {
parentContext = parent
}
只是賦值操作,目前parentContext為空
第四步發(fā)現(xiàn)又調(diào)用了一個(gè)相同方法名的setContent,在之后的分析還會(huì)出現(xiàn)一個(gè)類似的setContent,每一個(gè)方法作用都是不一樣,那看看當(dāng)前ComposeView.setContent(入口函數(shù)對(duì)象)做了什么事情
// androidx.compose.ui.platform.ComposeView
class ComposeView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr) {
private val content = mutableStateOf<(@Composable () -> Unit)?>(null)
@Suppress("RedundantVisibilityModifier")
protected override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
private set
@Composable
override fun Content() {
content.value?.invoke()
}
fun setContent(content: @Composable () -> Unit) {
// 翻譯為 “應(yīng)該在附加到窗口上創(chuàng)建合成”,做一個(gè)標(biāo)記
shouldCreateCompositionOnAttachedToWindow = true
// 賦值操作
this.content.value = content
if (isAttachedToWindow) {
// 暫時(shí)不會(huì)進(jìn)入
createComposition()
}
}
}
整個(gè)ComposeView的類結(jié)構(gòu)
AbstractComposeView
abstract fun Content()
open val shouldCreateCompositionOnAttachedToWindow: Boolean
ComposeView
val content = mutableStateOf
fun setContent()
這樣一看理解ComposeView也很容易了,它只是做了一個(gè)預(yù)備動(dòng)作,告訴AbstractComposeView有人(調(diào)用ComposeView.setContent()后)把預(yù)備狀態(tài)修改成準(zhǔn)備就緒啦(shouldCreateCompositionOnAttachedToWindow = true),并且入口函數(shù)對(duì)象也存儲(chǔ)好了,等你來(lái)拿走處理了(Content() = 入口函數(shù)對(duì)象)。
所以前兩句就是給AbstractComposeView修改和準(zhǔn)備需要的值。
而if對(duì)于初始化分析來(lái)說(shuō),根本不會(huì)進(jìn)入,因?yàn)楫?dāng)前的ComponentActivity.setContent,還沒(méi)用執(zhí)行到第五步setContentView,所以isAttachedToWindow肯定為false。
那再看第五步,setContentView再熟悉不過(guò)了,添加到android.R.id.content中。之后流程居然就沒(méi)了?第四步都準(zhǔn)備就緒了,之后就沒(méi)有啟動(dòng)邏輯了?
回想一下之前講的ComposeView的繼承關(guān)系,是View的子類,那setContentView后ComposeView被附加到Window上后,會(huì)回調(diào)方法onAttachedToWindow()。我們知道ComposeView的沒(méi)有這個(gè)方法的,在父類AbstractComposeView中找到了該實(shí)現(xiàn)。
AbstractComposeView.onAttachedToWindow()
// androidx.compose.ui.platform.AbstractComposeView
override fun onAttachedToWindow() {
super.onAttachedToWindow()
previousAttachedWindowToken = windowToken
// ComposeView 修改后預(yù)備狀態(tài)
if (shouldCreateCompositionOnAttachedToWindow) {
// 真正啟動(dòng)的地方
ensureCompositionCreated()
}
}
shouldCreateCompositionOnAttachedToWindow這個(gè)值Compose.setContent()已經(jīng)修改為true,所以直接看ensureCompositionCreated()
// androidx.compose.ui.platform.AbstractComposeView
private fun ensureCompositionCreated() {
// composition 初始化流程為空
if (composition == null) {
try {
creatingComposition = true
// 又來(lái)一個(gè) setContent?
composition = setContent(resolveParentCompositionContext()) {
// 抽象方法
Content()
}
} finally {
creatingComposition = false
}
}
}
composition還不確定是做什么的,先一步一步解析這個(gè)setContent涉及的內(nèi)容,看看方法的入?yún)ⅰ?/p>
// androidx.compose.ui.platform.Wrapper_androidKt.class
internal fun ViewGroup.setContent(
parent: CompositionContext,
content: @Composable () -> Unit
): Composition {
...
parent以目前的知識(shí)點(diǎn)也無(wú)法對(duì)它進(jìn)行分析,而content就是我們的入口函數(shù)對(duì)象。調(diào)用的Content()抽象方法,方法的實(shí)現(xiàn)之前講過(guò)的,內(nèi)部返回的就是ComposeView.content.value
為了了解parent為何物,就要看看resolveParentCompositionContext()方法。
resolveParentCompositionContext()
// androidx.compose.ui.platform.AbstractComposeView
private fun resolveParentCompositionContext() = parentContext
?: findViewTreeCompositionContext()?.also { cachedViewTreeCompositionContext = it }
?: cachedViewTreeCompositionContext
?: windowRecomposer.also { cachedViewTreeCompositionContext = it }
一大堆判空邏輯,那就挨個(gè)分析。
parentContext: 就是ComponentActivity.setContent()中第三步流程,并且還知道當(dāng)前案例傳遞為空。
findViewTreeCompositionContext(): 再看看源碼怎么尋找
// androidx.compose.ui.platform.WindowRecomposer_androidKt.class
fun View.findViewTreeCompositionContext(): CompositionContext? {
var found: CompositionContext? = compositionContext
if (found != null) return found
var parent: ViewParent? = parent
while (found == null && parent is View) {
found = parent.compositionContext
parent = parent.getParent()
}
return found
}
初始化流程默認(rèn)compositionContext為空,所以找上一層 parentVew, 然而整個(gè)while也是找不到需要的compositionContext,所以findViewTreeCompositionContext也會(huì)返回null。
cachedViewTreeCompositionContext: 是跟隨上一個(gè)findViewTreeCompositionContext()邏輯走的,上一個(gè)為空則cachedViewTreeCompositionContext也會(huì)返回空。
windowRecomposer: 看看源碼怎么尋找
// androidx.compose.ui.platform.WindowRecomposer_androidKt.class
internal val View.windowRecomposer: Recomposer
get() {
check(isAttachedToWindow) {
"Cannot locate windowRecomposer; View $this is not attached to a window"
}
// 擴(kuò)展函數(shù)拿到 contentChild
val rootView = contentChild
return when (val rootParentRef = rootView.compositionContext) {
// 初始化流程為null
null -> WindowRecomposerPolicy.createAndInstallWindowRecomposer(rootView)
is Recomposer -> rootParentRef
else -> error("root viewTreeParentCompositionContext is not a Recomposer")
}
}
private val View.contentChild: View
get() {
var self: View = this
var parent: ViewParent? = self.parent
while (parent is View) {
// 拿到 ComposeView 后返回
if (parent.id == android.R.id.content) return self
self = parent
parent = self.parent
}
return self
}
contentChild: View的擴(kuò)展屬性,當(dāng)前view的parentId等于android.R.id.content時(shí),會(huì)返回當(dāng)前view。根據(jù)之前的知識(shí)(ComposeView結(jié)構(gòu)圖)就可以得知,當(dāng)前view肯定是初始化setContentView傳入的ComposeView。
再根據(jù)當(dāng)前為初始化流程,所以rootView.compositionContext肯定也是為空,會(huì)進(jìn)入WindowRecomposerPolicy.createAndInstallWindowRecomposer(rootView)??吹竭@邊整個(gè)流程都是在尋找compositionContext,但是第一次進(jìn)入頁(yè)面發(fā)現(xiàn)各種方法加擴(kuò)展屬性尋找,都?jí)焊也坏健姆椒?code>create***,其實(shí)就可以知道找不到我就去給你創(chuàng)建一個(gè),并且這個(gè)方法還把rootView(ComposeView)傳入,在還沒(méi)有看createAndInstallWindowRecomposer(rootView),其實(shí)也可以猜出來(lái),創(chuàng)建compositionContext是肯定的,并且還會(huì)把創(chuàng)建的compositionContext存儲(chǔ)到rootView(ComposeView)其中,之后再次尋找就有緩存可尋。
WindowRecomposerPolicy.createAndInstallWindowRecomposer(rootView)
刪減了部分代碼
// androidx.compose.ui.platform.WindowRecomposer_androidKt.class
fun interface WindowRecomposerFactory {
fun createRecomposer(windowRootView: View): Recomposer
companion object {
val LifecycleAware: WindowRecomposerFactory = WindowRecomposerFactory { rootView ->
// 4. createRecomposer() lambda調(diào)用createLifecycleAwareViewTreeRecomposer()
rootView.createLifecycleAwareViewTreeRecomposer()
}
}
}
private fun View.createLifecycleAwareViewTreeRecomposer(): Recomposer {
...
// 5. 創(chuàng)建 Recomposer
val recomposer = Recomposer(contextWithClock)
...
// 6. 返回 Recomposer
return recomposer
}
object WindowRecomposerPolicy {
private val factory = AtomicReference<WindowRecomposerFactory>(
// 2. 創(chuàng)建 WindowRecomposerFactory
WindowRecomposerFactory.LifecycleAware
)
...
// rootView = ComposeView
internal fun createAndInstallWindowRecomposer(rootView: View): Recomposer {
// 1. factory.get() 創(chuàng)建 WindowRecomposerFactory
// 3. createRecomposer(rootView) 調(diào)用 WindowRecomposerFactory.createRecomposer()
val newRecomposer = factory.get().createRecomposer(rootView)
// 7. compositionContext 賦值到 ComposeView Tag數(shù)組中
rootView.compositionContext = newRecomposer
...
// 8. 整個(gè)創(chuàng)建 compositionContext 結(jié)束
return newRecomposer
}
}
第一步到第三步是用工廠模式WindowRecomposerFactory來(lái)創(chuàng)建Recomposer?;叵胍幌挛覀儾皇且肅ompositionContext對(duì)象的么?那這個(gè)Recomposer是何方神圣?
看一下繼承關(guān)系

對(duì)于Recomposer怎么理解,官方注解如下
/**
* The scheduler for performing recomposition and applying updates to one or more [Composition]s.
* 用于執(zhí)行重組并將更新應(yīng)用到一個(gè)或多個(gè) [Composition] 的調(diào)度程序。
* 先了解定義,之后會(huì)分析Recomposer的內(nèi)部運(yùn)用
*/
所以第四步到第六步就是創(chuàng)建CompositionContext,并且返回賦值給對(duì)象newRecomposer。
第七步是newRecomposer賦值給rootView.compositionContext, 看看賦值過(guò)程。
// androidx.compose.ui.platform.WindowRecomposer_androidKt.class
var View.compositionContext: CompositionContext?
get() = getTag(R.id.androidx_compose_ui_view_composition_context) as? CompositionContext
set(value) {
setTag(R.id.androidx_compose_ui_view_composition_context, value)
}
就是把CompositionContext添加到ComposeView的Tag數(shù)組,在Compose ui層的體系中,發(fā)現(xiàn)有很多類似的寫(xiě)法,通過(guò)set/get Tag來(lái)存取值,且之后要分析的流程也有類似寫(xiě)法。

第八步整個(gè)獲取CompositionContext流程終于結(jié)束了。
ViewGroup.setContent()
這個(gè)時(shí)候就要再貼一下之前的流程分析。
// androidx.compose.ui.platform.AbstractComposeView
private fun ensureCompositionCreated() {
// composition 初始化流程為空
if (composition == null) {
try {
creatingComposition = true
// 又來(lái)一個(gè) setContent
composition = setContent(resolveParentCompositionContext()) {
// 抽象方法
Content()
}
} finally {
creatingComposition = false
}
}
}
// androidx.compose.ui.platform.Wrapper_androidKt.class
internal fun ViewGroup.setContent(
parent: CompositionContext, // Recomposer
content: @Composable () -> Unit // 入口函數(shù)對(duì)象
): Composition {
GlobalSnapshotManager.ensureStarted()
// 獲得 AndroidComposeView
val composeView =
if (childCount > 0) {
getChildAt(0) as? AndroidComposeView
} else {
removeAllViews(); null
} ?: AndroidComposeView(context).also {
// 添加到 ViewGroup(ComposeView)
addView(it.view, DefaultLayoutParams)
}
// 又來(lái)一個(gè)doSetContent
return doSetContent(composeView, parent, content)
}
ViewGroup.setContent方法很簡(jiǎn)單,獲得AndroidComposeView添加到ComposeView,然后再調(diào)用doSetContent()。
以初始化流程來(lái)看肯定會(huì)創(chuàng)建AndroidComposeView,AndroidComposeView的結(jié)構(gòu)圖就可以理解為:

// // androidx.compose.ui.platform.Wrapper_androidKt.class
private fun doSetContent(
owner: AndroidComposeView,
parent: CompositionContext, // Recomposer
content: @Composable () -> Unit // 入口函數(shù)對(duì)象
): Composition {
...
// 創(chuàng)建 Composition
val original = Composition(UiApplier(owner.root), parent)
// 常見(jiàn) WrappedComposition 并賦值到 AndroidComposeView 的Tag組數(shù)
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)
}
// 又來(lái)一個(gè)setContent
wrapped.setContent(content)
return wrapped
}
Composition(UiApplier(owner.root), parent): 之前分析了parent,就是通過(guò)工廠模式創(chuàng)建的Recomposer,那要理解Composition還需要看看UiApplier是一個(gè)什么類?
UiApplier
internal class UiApplier(
root: LayoutNode
)
參數(shù)需要一個(gè)LayoutNode,那這個(gè)LayoutNode又是什么了?
先從目前的流程場(chǎng)景分析的的話,就直接查看傳遞的AndroidComposeView.root
// androidx.compose.ui.platform.AndroidComposeView
override val root = LayoutNode().also {
it.measurePolicy = RootMeasurePolicy
it.modifier = Modifier
.then(semanticsModifier)
.then(_focusManager.modifier)
.then(keyInputModifier)
}
原來(lái)AndroidComposeView有這個(gè)屬性對(duì)象,布局層次結(jié)構(gòu)中的一個(gè)元素,用于compose UI構(gòu)建,那又有一個(gè)問(wèn)題了,當(dāng)前是確定AndroidComposeView有這個(gè)元素,那入口函數(shù)對(duì)象也是用于compose UI構(gòu)建,那它有沒(méi)有了?
答案是肯定有的,查看一下Text()最底層實(shí)現(xiàn)源碼
// androidx.compose.foundation.text.CoreTextKt.class
internal fun CoreText(
...
) {
...
Layout(
content = if (inlineComposables.isEmpty()) {
{}
} else {
{ InlineChildren(text, inlineComposables) }
},
modifier = modifier
.then(controller.modifiers)
...
,
measurePolicy = controller.measurePolicy
)
...
}
// androidx.compose.ui.layout.LayoutKt.class
@Composable inline fun Layout(
content: @Composable () -> Unit,
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
ReusableComposeNode<ComposeUiNode, Applier<Any>>(
factory = ComposeUiNode.Constructor,
update = {
set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
set(density, ComposeUiNode.SetDensity)
set(layoutDirection, ComposeUiNode.SetLayoutDirection)
},
skippableUpdate = materializerOf(modifier),
content = content
)
}
最終是調(diào)用了ReusableComposeNode<ComposeUiNode, Applier<Any>>,而ComposeUiNode就是LayoutNode的實(shí)現(xiàn)接口。
internal class LayoutNode : Measurable, Remeasurement, OwnerScope, LayoutInfo, ComposeUiNode
所以結(jié)合繼承關(guān)系可以把LayoutNode結(jié)構(gòu)理解成:

對(duì)于UiApplier,就可以小結(jié)一下
UiApplier是一個(gè)視圖工具類,讓其他使用者能更方便的操作root: LayoutNode。內(nèi)部維護(hù)了一個(gè)stack = mutableListOf<T>()代表類似上圖的視圖樹(shù),當(dāng)視圖樹(shù)插入、移除、移動(dòng)等等都會(huì)更新stack。
那問(wèn)題又來(lái)了,UiApplier是Composition的入?yún)?,究竟是哪個(gè)對(duì)象來(lái)操作UiApplier了?
Composition
// androidx.compose.runtime.CompositionKt.class
fun Composition(
applier: Applier<*>, // UiApplier
parent: CompositionContext // Recomposer
): Composition =
CompositionImpl(
parent,
applier
)
internal class CompositionImpl(
private val parent: CompositionContext, // Recomposer
private val applier: Applier<*>, // UiApplier
recomposeContext: CoroutineContext? = null
) : ControlledComposition {
...
// 保存所有視圖組合的信息(核心數(shù)據(jù)結(jié)構(gòu))
private val slotTable = SlotTable()
...
private val composer: ComposerImpl =
ComposerImpl(
applier = applier,
parentContext = parent,
slotTable = slotTable,
abandonSet = abandonSet,
changes = changes,
composition = this
).also {
parent.registerComposer(it)
}
...
}
SlotTable是一個(gè)很大的話題,類似于 Gap Buffer (間隙緩沖區(qū)) ,技術(shù)有限這邊就不展開(kāi)說(shuō)了,官方可能覺(jué)得也很難理解,在知乎上說(shuō)明了SlotTable的執(zhí)行模式。
所以可以說(shuō)Composition是一個(gè)連接器,把視圖樹(shù)(UiApplier)和調(diào)度程序(Recomposer)連接到一起,當(dāng)監(jiān)聽(tīng)到數(shù)據(jù)(比如mutableState)變化或者添加視圖等等,Recomposer會(huì)通過(guò)Composition通知SlotTable更新視圖組合信息、UiApplier更新視圖樹(shù)等等,在分析UiApplier的Text()底層實(shí)現(xiàn)源碼中,最終是創(chuàng)建了ReusableComposeNode,那在看看對(duì)應(yīng)的源碼。
@Composable inline fun <T : Any, reified E : Applier<*>> ReusableComposeNode(
noinline factory: () -> T,
update: @DisallowComposableCalls Updater<T>.() -> Unit
) {
if (currentComposer.applier !is E) invalidApplier()
currentComposer.startReusableNode()
if (currentComposer.inserting) {
currentComposer.createNode { factory() }
} else {
currentComposer.useNode()
}
currentComposer.disableReusing()
Updater<T>(currentComposer).update()
currentComposer.enableReusing()
currentComposer.endNode()
}
currentComposer就是Composition中的composer
調(diào)用startReusableNode()就是操作SlotReader,而SlotReader其實(shí)就是SlotTable的讀取控制器,對(duì)應(yīng)的SlotTable也有SlotWriter寫(xiě)入控制器。
調(diào)用createNode()就是操作UiApplier
WrappedComposition.setContent()
終于講到WrappedComposition.setContent(),它是一個(gè)帶有處理生命周期的包裹,把AndroidComposeView和Composition包裹,當(dāng)AndroidComposeView被附加到ComposeView后,會(huì)添加LifecycleEventObserver,之后觸發(fā)生命周期Lifecycle.Event.ON_CREATE,會(huì)先調(diào)用連接器Composition的setContent(),再執(zhí)行調(diào)度程序Recomposer的composeInitial(),再調(diào)用連接器Composition的setContent() -> composeContent(),再調(diào)用連接器中的帶有SlotTable的composer.composeContent(),最終執(zhí)行invokeComposable()來(lái)組合我們的入口函數(shù)對(duì)象。
調(diào)用鏈看起來(lái)很懵逼,關(guān)鍵是要理解Composition、composer、CompositionContext(Recomposer)、UiApplier每個(gè)對(duì)象的關(guān)系、職責(zé)和工作的傳遞,最后再看看相關(guān)涉及的源碼。
// androidx.compose.ui.platform.WrappedComposition
private class WrappedComposition(
val owner: AndroidComposeView,
val original: Composition //
) : Composition, LifecycleEventObserver {
private var disposed = false
private var addedToLifecycle: Lifecycle? = null
private var lastContent: @Composable () -> Unit = {}
override fun setContent(content: @Composable () -> Unit) {
owner.setOnViewTreeOwnersAvailable {
if (!disposed) {
val lifecycle = it.lifecycleOwner.lifecycle
lastContent = content
if (addedToLifecycle == null) {
// 1. 初始化流程第一次會(huì)進(jìn)入
addedToLifecycle = lifecycle
// 2. 設(shè)置生命周期監(jiān)聽(tīng)
lifecycle.addObserver(this)
} else if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
// 4. 調(diào)用連接器的setContent
original.setContent {
@Suppress("UNCHECKED_CAST")
val inspectionTable =
owner.getTag(R.id.inspection_slot_table_set) as?
MutableSet<CompositionData>
?: (owner.parent as? View)?.getTag(R.id.inspection_slot_table_set)
as? MutableSet<CompositionData>
if (inspectionTable != null) {
@OptIn(InternalComposeApi::class)
inspectionTable.add(currentComposer.compositionData)
currentComposer.collectParameterInformation()
}
LaunchedEffect(owner) { owner.keyboardVisibilityEventLoop() }
LaunchedEffect(owner) { owner.boundsUpdatesEventLoop() }
// 入口函數(shù)對(duì)象
CompositionLocalProvider(LocalInspectionTables provides inspectionTable) {
ProvideAndroidCompositionLocals(owner, content)
}
}
}
}
}
}
override fun dispose() {
if (!disposed) {
disposed = true
// 清空帶生命周期的包裹
owner.view.setTag(R.id.wrapped_composition_tag, null)
// 移除生命周期艦艇
addedToLifecycle?.removeObserver(this)
}
original.dispose()
}
override val hasInvalidations get() = original.hasInvalidations
override val isDisposed: Boolean get() = original.isDisposed
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_DESTROY) {
dispose()
} else if (event == Lifecycle.Event.ON_CREATE) {
if (!disposed) {
// 3. 重新執(zhí)行
setContent(lastContent)
}
}
}
}
Composition -> original.setContent()
// androidx.compose.runtime.CompositionImpl
override fun setContent(content: @Composable () -> Unit) {
check(!disposed) { "The composition is disposed" }
this.composable = content
// parent = Recomposer = ComposeView.compositionContext
parent.composeInitial(this, composable)
}
Recomposer -> parent.composeInitial(this, composable)
// androidx.compose.runtime.Recomposer
internal override fun composeInitial(
composition: ControlledComposition, // Recomposer
content: @Composable () -> Unit // 入口函數(shù)對(duì)象
) {
val composerWasComposing = composition.isComposing
composing(composition, null) {
// 走到連接器
composition.composeContent(content)
}
...
}
composition.composeContent(content)
// androidx.compose.runtime.CompositionImpl
override fun composeContent(content: @Composable () -> Unit) {
synchronized(lock) {
drainPendingModificationsForCompositionLocked()
// 走到內(nèi)部composer
composer.composeContent(takeInvalidations(), content)
}
}
composer.composeContent(takeInvalidations(), content)
// androidx.compose.runtime.ComposerImpl
internal fun composeContent(
invalidationsRequested: IdentityArrayMap<RecomposeScopeImpl, IdentityArraySet<Any>?>,
content: @Composable () -> Unit // 入口函數(shù)對(duì)象
) {
runtimeCheck(changes.isEmpty()) { "Expected applyChanges() to have been called" }
doCompose(invalidationsRequested, content)
}
private fun doCompose(
invalidationsRequested: IdentityArrayMap<RecomposeScopeImpl, IdentityArraySet<Any>?>,
content: (@Composable () -> Unit)? // 入口函數(shù)對(duì)象
) {
runtimeCheck(!isComposing) { "Reentrant composition is not supported" }
trace("Compose:recompose") {
snapshot = currentSnapshot()
invalidationsRequested.forEach { scope, set ->
val location = scope.anchor?.location ?: return
invalidations.add(Invalidation(scope, location, set))
}
invalidations.sortBy { it.location }
nodeIndex = 0
var complete = false
isComposing = true
try {
startRoot()
// Ignore reads of derivedStatOf recalculations
observeDerivedStateRecalculations(
start = {
childrenComposing++
},
done = {
childrenComposing--
},
) {
if (content != null) {
startGroup(invocationKey, invocation)
// 真正處理入口函數(shù)對(duì)象的地方
invokeComposable(this, content)
endGroup()
} else {
skipCurrentGroup()
}
}
endRoot()
complete = true
} finally {
isComposing = false
invalidations.clear()
providerUpdates.clear()
if (!complete) abortRoot()
}
}
}
startRoot()、endGroup() 是操作SlotTable
最后再看看invokeComposable做了什么
internal fun invokeComposable(composer: Composer, composable: @Composable () -> Unit) {
@Suppress("UNCHECKED_CAST")
val realFn = composable as Function2<Composer, Int, Unit>
realFn(composer, 1)
}
原來(lái)最終把我們的入口函數(shù)對(duì)象強(qiáng)轉(zhuǎn)成Function2,但是當(dāng)要去查看Function2是做什么的時(shí)候,沒(méi)辦法找到類。

原因是這邊是compiler(基于kotlin的編譯器插件)做的事情,它把我們的入口函數(shù)進(jìn)行了修改,添加了一些參數(shù),比如Int對(duì)應(yīng)的就是groupId,所以才能強(qiáng)轉(zhuǎn)成功。拿官方的例子來(lái)說(shuō)明一下就能明白了。
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(
text="Count: $count",
onPress={ count += 1 }
)
}
fun Counter($composer: Composer, groupId: Int) {
$composer.start(groupId)
var count by remember { mutableStateOf(0) }
Button(
text="Count: $count",
onPress={ count += 1 }
)
$composer.end()
}
小結(jié)
初次看setContent源碼,什么流程都記不住看不懂,只知道N個(gè)setContent跳來(lái)跳去,當(dāng)把Composition、composer、CompositionContext(Recomposer)、UiApplier每個(gè)對(duì)象的關(guān)系、職責(zé)搞清楚之后,再對(duì)跳轉(zhuǎn)流程理清楚,就能發(fā)現(xiàn)具體的工作流程了。
所分析的setContent()只是Compose Ui體系中的一小部分,發(fā)現(xiàn)分析完后,延伸出了更多的知識(shí)點(diǎn),比如SlotTable工作的流程和時(shí)間復(fù)雜度、LayoutNode和AndroidComposeView相關(guān)的測(cè)量繪制觸摸反饋、mutableState刷新機(jī)制等等,還有分析過(guò)程中提出的一個(gè)問(wèn)題Compose和傳統(tǒng)View/ViewGroup互相交互并且能在同一個(gè)activity混合開(kāi)發(fā)嗎?