ComposeView
传统的 Android UI 是通过 View 对象形式来构建的,而 Compose 的 UI 是通过方法的形式构建的。创建一个全新的 Android Compose 项目,它会自动生成 MainActivity :
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
ComposeReadTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
ComponentActivity.setContent
这里有一个 Compose 的入口 setContent { ... } ,这是一个拓展方法:
public fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit
) {
val existingComposeView = window.decorView
.findViewById<ViewGroup>(android.R.id.content)
.getChildAt(0) as? ComposeView
if (existingComposeView != null) with(existingComposeView) {
setParentCompositionContext(parent)
setContent(content)
} else ComposeView(this).apply {
setParentCompositionContext(parent)
setContent(content)
setOwners()
setContentView(this, DefaultActivityContentLayoutParams)
}
}
decorView 是整个窗口视图树的根节点,在这里,会将 decorView 中的 R.id.content 的 View 强转为 ComposeView,作为根视图,然后从它去设置内容,如果强转失败或其他原因导致 existingComposeView 为 null,则新建一个 ComposeView 对象。
总之第二步来到了 ComposeView 的 setContent。
ComposeView.setContent
class ComposeView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr) {
// ...
private val content = mutableStateOf<(@Composable () -> Unit)?>(null)
fun setContent(content: @Composable () -> Unit) {
shouldCreateCompositionOnAttachedToWindow = true
this.content.value = content
if (isAttachedToWindow) {
createComposition()
}
}
}
在 setContent 中,有两个重要步骤,一是将方法参数保存到 content 中;第二个步骤是调用 createComposition,即创建组合。
AbstractComposeView.createComposition
fun createComposition() {
check(parentContext != null || isAttachedToWindow) {
"createComposition requires either a parent reference or the View to be attached" +
"to a window. Attach the View or call setParentCompositionReference."
}
ensureCompositionCreated()
}
private fun ensureCompositionCreated() {
if (composition == null) {
try {
creatingComposition = true
composition = setContent(resolveParentCompositionContext()) {
Content()
}
} finally {
creatingComposition = false
}
}
}
在 ensureCompositionCreated 里调用 setContent 创建组合(这里的 setContent 是 AbstractComposeView 的拓展函数),并传递一个 上下文和 Content() 。
上下文是通过 resolveParentCompositionContext() 获取到的,其作用是为当前视图找到合适的 CompositionContext 作为父上下文。这涉及到从父视图、缓存中、或从窗口的 Recomposer(负责管理 Compose 组件的重组操作)中查找正确的上下文。
private fun resolveParentCompositionContext() = parentContext
?: findViewTreeCompositionContext()?.cacheIfAlive()
?: cachedViewTreeCompositionContext?.get()?.takeIf { it.isAlive }
?: windowRecomposer.cacheIfAlive()
Content() 就是其实现类型 ComposeView 的 setContent 传递进来的内容:
class ComposeView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr) {
private val content = mutableStateOf<(@Composable () -> Unit)?>(null)
@Composable
override fun Content() {
content.value?.invoke()
}
}
AbstractComposeView.setContent
继续分析 AbstractComposeView 的拓展函数 setContent :
internal fun AbstractComposeView.setContent(
parent: CompositionContext,
content: @Composable () -> Unit
): Composition {
GlobalSnapshotManager.ensureStarted()
val composeView =
if (childCount > 0) {
getChildAt(0) as? AndroidComposeView
} else {
removeAllViews(); null
} ?: AndroidComposeView(context, parent.effectCoroutineContext).also {
addView(it.view, DefaultLayoutParams)
}
return doSetContent(composeView, parent, content)
}
这里是将 AbstractComposeView 的第一个子节点强转为 AndroidComposeView,如果为空,新建一个 AndroidComposeView,并将其添加到 AbstractComposeView 中。最后执行 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 对象,传递了两个参数:
- Applier:这里新建了一个 UiApplier 对象,UiApplier 是对视图树结构进行操作的包装器,可以插入/删除/移动节点;
- CompositionContext:组合上下文。
fun Composition(
applier: Applier<*>,
parent: CompositionContext
): Composition =
CompositionImpl(
parent,
applier
)
WrappedComposition.setContent
然后又创建了一个 WrappedComposition,这里先去尝试获取 tag 强转,失败则新建,然后调用 WrappedComposition 的setContent:
override fun setContent(content: @Composable () -> Unit) {
owner.setOnViewTreeOwnersAvailable {
if (!disposed) {
val lifecycle = it.lifecycleOwner.lifecycle
lastContent = content
if (addedToLifecycle == null) {
addedToLifecycle = lifecycle
// this will call ON_CREATE synchronously if we already created
lifecycle.addObserver(this)
} else if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
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) {
inspectionTable.add(currentComposer.compositionData)
currentComposer.collectParameterInformation()
}
LaunchedEffect(owner) { owner.boundsUpdatesEventLoop() }
CompositionLocalProvider(LocalInspectionTables provides inspectionTable) {
ProvideAndroidCompositionLocals(owner, content)
}
}
}
}
}
}
在这个方法中,owner 是个 AndroidComposeView,setContent 方法通过与 AndroidComposeView 的生命周期管理集成,确保 Composable 内容只在正确的生命周期状态下被渲染,并且在视图树和生命周期都准备好时,才会执行。
- owner.setOnViewTreeOwnersAvailable:
- 这里的 owner 是 AndroidComposeView 的实例,它在视图树可用时执行回调。这通过 setOnViewTreeOwnersAvailable 进行处理,确保 viewTreeOwners 可用时再执行后续逻辑。
- 一旦 viewTreeOwners 可用,代码中的逻辑才会继续执行。
- 检查 disposed 状态:
- 如果 Composition 已经被销毁(disposed 为 true),不会继续执行任何操作,防止内存泄漏和无效操作。
- 获取并设置生命周期:
- val lifecycle = it.lifecycleOwner.lifecycle:通过 viewTreeOwners 获取 LifecycleOwner 并从中获取 lifecycle 对象,来跟踪 Compose 组件的生命周期。
- 将传入的 content 保存为 lastContent,以便在必要时重新渲染。
- 添加生命周期观察者:
- 如果该 Composition 尚未添加到生命周期中(addedToLifecycle == null),则将 lifecycle 添加为观察者,并同步调用 ON_CREATE,即立即触发生命周期事件。这确保了 Composition 可以在 ON_CREATE 之后正确初始化。
- 如果 lifecycle 的状态已经是 CREATED 或更高的状态,说明组件已经被创建了,接下来会设置 original.setContent 来渲染实际内容。
- 设置实际内容 (original.setContent):
- 使用原始 Composition 的 setContent 来设置实际的 Composable 内容。
- 其中使用了 @Suppress("UNCHECKED_CAST") 来从视图的 tag 中检索 inspectionTable(用于调试或检查 Compose 的内部状态的结构)。
- inspectionTable 用来跟踪 Composition 的数据和参数信息,以支持 Compose 的调试和检查。
- 将当前 Composer 的数据(currentComposer.compositionData)添加到 inspectionTable 中,并收集参数信息。
- LaunchedEffect 和 CompositionLocalProvider:
- 使用 LaunchedEffect 来启动 owner.boundsUpdatesEventLoop(),它可能是一个监听视图边界变化的循环任务,用于响应视图的动态调整。
- 通过 CompositionLocalProvider 提供 LocalInspectionTables(当前组合的调试信息表)和 AndroidCompositionLocals(将 Android 上下文、生命周期等本地化信息传递给 Compose 组件)给 Composable 内容。确保内容可以正确使用本地化的上下文信息。
CompositionImpl.setContent
override fun setContent(content: @Composable () -> Unit) {
composeInitial(content)
}
private fun composeInitial(content: @Composable () -> Unit) {
check(!disposed) { "The composition is disposed" }
this.composable = content
parent.composeInitial(this, composable)
}
parent 这里是 CompositionContext:
CompositionContext.composeInitial
internal abstract fun composeInitial(
composition: ControlledComposition,
content: @Composable () -> Unit
)
其实现是 Recomposer。
Recomposer.composeInitial
internal override fun composeInitial(
composition: ControlledComposition,
content: @Composable () -> Unit
) {
val composerWasComposing = composition.isComposing
try {
composing(composition, null) {
composition.composeContent(content)
}
} catch (e: Exception) {
processCompositionError(e, composition, recoverable = true)
return
}
// TODO(b/143755743)
if (!composerWasComposing) {
Snapshot.notifyObjectsInitialized()
}
synchronized(stateLock) {
if (_state.value > State.ShuttingDown) {
if (composition !in knownCompositions) {
addKnownCompositionLocked(composition)
}
}
}
try {
performInitialMovableContentInserts(composition)
} catch (e: Exception) {
processCompositionError(e, composition, recoverable = true)
return
}
try {
composition.applyChanges()
composition.applyLateChanges()
} catch (e: Exception) {
processCompositionError(e)
return
}
if (!composerWasComposing) {
// Ensure that any state objects created during applyChanges are seen as changed
// if modified after this call.
Snapshot.notifyObjectsInitialized()
}
}
-
**composerWasComposing 变量:**首先记录 composition.isComposing,判断当前 composition 是否正在处于组合过程中。这个标志用于在后续逻辑中判断是否需要进行某些后处理。
-
composing 函数:
composing(composition, null) { ... }是一个关键的组合函数,它确保 composeContent 调用时在一个安全的组合上下文中执行。composition.composeContent(content)实际执行了传入的 Composable 内容的组合操作,将 UI 树生成和渲染逻辑交给 Compose 运行时进行处理。
-
异常处理:
- 在组合过程中使用 try-catch 块来捕获异常。异常被捕获后,通过 processCompositionError 进行处理。
- 如果发生了组合错误(如某个 Composable 抛出了异常),该异常会被处理为可恢复的错误(recoverable = true),并且函数会提前返回,避免继续执行后续的操作。
-
Snapshot.notifyObjectsInitialized():
- 在完成组合后,调用 Snapshot.notifyObjectsInitialized() 通知 Compose 框架,任何在组合过程中创建的状态对象现在已经被初始化。这是 Jetpack Compose 的快照系统的一部分,用于跟踪和管理 UI 状态。
-
同步状态检查:
- 使用 synchronized(stateLock) 同步块对状态进行线程安全的检查和更新,确保在多线程环境下不会出现并发问题。
- 如果当前 composition 的状态超过了 State.ShuttingDown,并且 composition 尚未在已知组合中,则将其添加到 knownCompositions 集合中。这意味着该组合处于有效状态,并且需要被 Compose 进行管理。
-
插入可移动内容 (performInitialMovableContentInserts):
- 调用 performInitialMovableContentInserts(composition) 以处理任何需要移动的内容插入。这个函数处理的是组合中需要动态插入或移动的部分,例如一些动态布局的组件。
- 如果在这个过程中发生异常,同样使用 processCompositionError 进行处理。
-
应用更改 (applyChanges 和 applyLateChanges):
- composition.applyChanges():应用在组合过程中计算出来的变更,更新 UI 树。
- composition.applyLateChanges():应用后续需要处理的变更,确保所有状态更新和 UI 改变都正确反映在屏幕上。
- 如果在这些过程中抛出异常,则捕获并处理,确保组合不会崩溃。
-
再次调用 Snapshot.notifyObjectsInitialized():
如果 composerWasComposing 为 false(表示这是一次新的组合),再次调用 Snapshot.notifyObjectsInitialized()。这样可以确保在应用完组合和状态更改后,任何在 applyChanges 期间创建的状态对象在后续修改时都会被检测到。