- Component. A bundle of immutable data (Props) and pure functions that can transform the props into UI.
- LithoView. A subclass of
android.view.ViewGroupthat is used to render a
- ComponentTree. A class that holds a single root
Componentand controls its lifecycle.
- LayoutState. Responsible for calculating a layout and holding onto the output of the calculation.
- MountState. Responsible for mounting the
LithoViewhas exactly one
- InternalNode. Class which represents a single node in the layout of a
- LayoutOutput. Lightweight representation of the output of the layout pass. The
LayoutStateproduces a List of
LayoutOutputs that are then mounted by the
We recommend you start by watching this video: it covers most of the core concepts above, as well as the fundamentals of how a Litho component gets turned into a
View on screen.
Layout calculation is controlled by the
ComponentTree. There are two mains ways that a
ComponentTree will kick off a layout calculation:
- If the bounds are known in advance, then the user can call
- When the
ComponentTreeis set on a
LithoView#onMeasure()will cause a layout to be calculated, unless the
ComponentTreealready has a valid layout (e.g. from an earlier
LayoutState#createTree. The first step in calculating layout is creating the tree of components (a tree of
InternalNodes). This is done by recursively resolving each Component in the hierarchy, which for
Columnmeans creating a new
InternalNodeand adding each of their children, and for all other Components just means calling
LayoutState#measureTree. The second step in the calculation is measuring the tree. To do this the
InternalNodes just defer to their underlying Yoga nodes, and Yoga does the calculation for us.
LayoutState#collectResults. Now that we have created and measured the tree, we collect the results into a lightweight representation of elements that need mounting and throw away the
When calculating new layout on a
ComponentTree that already has an existing layout, it is common that much of the layout is actually quite similar to before (e.g. you might change the color of an icon when you click on it, but leave the remaining UI the same). In order to take advantage of the similarities in cases such as this, we do something called
LayoutState that we create has a tree of
DiffNodes that store the results of the layout that we calculated, including measurements. When creating a new
LayoutState for a
ComponentTree that already has one, we use the
DiffNode tree to skip work where possible.
MountState#mount) takes place for the first time when the
LithoView is first laid out, and again every time the visible portion of the
LithoView changes (provided that incremental mount is enabled). The mount step receives a
Rect indicating the current visible area of the LithoView, and it uses that
Rect to determine which of the
LayoutOutputs from the Layout phase need to be either mounted (if they are now on the screen and they weren't before) or unmounted (if they are no longer on the screen).
For each Component that needs to be mounted, we first acquire the
Drawable defined in the
onCreateMountContent method. If possible, we acquire the mount content from our recycling pool (which is stored on a per-context basis), but if the pool is empty, then we create a new one. After that, we call
mount, and then
bind. When unmounting, we call
unbind and then
Each time mount is called, we also call
MountState#processVisibilityOutputs, which determines whether any visibility events need to be fired, and fires them if so.
Mounting (including firing visibility events) is bound to the UI thread, so operations called here should be as lightweight as possible.
Mount diffing is a technique that Litho uses to avoid unmounting and mounting content when it is not necessary. Before mounting, we look at the currently mounted content and compare it with the content that we want to mount. If the currently mounted content does not need updating, then we don't call