Mount Specs
A mount spec defines a component that can render views or drawables.
Mount specs should only be created when you need to integrate your own views/drawables with Litho. Mount here refers to the operation performed by all components in a layout tree to extract their rendered state (a View
or a Drawable
) to be displayed.
Mount spec classes should be annotated with @MountSpec
and implement at least an @OnCreateMountContent
method. The other methods listed below are optional.
The lifecycle of mount spec components is as follows:
[BG
= occurs on BG thread when possible -- do not modify the view hierarchy, UI
= can occur on UI thread, PC
= Performance critical -- put as little work in it as possible, use BG
methods instead]
- Run
@OnPrepare
once, before layout calculation.BG
/UI
- Run
@OnMeasure
optionally during layout calculation. This will not be called if Yoga has already determined your component's bounds (e.g. a static width/height was set on the component).BG
/UI
- Run
@OnBoundsDefined
once, after layout calculation. This will be called whether or not@OnMeasure
was called.BG
/UI
- Run
@OnCreateMountContent
before the component is attached to a hosting view. This content may be reused for other instances of this component.UI
- Run
@OnMount
before the component is attached to a hosting view. This will happen when the component is about to become visible when incremental mount is enabled (it is enabled by default).UI
/PC
- Run
@OnBind
after the component is attached to a hosting view.UI
/PC
- Run
@OnUnbind
before the component is detached from a hosting view.UI
/PC
- Run
@OnUnmount
optionally after the component is detached from a hosting view. See incremental mount notes on@OnMount
: they apply in reverse here.UI
/PC
Mounting
Let's start with a simple ColorComponent
that takes a color name as a prop and mounts its respective ColorDrawable
.
- The mount operation has an API very similar to Android's RecyclerView Adapters. It has a
onCreateMountContent
method to create and initialize theView
/Drawable
content if the recycling pool is empty, and anonMount
method to update the recycled content with the current information. - The return type from
onCreateMountContent
should always match the type of the second argument ofonMount
. They are required to be aView
or aDrawable
subclass. This is validated by the annotation processor at build time. - Mounting always happens in the main thread as it might have to deal with Android Views (which are bound to the main thread).
onCreateMountContent
cannot take a@Prop
or any other annotated parameter.- Given that the
@OnMount
method always runs in the UI thread, expensive operations should not be performed in it.
Inter-stage inputs and outputs
You can move heavy operations off the UI thread by performing them in the @OnPrepare
method, which runs only once before the layout calculation is performed and can be executed in a background thread.
Let's say we want to perform the color name parsing off the UI thread in the ColorComponent
above. In order to do this, we need a way to pass values generated in the @OnPrepare
method to the @OnMount
implementation. Litho provides inter-stage inputs and outputs to allow you to do exactly that.
Let's have a look at ColorComponent
with the described @OnPrepare
method.
Using Output<?>
in any of the @MountSpec
methods automatically creates an input for the following stages. In this case, an @OnPrepare
output creates an input for @OnMount
.
The annotation processor will ensure inter-stage invariants are respected at build time e.g. you cannot use outputs from @OnMeasure
in @OnPrepare
as @OnPrepare
always runs before @OnMeasure
.
Measurement
You should implement an @OnMeasure
method whenever you want to define how your component should be measured during the layout calculation.
Now, let's suppose we want our ColorComponent
to have a default width and enforce a certain aspect ratio when its height is undefined.
You can access component props with the @Prop
annotation as usual in @OnMeasure
. SizeSpec's API is analogous to Android's MeasureSpec.
Just like @OnPrepare
, the @OnMeasure
method can also generate inter-stage outputs (accessible via the @FromMeasure
argument annotation) and may be performed in a background thread.
ShouldUpdate
A MountSpec can define a method annotated with @ShouldUpdate
to avoid remeasuring and remounting upon updates.
Invocations of @ShouldUpdate
are dependent on whether a Component is a pure render function. A Component is a pure render function if the result of the rendering only depends on its props and states. This means that the Component shouldn't be accessing any mutable global variable during @OnMount
.
A @MountSpec
can be defined as pure render by using the pureRender parameter of the @MountSpec
annotation.
Only pure render Components can assume that when props do not change remounting won't be needed. A @ShouldUpdate
function can be defined as follows:
The parameters taken from shouldUpdate
are Diffs of Props or State. A Diff is an object containing the value of a @Prop
or a @State
in the old components hierarchy and the value of the same @Prop
or @State
in the new components hierarchy.
In this example this component was defining someStringProp as a String @Prop
. shouldUpdate
will receive a Diff<String>
to be able to compare the old and new value of this @Prop
.
shouldUpdate
has to take into consideration any prop and any states that are used at @OnMount
time. It can safely ignore props and states that are only used at @OnBind
/@OnUnbind
time as these two methods will be executed regardless.
The onMount
attribute on the @ShouldUpdate
annotation controls whether this shouldUpdate
check can happen at mount time. By default, Litho will try to do this reconciliation at layout time, but if layout diffing is turned off it might be useful to set onMount to true in order to execute this check at mount time instead. The onMount
attribute is set to false by default as the equality check might be heavy itself and make mount performances worse.
@ShouldUpdate
annotated methods are currently only supported in @MountSpec
. We have plans to expand the support to complex layouts in the future but at the moment a @ShouldUpdate
annotated method in a @LayoutSpec
would have no effect.
Pre-allocation
When a MountSpec component is being mounted, its View
/Drawable
content needs to be either initialized or reused from the recycling pool. If the pool is empty, a new instance will be created at that time, which might keep the UI thread too busy and drop one or more frames. To mitigate that, Litho can pre-allocate a few instances and put in the recycling pool.
canPreallocate
enables pre-allocation for this MountSpec and poolSize
defines the amount of instances to pre-allocate. For this ColorComponent
example, three instances of ColorDrawable
will be created and put in the recycling pool. This option is recommended for MountSpec components that inflate a complex View
.