internal class ComposerImpl: Composer
Composer interface@Composable function at compile timeprivate var reusing = falseprivate var reusingGroup = -1public abstract class CompositionContext
public val effectCoroutineContext: CoroutineContext; effects for the composition (eg LaunchedEffect)internal abstract fun composeInitial(
composition: ControlledComposition,
content: @Composable () -> Unit,
)public class Recomposer; the context (role) of/referenced by a root compositioninternal inner class CompositionContextImpl
ComposerImpl) that constructed itprivate val parentContext: CompositionContext; contexts (roles) are organised into a tree
CompositionContextImpl::composeInitial delegates to parentContextinternal class RecomposeScopeImpl
internal var owner: RecomposeScopeOwner?public interface RecomposeScope
public fun invalidate()
ComposerRecomposeScopeImpl::invalidate calls this.owner?.invalidate(this, null)public interface ScopeUpdateScopepublic interface IdentifiableRecomposeScopepublic interface Composition
public fun dispose(); should free resources and invalidate itself for further usepublic fun setContent(content: @Composable () -> Unit); should update (calculate and apply changes to) itself as described by the content.public val hasInvalidations: Boolean; should be true if RecomposeScope::invalidate has been called in the compositionpublic val isDisposed: Boolean; should be true if the dispose method has been calledprivate class WrappedComposition: Composition, LifecycleEventObserver, CompositionServices
val owner: AndroidComposeView; owned by an AndroidComposeView: ViewGroupval original: Composition; the composition this: WrappedComposition wraps → intercepts & handles accessprivate var lastContent: @Composable () -> Unit = {}private var addedToLifecycle: Lifecycle? = null
Lifecycle in setContent if available.addObserver(this); observed by this: LifecycleEventObserver
Lifecycle.Event.ON_DESTROY → this.dispose()Lifecycle.Event.ON_CREATE → if !this.disposed, calls this.setContent(this.lastContent)addObserver brings this to the current state of this.addedToLifecycleoverride fun setContent(content: @Composable () -> Unit); assigns content to this.lastContent + arranges a call to original::setContentinternal class CompositionImpl
private var state = RUNNING
private const val RUNNING = 0 in CompositionImpl::clearDeactivatedprivate const val DEACTIVATED = 1 in CompositionImpl::deactivateprivate const val INCONSISTENT = 2 in CompositionImpl::pausedCompositionFinished conditionallyprivate const val DISPOSED = 3 in CompositionImpl::disposepublic sealed interface ControlledComposition: Composition
public val isComposing: Boolean; implemented by this: CompositionImpl to return this.composer.isComposingpublic sealed interface ReusableComposition: Composition public sealed interface PausableComposition: ReusableCompositionpublic interface ObservableComposition; disjoint interfaceinternal interface RecomposeScopeOwnerpublic abstract class ViewGroup
android.view.View; a tree (node) of interactive UI elementsView::dispatchAttachedToWindow called on this: ViewGroup then on each in this.{mChildren, mTransientViews}
View::isAttachedToWindow is true for the receiver; not yet for its childrenView::onAttachedToWindow called on the receiver; its children then traversed and attached depth firstinternal class AndroidComposeView
DefaultLifecycleObserver: LifecycleObserverOwner; the connection from compose to the android view system
val root: LayoutNode; the root of the composition tree that (this: AndroidComposeView) as Owner owns/hostsViewTreeOwners class unrelated by inheritance
val lifecycleOwner: LifecycleOwnerval savedStateRegistryOwner: SavedStateRegistryOwnerval viewModelStoreOwner: ViewModelStoreOwner?private var _viewTreeOwners: ViewTreeOwners? by mutableStateOf(null)val viewTreeOwners: ViewTreeOwners? by derivedStateOf { _viewTreeOwners }override fun onAttachedToWindow() instantiates ViewTreeOwners and assigns it to this._viewTreeOwners with
this.findViewTreeLifecycleOwner(): LifecycleOwner?
.lifecycle.addObserver(this) called on itthis.findViewTreeSavedStateRegistryOwner(): SavedStateRegistryOwner?this.findViewTreeViewModelStoreOwner(): ViewModelStoreOwner?private var onViewTreeOwnersAvailable: ((ViewTreeOwners) -> Unit)? = null
this.viewTreeOwners) if !null then cleared by onAttachedToWindowabstract class AbstractComposeView
private var composition: Composition? = null
this: AbstractComposeView is to have an AndroidComposeView childthis references the composition backed by the Owner::root tree owned by the child viewprivate fun ensureCompositionCreated(); assigns a Composition to this.composition if nulloverride fun onAttachedToWindow(); calls this.ensureCompositionCreated()private var cachedViewTreeCompositionContext: WeakReference<CompositionContext>? = nullprivate var parentContext: CompositionContext? = null; when assigned a !null value, the setter
this.cachedViewTreeCompositionContext = nullthis.composition != null, calls .dispose() on & assigns null to it
this.isAttachedToWindow, calls this.ensureCompositionCreated()fun createComposition()
this.ensureCompositionCreated()ComposeView::setContent if this.isAttachedToWindowabstract fun Content()
@Composable) of this viewthis.createComposition() is calledclass ComposeView
private val content: MutableState<(@Composable () -> Unit)?> = mutableStateOf(null)override fun Content(); calls this.content.value?.invoke()public abstract class AbstractApplier<T>
Applier<T>; manipulates a tree of T nodes
val current: T; the target of operationsoverride var current: T; protected set; the subtype supports traversing the treeval root: T; references the same object throughout the lifetime of the applierprivate val stack = Stack() ; a FIFO stack that keeps track of the nodes it’s visitedinternal class UiApplier: AbstractApplier<LayoutNode>
LayoutNode tree (eg (AndroidComposeView as Owner)::root: LayoutNode)ComponentActivity::setContentpublic fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit
)
content into this: ComponentActivity; populates the activity window view treeComposeView to upsert either from this.window.decorView or by constructing oneComposeView
ComposeView, calls .setParentCompositionContext(parent); sets this.parentContext = parent
this.ensureCompositionCreated()ComposeView, calls .setContent(content)
this.content.value = contentthis.isAttachedToWindow, calls this.createComposition() (→ this.ensureCompositionCreated())ComposeView if it’s new
ComponentActivity, calls .setOwners(), which passes this: ComponentActivity
this.window.decorView.findViewTree{Lifecycle, ViewModelStore, SavedStateRegistry}Owner() == nullthis.window.decorView.setViewTree{Lifecycle, ViewModelStore, SavedStateRegistry}Owner respectivelyComponentActivity, calls .setContentView(/* ComposeView */, DefaultActivityContentLayoutParams)
private val DefaultActivityContentLayoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
)
ComposeViewAbstractComposeView::ensureCompositionCreatedthis.composition == null, resolves its parent and constructs a Compositionthis.isAttachedToWindow; this attached to a windowthis.resolveParentCompositionContext()
this.parentContext if !nullthis.findViewTreeCompositionContext()?.cacheIfAlive() if !null
View hierarchy tree until the first View::compositionContext: CompositionContext? that’s !null found!null, used if .isAlive; cached in this.cachedViewTreeCompositionContext = WeakReference(it)this.cachedViewTreeCompositionContext?.get()?.takeIf { it.isAlive } if !nullthis.windowRecomposer.cacheIfAlive() otherwise; View::windowRecomposer: Recomposer is a computed property
this.isAttachedToWindowView hierarchy tree for the first one parented by a android.R.id.content viewView::compositionContext: CompositionContext? for the found view
is Recomposer → returns the Recomposernull → WindowRecomposerPolicy.createAndInstallWindowRecomposer passed the found viewelse → throws if reachedthis.setContent
internal fun AbstractComposeView.setContent(
parent: CompositionContext,
content: @Composable () -> Unit,
): Composition
ComposeView::setContent by inheritanceparent passed the return value of this.resolveParentCompositionContext()content calls this.Content() when calledGlobalSnapshotManager.ensureStarted()AndroidComposeView either from this.mChildren or by constructing one
this.getChildAt(0) as? AndroidComposeView used if this.childCount > 0AndroidComposeView(this.context, parent.effectCoroutineContext)
View::context: Context; an environment role valid for a lifetime & shared by every reference siteAndroidComposeView (added) made the only child of the this: AbstractComposeView parent (all other children removed)return doSetContent(/* ... */)
private fun doSetContent(
owner: AndroidComposeView,
parent: CompositionContext,
content: @Composable () -> Unit,
): Composition
owner passed the obtained AndroidComposeView childparent relayed the parent: CompositionContext from AbstractComposeView::setContentcontent relayed the content: @Composable () -> Unit from AbstractComposeView::setContentWrappedComposition: Composition either from the owner or by constructing one
owner.view.getTag(R.id.wrapped_composition_tag) as? WrappedComposition used if !nullWrappedComposition(owner, Composition(UiApplier(owner.root), parent)) added to the owner and used
public fun Composition(applier: Applier<*>, parent: CompositionContext)
: Composition
= CompositionImpl(parent, applier).setContent(content) on the obtained WrappedComposition and then returns it(WrappedComposition as Composition)::setContentoverride fun setContent(content: @Composable () -> Unit)
content relayed the content: @Composable () -> Unit from doSetContent, relayed from AbstractComposeView::setContentViewTreeOwners of this.owner: AndroidComposeView if it’s attached to a view, or on attach later
(<activity> as ComponentActivity)::onCreate → ComponentActivity::setContent → ComposeView::setContent call path
AbstractComposeView::ensureCompositionCreated not reached(<activity> as ComponentActivity)::onCreate has returnedAbstractComposeView::onAttachedToWindow called; its children attached after the callback has returned (ie not yet)
AbstractComposeView::onAttachedToWindow → AbstractComposeView::ensureCompositionCreated → AbstractComposeView::setContent → doSetContent → WrappedComposition::setContent calledWrappedComposition::owner: AndroidComposeView not attached yet, but owner.parent as AbstractComposeView isAbstractComposeView::onAttachedToWindow has returned, this.owner: AndroidComposeView attached
AndroidComposeView::onAttachedToWindow called; instantiates ViewTreeOwners → obtained for the remaining stepsComponentActivity registered for (this: AndroidComposeView).findViewTree{Lifecycle, SavedStateRegistry, ViewModelStore}Owner()this.disposed, otherwise sets this.lastContent = content and continuesthis.addedToLifecycle == null; true if the initialiser hasn’t been overridden
.lifecycleOwner.lifecycle: Lifecycle of the obtained ViewTreeOwners to this.addedToLifecycle
ComponentActivity on this code path; already created, started, and resumed.addObserver(this) on it; observed by this: LifecycleEventObserver
addObserver brings this to the current state of this.addedToLifecycle (created → started → resumed)Lifecycle.Event.ON_DESTROY → this.dispose()Lifecycle.Event.ON_CREATE (called on add here) → calls this.setContent(this.lastContent)this.addedToLifecycle != null), if the observed lifetime .currentState.isAtLeast(Lifecycle.State.CREATED), provides the following steps for updating (calculating and applying changes to) its original: Composition
CompositionImpl::setContent on this call pathCompositionImpl::setContentoverride fun setContent(content: @Composable () -> Unit)
this: CompositionImpl by the callerthis.state == DEACTIVATED and assigns RUNNING to the fieldthis.state == RUNNING
INCONSISTENT → this: CompositionImpl must be disposed because the previous composition was cancelledDISPOSED → invalid for use because this: CompositionImpl has been disposed ofDEACTIVATED → should be activated again before setting the contentthis.pendingPausedComposition == null; a pausable composition is already in progress(this: CompositionImpl).state was DEACTIVATED per the cache, calls this.composer.startReuseFromRoot()
this: ComposerImpl) sets this.reusingGroup = /* static */ rootKey; this.reusing = true(this: CompositionImpl).composable = content(this: CompositionImpl).parent.composeInitial(this, composable)
CompositionContext::composeInitial delegates up the hierarchy to the Recomposer: CompositionContext impl(this: CompositionImpl).state was DEACTIVATED per the cache, calls this.composer.endReuseFromRoot()
this: ComposerImpl) checks the invariant; throws unless !this.isComposing && this.reusingGroup == rootKeythis: ComposerImpl) sets this.reusingGroup = -1; this.reusing = falseRecomposer::composeInitialinternal override fun composeInitial(
composition: ControlledComposition,
content: @Composable () -> Unit,
)
composition passed the receiver of CompositionImpl::setContent from it
WrappedComposition::original: Composition on this call pathcontent relayed the content: @Composable () -> Unit from CompositionImpl::setContent
CompositionImpl provided in WrappedComposition::setContentRecomposer::composingprivate inline fun <T> composing(
composition: ControlledComposition,
modifiedValues: MutableScatterSet?,
block: () -> T,
): T
composition relayed the composition: ControlledComposition from Recomposer::composeInitial
(WrappedComposition::original: Composition) as CompositionImpl on this pathWrappedComposition::setContentmodifiedValues passed null from Recomposer::composeInitialblock passed from Recomposer::composeInitial the partial application of ControlledComposition::composeContent to its arguments{ value -> composition.recordReadOf(value) }{ value ->
composition.recordWriteOf(value)
modifiedValues?.add(value)
}block
ControlledComposition::composeContent to the arguments of Recomposer::composeInitialblock after applying the snapshot; throws & disposes on failure(CompositionImpl as ControlledComposition)::recordReadOf(CompositionImpl as ControlledComposition)::recordWriteOf