Litho介绍和原理分析

Litho介绍和原理分析一 Litho 简介 Litho 是 Facebook 推出的一套高效构建 AndroidUI 的框架 主要目的是提升 RecycleView 复杂列表的滑动性能和内存占用 官网原文介绍如下 Lithoisadecl UI onAndroid Itallowsyout

一、Litho简介

Litho是Facebook推出的一套高效构建Android UI的框架,主要目的是提升RecycleView复杂列表的滑动性能和内存占用。官网原文介绍如下:

当我们在使用recycleview 或者listview来承载一些列表页的时候,如果遇到页面中有很多类型不同的item或者item的布局比较复杂,会遇到滑动过程帧率骤降的过程(需要重新inflate 对应的xml来生成对应的控件,并进行装载),并且虽然listview或者recycleview已经提供了复用的机制,但是否能够高效回收还取决于每个开发者的功力。因此,Litho应运而生。

使用

1、依赖导入

按照自己的相关诉求进行导入,前面几个是必选的,另外的看使用情况。

 // Litho implementation 'com.facebook.litho:litho-core:0.32.0' implementation 'com.facebook.litho:litho-widget:0.32.0' annotationProcessor 'com.facebook.litho:litho-processor:0.32.0' // SoLoader implementation 'com.facebook.soloader:soloader:0.5.1' // For integration with Fresco implementation 'com.facebook.litho:litho-fresco:0.32.0' // For testing testImplementation 'com.facebook.litho:litho-testing:0.32.0' // Sections implementation 'com.facebook.litho:litho-sections-core:0.32.0' implementation 'com.facebook.litho:litho-sections-widget:0.32.0' compileOnly 'com.facebook.litho:litho-sections-annotations:0.32.0' annotationProcessor 'com.facebook.litho:litho-sections-processor:0.32.0' 

2、第一个例子

 final ComponentContext context = new ComponentContext(this); final Component component = Text.create(context) .text("Hello World") .textSizeDip(50) .textColor(Color.RED) .build(); LithoView lithoView = LithoView.create(context, component); 

通过上面的代码,就可以创建一个TextView并显示在Activity上面。

3、列表开发

Litho底层也是使用了RecycleView,在上层做了一层封装,如果需要使用到litho的来显示列表数据,可以使用下面的方式,先定义一个Group,然后再实现自己的Item对象。

@GroupSectionSpec public class ListSectionSpec { 
    @OnCreateChildren static Children onCreateChildren(final SectionContext c) { 
    Children.Builder builder = Children.create(); for (int i = 0; i < 32; i++) { 
    builder.child( SingleComponentSection.create(c) .key(String.valueOf(i)) .component(ListItem.create(c).build())); } return builder.build(); } } @LayoutSpec public class ListItemSpec { 
    @OnCreateLayout static Component onCreateLayout(ComponentContext c) { 
    return Column.create(c) .paddingDip(ALL, 16) .backgroundColor(Color.WHITE) .child(Text.create(c).text("Hello world").textSizeSp(40)) .child(Text.create(c).text("Litho tutorial").textSizeSp(20)) .build(); } } 

在这里插入图片描述

三、特性

1、异步布局

Litho可以提前异步测量和布局UI,而不是阻塞主线程。按照常规的实现,Android上对于页面所有的View操作,包括measurelayoutdraw的操作都是在UI线程执行的,这样子主要是为了保证页面不会因为多线程出现错乱。如果我们的页面比较复杂,那么measurelayout的时间就会很长,导致页面滑动的时候出现卡顿。Litho提供了异步布局的方案来优化解决这种问题。对于不同线程操作可能会存在的错乱问题,Litho是使用Immutability(不变性)来解决的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QYTnsHBg-51)(media/340.jpg)]

实现原理

  • 为什么会出现错乱?

在了解异步的实现之前,我们先来看看,为啥会出现错乱的问题?

public class SomeExampleClass { 
    private int mCounter; public String getThisOrThat() { 
    if (mCounter > 10) { 
    return "this"; } else { 
    mCounter++; return "that"; } } } 

如上,如果有多个线程要在SomeExampleClass的同一实例上调用getThisOrThat,等到第二个线程调用getThisOrThat,尝试读取mCounter时,第一个线程可能正在执行mCounter ++,我们将无法确定第二个线程实际从mCounter读取的值是什么。
因此通常存在的问题是,代码中存在一个可变状态(mCounter),并且有多个线程试图写入和读取它。当编写尝试在多个线程上分配工作的应用程序时,就会出现最常见的竞争问题。
这个就是为啥在传统实现上,多个线程操作UI相关的逻辑会很复杂,因为Android的view 是有状态和多变的,例如TextView的为文案设置提供的setText方法,如果要在其他线程也调用这个方法来改变文案,那么意味着,必须解决用户在布局计算发生时从另一个线程调用setText并更改当前文本的问题(谁先谁后)。




  • 如何实现异步?

看回上面的问题,之所以会出现多线程问题,是因为TextView的状态是可变的,有没有另外一种写法,可以实现等价的功能,并且是不依赖于这种可变状态的呢?如果一个对象在创建之后,就不再可变了,是否是就可以解决这种问题?

 public static class Result { 
    public final int mCounter; public final String mValue; public Result(int counter, int value) { 
    mCounter = counter; mValue = value; } } public class SomeExampleClass { 
    public static Result getThisOrThat(int counterValue) { 
    if (counterValue > 10) { 
    return new Result(counterValue, "this"); } else { 
    return new Result(counterValue + 1, "that"); } } } 

如上,同样是对应 getThisOrThat,无论是哪个线程进行调用,只要输入正确,就能返回对应的结果。Litho在布局的时候就是使用这种方式来实现的。每一个Component都是一个不可变的对象,以@Prop@State相关的数据来作为布局功能的所有输入。
这也说明了为啥litho要求 @Prop@State是不可变的,要不然,也就没有了布局仅是纯功能的属性了。
可以看到,这种实现方式会带来额外的对象分配(内存占用),这种额外的占用是必须的,但Litho 通过使用池和代码生成来最小化额外的对象内存占用。
Litho 提供了 setRootsetRootSync 两个方法来实现同步和异步的效果。异步的实现,会使用Litho的layout线程
来进行布局计算,异步也就意味着布局结果不会立马得出,因此这种方式只在 RecyclerBinder上使用。








  • 源码分析
 final ComponentContext context = new ComponentContext(this); final Component component = Text.create(context) .text("Hello World") .textSizeDip(50) .textColor(Color.RED) .build(); LithoView lithoView = LithoView.create(context, component); 

以创建一个最简单的Hello World为例,相关时序图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SfJbb2Vy-1576508743153)(media/15765077465122.jpg)]

在创建view显示的过程中,最终会调用到LayoutState做测量和布局的操作,并以一个LayoutOunt的对象进行输出。ComponentTree里面通过方法了getDefaultLayoutThreadLooper,获得排版线程的Looper(有单线程模型,也有多线程模型),最终会post 一个CalculateLayoutRunnable进行布局和测量的相关工作。相关的输出和状态会存放在LayoutState。当需要绘制的时候,拿出里面的LayoutOutput进行draw 展示。
ps:如果还是不能理解,可以按照上面的流程,下对应的断点,整个流程debug 看一次,就能够理解了。

2、视图扁平化

Litho使用yoga进行布局,并且会自动减少UI包含的ViewGroup数量,去除不必要的结构,进行UI拍平。

  • 如何实现?
    在这里插入图片描述
    譬如想要实现上面的效果,Android 上一般的实现,需要增加一个FrameLayout作为容器,左边是一个ImageView,右边是一个LinearLayout包含两个TextView(当然,有更高优的实现,这里只是说很多人脑海浮现的实现方式)。对应的布局边界如下:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5tvM7j3u-1576594787233)(media/15765915066731.jpg)]






降级处理

上面的例子,是以一张图的形式来展示我们想要的布局,但是,如果这个样式的某块区域需要进行事件响应的时候(点击、长按或者触摸),就没法处理为一个Drawable了,需要还是以View控件的形式挂载到LithoView(ViewGroup)上。也就是如果有点击事件绑定,需要降级到更Android 普通的实现一样的view。

3、更细粒度的回收机制

对于长列表的内容展示,我们一般都是使用ListView或者RecycleView来进行展示。因为ListView或者RecycleView都提供了回收机制,让我们在滚动过程中可以重用已经创建出来的view,而不是重新创建、测量和布局,能够减少UI线程的工作量,达到一个流畅的效果。这种在我们的列表比较简单(都是同种类型的item)的情况下是没有问题的。但如果对于复杂的场景,譬如,每次滚动,要展示的类型都不一样,这势必会为UI线程带来更大的工作量。因此,除了上面提供的异步排版能够减少这种新建UI类型带来的损耗之外,Litho 提供了一个更加可扩展和更高效率的回收机制。
因为在litho中,布局的表示和即将用在屏幕上渲染的视图是完全分离的,这就意味着,可以以更小的维度来定义每个item。譬如每个简单的TEXT、Image,都可以是单独的一个item,这种子,回收的粒度就细化到每个小控件维度了,而不是我们通用的一大快。

实现原理

  • 如何实现?
    复用原理如下:每个item,在会受到的时候,会进行解绑,拆分成每个绘制单元,由Litho的缓存进行分类回收和复用。
    在这里插入图片描述




4、声明式

Litho使用声明式API来定义UI组件,使用者只需要基于一组不可变的输入来描述UI布局,其他的都交由框架来处理即可。框架层使用了注解和代码生成的方式来创建你声明的组件。

参考链接

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/200487.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 上午10:44
下一篇 2026年3月20日 上午10:44


相关推荐

  • 5. 卡特兰数(Catalan)公式、证明、代码、典例.

    5. 卡特兰数(Catalan)公式、证明、代码、典例.1 定义卡特兰数 卡塔兰数 英文名 Catalannumbe 是组合数学中一个常出现在各种计数问题中出现的数列 其前几项为 从第零项开始 C0 1 C1 1 C2 2 C3 5 C4 14 C5 42 C6 132 C7 429

    2026年3月18日
    2
  • SAT到底是变难了还是变简单了?

    SAT到底是变难了还是变简单了?

    2021年8月28日
    57
  • BeanUtils的populate方法之日期处理

    BeanUtils的populate方法之日期处理BeanUtils的populate方法之日期处理BeanUtils.populate(Obj,Map);可以使map转对象,但是如果是Obj有个日期字段而Map中这个字段为空时候就会报错,无法赋值,这个时候就需要一个日期转换器!首先写一个日期转换器packagecom.hzdl.examination.web;importorg.apache.commons.beanutils.BeanUtilsBean;importorg.apache.commons.beanutils.Conve

    2022年7月13日
    29
  • Luajit 概述「建议收藏」

    Luajit 概述「建议收藏」一、JIT即时编译器JIT:即时编译器。将频繁执行的代码,通过JIT编译器编译成机器码缓存起来,下次再调用时直接执行机器码。相比与原生Lua的逐条执行虚拟机指令效率更高。对于那些只执行一次的代码,则保持于原生Lua一样,逐条执行。JIT带来的效率提升,并不一定能抵消编译效率的下降。当虚拟机执行指令时并不会立刻用JIT进行编译。只有部分指令需要JIT进行编译,JIT将决定那些代码将被编译。延迟编译有…

    2022年10月7日
    4
  • linux的格式化命令是什么,linux格式化命令如何使用

    linux的格式化命令是什么,linux格式化命令如何使用若在 t 参数中指定 ext2 我们再来介绍 mkfs ext3mkfs reiserfsmkfs ext2mkdosfsm msdosmkfs vfat 例如 whichevercom Usetune2fs cor itooverride 这样就格式化好了 也是调用的这个工具 如何使用 linux 格式化命令呢 步骤如下 linux 格式化磁盘

    2026年3月20日
    2
  • 使用PHPExcel导入导出excel格式文件

    使用PHPExcel导入导出excel格式文件

    2021年12月1日
    44

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号