日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基本功 | Litho的使用及原理剖析

發(fā)布時間:2024/7/5 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基本功 | Litho的使用及原理剖析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1. 什么是Litho?

Litho是Facebook推出的一套高效構(gòu)建Android UI的聲明式框架,主要目的是提升RecyclerView復(fù)雜列表的滑動性能和降低內(nèi)存占用。下面是Litho官網(wǎng)的介紹:

Litho is a declarative framework for building efficient user interfaces (UI) on Android. It allows you to write highly-optimized Android views through a simple functional API based on Java annotations. It was primarily built to implement complex scrollable UIs based on RecyclerView. With Litho, you build your UI in terms of components instead of interacting directly with traditional Android views. A component is essentially a function that takes immutable inputs, called props, and returns a component hierarchy describing your user interface.

Litho是高效構(gòu)建Android UI的聲明式框架,通過注解API創(chuàng)建高優(yōu)的Android視圖,非常適用于基于Recyclerview的復(fù)雜滾動列表。Litho使用一系列組件構(gòu)建視圖,代替了Android傳統(tǒng)視圖交互方式。組件本質(zhì)上是一個函數(shù),它接受名為Props的不可變輸入,并返回描述用戶界面的組件層次結(jié)構(gòu)。

Litho是一套完全不同于傳統(tǒng)Android的UI框架,它繼承了Facebook一向大膽創(chuàng)新的風(fēng)格,突破性地在Android上實現(xiàn)了React風(fēng)格的UI框架。架構(gòu)圖如下:

應(yīng)用層:上層Android應(yīng)用接入層。

規(guī)范層(API):允許用戶使用聲明式的API(注解)來構(gòu)建符合Flexbox規(guī)范的布局。

布局層:Litho使用可掛載組件、布局組件和Flexbox組件來構(gòu)建布局,其中可掛載組件和布局組件允許用戶使用規(guī)范來定義,各個組件的具體用法下面的組件規(guī)范中會詳細(xì)介紹。在Litho中每一個組件都是一個獨立的功能模塊。Litho的組件和React的組件相類似,也具有屬性和狀態(tài)的概念,通過狀態(tài)的變更來控制組件的展示樣式。

布局測量:Litho使用Yoga來完成組件布局的異步或同步(可根據(jù)場景定制)測量和計算,實現(xiàn)了布局的扁平化。

布局渲染:Litho不僅支持使用View來渲染視圖,還可以使用更輕量的Drawable來渲染視圖。Litho實現(xiàn)了大量使用Drawable來渲染的基礎(chǔ)組件,可以進(jìn)一步拍平布局。

除了上面提到的扁平化布局,Litho還實現(xiàn)了布局的細(xì)粒度復(fù)用和異步計算布局的能力,對于這些功能的實現(xiàn)在Litho的特性及原理剖析中詳細(xì)介紹。下面先介紹一下大家比較關(guān)心的Litho使用方法。

2. Litho的使用

Litho的使用方式相比于傳統(tǒng)的Android來說有些另類,它拋棄了通過XML定義布局的方式,采用聲明式的組件在Java中構(gòu)建布局。

2.1 Litho和原生Android在使用上的區(qū)別

Android傳統(tǒng)布局:首先在資源文件res/layout目錄下定義布局文件xx.xml,然后在Activity或Fragment中引用布局文件生成視圖,示例如下:

<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World"android:textAlignment="center"android:textColor="#666666"android:textSize="40dp" /> public class MainActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.helloworld);} }

Litho布局:Litho拋棄了Android原生的布局方式,通過組件方式構(gòu)建布局生成視圖,示例如下:

public class MainActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ComponentContext context = new ComponentContext(this);final Text.Builder builder = Text.create(context);final Component = builder.text("Hello World").textSizeDip(40).textColor(Color.parseColor("#666666")).textAlignment(Layout.Alignment.ALIGN_CENTER).build();LithoView view = LithoView.create(context, component);setContentView(view);} }

2.2 Litho自定義視圖

Litho中的視圖單元叫做Component,可以直觀的翻譯為“組件”,它的設(shè)計理念來自于React組件化的思想。每個組件持有描述一個視圖單元所必須的屬性和狀態(tài),用于視圖布局的計算工作。視圖最終的繪制工作是由組件指定的繪制單元(View或者Drawable)來完成的。

Litho組件的創(chuàng)建方式也和原生View的創(chuàng)建方式有著很大的區(qū)別。Litho使用注解定義了一系列的規(guī)范,我們需要使用Litho的注解來定義自己的組件生成規(guī)則,最終由Litho在編譯期自動編譯生成真正的組件。

2.2.1 組件規(guī)范

Litho提供了兩種類型的組件規(guī)范,分別是Layout Spec規(guī)范和Mount Spec規(guī)范。下面分別介紹兩種規(guī)范的使用方式:

Layout Spec規(guī)范:用于生成布局類型組件的規(guī)范,布局組件在邏輯上等同于Android中的ViewGroup,用于組織其他組件構(gòu)成一個布局。它要求我們必須使用@LayoutSpec注解來注明,并實現(xiàn)一個標(biāo)注了@OnCreateLayout注解的方法。示例如下:

@LayoutSpecclass HelloComponentSpec {@OnCreateLayoutstatic Component onCreateLayout(ComponentContext c, @Prop String name) {return Column.create(c).child(Text.create(c).text("Hello, " + name).textSizeRes(R.dimen.my_text_size).textColor(Color.BLACK).paddingDip(ALL, 10).build()).child(Image.create(c).drawableRes(R.drawable.welcome).scaleType(ImageView.ScaleType.CENTER_CROP).build()).build();}}

最終Litho會在編譯時生成一個名為HelloComponent的組件。

public final class HelloComponent extends Component {@Prop(resType = ResType.NONE,optional = false) String name;private HelloComponent() {super();}@Overrideprotected Component onCreateLayout(ComponentContext c) {return (Component) HelloComponentSpec.onCreateLayout((ComponentContext) c, (String) name);}...public static Builder create(ComponentContext context, int defStyleAttr, int defStyleRes) {Builder builder = sBuilderPool.acquire();if (builder == null) {builder = new Builder();}HelloComponent instance = new HelloComponent();builder.init(context, defStyleAttr, defStyleRes, instance);return builder;}public static class Builder extends Component.Builder<Builder> {private static final String[] REQUIRED_PROPS_NAMES = new String[] {"name"};private static final int REQUIRED_PROPS_COUNT = 1;HelloComponent mHelloComponent;...public Builder name(String name) {this.mHelloComponent.name = name;mRequired.set(0);return this;}@Overridepublic HelloComponent build() {checkArgs(REQUIRED_PROPS_COUNT, mRequired, REQUIRED_PROPS_NAMES);HelloComponent helloComponentRef = mHelloComponent;release();return helloComponentRef;}}}

Mount Spec規(guī)范:用來生成可掛載類型組件的規(guī)范,用來生成渲染具體View或者Drawable的組件。同樣,它必須使用@MountSpec注解來標(biāo)注,并至少實現(xiàn)一個標(biāo)注了@onCreateMountContent的方法。Mount Spec相比于Layout Spec更復(fù)雜一些,它擁有自己的生命周期:

  • @OnPrepare,準(zhǔn)備階段,進(jìn)行一些初始化操作。
  • @OnMeasure,負(fù)責(zé)布局的計算。
  • @OnBoundsDefined,在布局計算完成后掛載視圖前做一些操作。
  • @OnCreateMountContent,創(chuàng)建需要掛載的視圖。
  • @OnMount,掛載視圖,完成布局相關(guān)的設(shè)置。
  • @OnBind,綁定視圖,完成數(shù)據(jù)和視圖的綁定。
  • @OnUnBind,解綁視圖,主要用于重置視圖的數(shù)據(jù)相關(guān)的屬性,防止出現(xiàn)復(fù)用問題。
  • @OnUnmount,卸載視圖,主要用于重置視圖的布局相關(guān)的屬性,防止出現(xiàn)復(fù)用問題。

除了上述兩種組件類型,Litho中還有一種特殊的組件——Layout,它不能使用規(guī)范來生成。Layout是Litho中的容器組件,類似于Android中的ViewGroup,但是只能使用Flexbox的規(guī)范。它可以包含子組件節(jié)點,是Litho各組件連接的紐帶。Layout組件只是Yoga在Litho中的代理,組件的所有布局相關(guān)的屬性都會直接設(shè)置給Yoga,并由Yoga完成布局的計算。Litho實現(xiàn)了兩個Layout組件Row和Column,分別對應(yīng)Flexbox中的行和列。

2.2.2 Litho的屬性

在Litho中屬性分為兩種,不可變屬性稱為Props,可變屬性稱為State,下面分別介紹一下兩種屬性:

Props屬性:組件中使用@Prop注解標(biāo)注的參數(shù)集合,具有單向性和不可變性。下面通過一個簡單的例子了解一下如何在組件中定義和使用Props屬性:

@MountSpecclass MyComponentSpec {@OnPreparestatic void onPrepare(ComponentContext c,@Prop(optional = true) String prop1) {...}@OnMountstatic void onMount(ComponentContext c,SomeDrawable convertDrawable,@Prop(optional = true) String prop1,@Prop int prop2) {if (prop1 != null) {...}}}

在上面的代碼中,共使用了三次Prop注解,分別標(biāo)注prop1和prop2兩個變量,即定義了prop1和prop2兩個屬性。Litho會在自動編譯生成的MyComponent類的Builder類中生成這兩個屬性的同名方法。按照如下代碼,便可以去使用上面定義的屬性:

MyComponent.create(c).prop1("My prop 1").prop2(256).build();

State屬性:意為“狀態(tài)”屬性,State屬性雖然可變,但是其變化由組件內(nèi)部控制,例如:輸入框、Checkbox等都是由組件內(nèi)部去感知用戶行為,并更新組件的State屬性。所以一個組件一旦創(chuàng)建,我們便無法通過任何外部設(shè)置去更改它的屬性。組件的State屬性雖然不允許像Props屬性那樣去顯式設(shè)置,但是我們可以定義一個單獨的Props屬性來當(dāng)做某個State屬性的初始值。

3. Litho的特性及原理剖析

Litho官網(wǎng)首頁通過4個段落重點介紹了Litho的4個特性。

3.1 聲明式組件

Litho采用聲明式的API來定義UI組件,組件通過一組不可變的屬性來描述UI。這種組件化的思想靈感來源于React,關(guān)于聲明式組件的用法上面已經(jīng)詳細(xì)介紹過了。

傳統(tǒng)Android布局因為UI與邏輯分離,所以開發(fā)工具都有強大的預(yù)覽功能,方便開發(fā)者調(diào)整布局。而Litho采用React組件化的思想,通過組件連接了邏輯與布局UI,雖然Litho也提供了對Stetho的支持,借助于Chrome開發(fā)者工具對界面進(jìn)行調(diào)試,不過使用起來并沒有那么方便。

3.2 異步布局

Android系統(tǒng)在繪制時為了防止頁面錯亂,頁面所有View的測量(Measure)、布局(Layout)以及繪制(Draw)都是在UI線程中完成的。當(dāng)頁面UI非常復(fù)雜、視圖層級較深時,難免Measure和Layout的時間會過長,從而導(dǎo)致頁面渲染時候丟幀出現(xiàn)卡頓情況。Litho為解決該問題,提出了異步布局的思想,利用CPU的閑置時間提前在異步線程中完成Measure和Layout的過程,僅在UI線程中完成繪制工作。當(dāng)然,Litho只是提供了異步布局的能力,它主要使用在RecyclerView等可以提前知道下一個視圖長什么樣子的場景。

3.2.1 異步布局原理剖析

針對RecyclerView等滑動列表,由于可以提前知道接下來要展示的一個甚至多個條目的視圖樣式,所以只要提前創(chuàng)建好下一個或多個條目的視圖,就可以提前完成視圖的布局工作。

那么Android原生為什么不支持異步布局呢?主要有以下兩個原因:

  • View的屬性是可變的,只要屬性發(fā)生變化就可能導(dǎo)致布局變化,因此需要重新計算布局,那么提前計算布局的意義就不大了。而Litho組件的屬性是不可變的,所以對于一個組件來說,它的布局計算結(jié)果是唯一且不變的。

  • 提前異步布局就意味著要提前創(chuàng)建好接下來要用到的一個或者多個條目的視圖,而Android原生的View作為視圖單元,不僅包含一個視圖的所有屬性,而且還負(fù)責(zé)視圖的繪制工作。如果要在繪制前提前去計算布局,就需要預(yù)先去持有大量未展示的View實例,大大增加內(nèi)存占用。反觀Litho的組件則沒有這個問題,Litho的組件只是視圖屬性的一個集合,僅負(fù)責(zé)計算布局,繪制工作由指定的繪制單元來完成,相比與傳統(tǒng)的View顯然Litho的組件要輕量的多。所以在Litho中,提前創(chuàng)建好接下來要用到的多個條目的組件,并不會帶來性能問題,甚至還可以直接把組件當(dāng)成滑動列表的數(shù)據(jù)源。如下圖所示:

3.3 扁平化的視圖

使用Litho布局,我們可以得到一個極致扁平的視圖效果。它可以減少渲染時的遞歸調(diào)用,加快渲染速度。

下面是同一個視圖在Android和Litho實現(xiàn)下的視圖層級效果對比。可以看到,同樣的樣式,使用Litho實現(xiàn)的布局要比使用Android原生實現(xiàn)的布局更加扁平。

3.3.1 扁平化視圖原理剖析

Litho使用Flexbox來創(chuàng)建布局,最終生成帶有層級結(jié)構(gòu)的組件樹。然后Litho對布局層級進(jìn)行了兩次優(yōu)化。

  • 使用了Yoga來進(jìn)行布局計算,Yoga會將Flexbox的相對布局轉(zhuǎn)成絕對布局。經(jīng)過Yoga處理后的布局沒有了原來的布局層級,變成了只有一層。雖然不能解決過度繪制的問題,但是可以有效地減少渲染時的遞歸調(diào)用。

  • 前面介紹過Litho的視圖渲染由繪制單元來完成,繪制單元可以是View或者更加輕量的Drawable,Litho自己實現(xiàn)了一系列掛載Drawable的基本視圖組件。通過使用Drawable可以減少內(nèi)存占用,同時相比于View,Android無法檢查出Drawable的視圖層級,這樣可以使視圖效果看起來更加扁平。

原理如下圖所示,Litho會先把組件樹拍平成沒有層級的列表,然后使用Drawable來繪制對應(yīng)的視圖單元。

Litho使用Drawable代替View能帶來多少好處呢?Drawable和View的區(qū)別在于前者不能和用戶交互,只能展示,因此Drawable不會像View那樣持有很多變量和引用,所以Drawable比View從內(nèi)存上看要輕量很多。舉個例子:50個同樣展示“Hello world”的TextView和TextDrawable在內(nèi)存占比上,前者幾乎是后者的8倍。對比圖如下,Shallow Size表示對象自身占用的內(nèi)存大小。

3.3.2 繪制單元的降級策略

由于Drawable不具有交互能力,所以對于使用Drawable無法實現(xiàn)的交互場景,Litho會自動降級成View。主要有以下幾種場景:

  • 有監(jiān)聽點擊事件。
  • 限制子視圖繪出父布局。
  • 有監(jiān)聽焦點變化。
  • 有設(shè)置Tag。
  • 有監(jiān)聽觸摸事件。
  • 有光影效果。

對于以上場景的使用請仔細(xì)考慮,過多的使用會導(dǎo)致Litho的層級優(yōu)化效果變差。

3.3.3 對比Android的約束布局

為了解決布局嵌套問題,Android推出了約束布局(ConstraintLayout),使用約束布局也可以達(dá)到扁平化視圖的目的,那么使用Litho的好處是什么呢?

Litho可以更好地實現(xiàn)復(fù)雜布局。約束布局雖然可以實現(xiàn)扁平效果,但是它使用了大量的約束來固定視圖的位置。隨著布局復(fù)雜程度的增加,約束條件變得越來越多,可讀性也變得越來越差。而Litho則是對Flexbox布局進(jìn)行的扁平化處理,所以實際使用的還是Flexbox布局,對于復(fù)雜的布局Flexbox布局可讀性更高。

3.4 細(xì)粒度的復(fù)用

Litho中的所有組件都可以被回收,并在任何位置進(jìn)行復(fù)用。這種細(xì)粒度的復(fù)用方式可以極大地提高內(nèi)存使用率,尤其適用于復(fù)雜滑動列表,內(nèi)存優(yōu)化非常明顯。

3.4.1 原生RecyclerView復(fù)用原理剖析

原生的RecyclerView視圖按模板類型進(jìn)行存儲并復(fù)用,也就是說模板類型越多,所需存儲的模板種類也就越多,導(dǎo)致內(nèi)存占用越來越大。原理如下圖。滑出屏幕的itemType1和itemType2都會在Recycler緩存池保存,等待后面滑進(jìn)屏幕的條目的復(fù)用。

3.4.2 細(xì)粒度復(fù)用優(yōu)化內(nèi)存原理剖析

在Litho中,item在回收前,會把LithoView中掛載的各個繪制單元拆分出來(解綁),由Litho自己的緩存池去分類回收,在展示前由LithoView按照組件樹的樣式組裝(掛載)各個繪制單元,這樣就達(dá)到了細(xì)粒度復(fù)用的目的。原理如下圖。滑出屏幕的itemType1會被拆分成一個個的視圖單元。LithoView容器由Recycler緩存池回收,其他視圖單元由Litho的緩存池分類回收。

使用細(xì)粒度復(fù)用的RecyclerView的緩存池不再需要區(qū)分模板類型來緩存大量的視圖模板,只需要緩存LithoView容器。細(xì)粒度回收的視圖單元數(shù)量要遠(yuǎn)遠(yuǎn)小于原來緩存在各個視圖模板中的視圖單元數(shù)量。

4. 實踐

美團對Litho進(jìn)行了二次開發(fā),在美團的MTFlexbox動態(tài)化實現(xiàn)方案(簡稱動態(tài)布局)中把Litho作為底層UI渲染引擎來使用。通過動態(tài)布局的預(yù)覽工具,為Litho提供實時預(yù)覽能力,同時可以有效發(fā)揮Litho的性能優(yōu)化效果。

目前Litho+動態(tài)布局的實現(xiàn)方案已經(jīng)應(yīng)用在了美團App中,給美團App帶來了不錯的性能提升。后續(xù)博主會詳細(xì)介紹Litho+動態(tài)布局在美團性能優(yōu)化的實踐方案。

4.1 內(nèi)存數(shù)據(jù)

由于Litho中使用了大量Drawable替換View,并且實現(xiàn)了視圖單元的細(xì)粒度復(fù)用,因此復(fù)雜列表滑動時內(nèi)存優(yōu)化比較明顯。美團首頁內(nèi)存占用隨滑動頁數(shù)變化走勢圖如下。隨著一頁一頁地滑動,內(nèi)存優(yōu)化了30M以上。(數(shù)據(jù)采集自Vivo x20手機內(nèi)存占用情況)

4.2 FPS數(shù)據(jù)

FPS的提升主要得益于Litho的異步布局能力,提前計算布局可以減少滑動時的幀率波動,所以滑動過程較平穩(wěn),不會有高低起伏的卡頓感。(數(shù)據(jù)采集自魅藍(lán)2手機一段時間內(nèi)連續(xù)fps的波動情況)

5. 總結(jié)

Litho相對于傳統(tǒng)Android是顛覆式的,它采用了React的思路,使用聲明式的API來編寫UI。相比于傳統(tǒng)Android,確實在性能優(yōu)化上有很大的進(jìn)步,但是如果完全使用Litho開發(fā)一款應(yīng)用,需要自己實現(xiàn)很多組件,而Litho的組件需要在編譯時生成,實時預(yù)覽方面也有所欠缺。相對于直接使用Litho的高成本,把Litho封裝成Flexbox布局的底層渲染引擎是個不錯的選擇。

6. 參考資料

  • Litho官網(wǎng)
  • 說一說 Facebook 開源的 Litho
  • React官網(wǎng)
  • Yoga官網(wǎng)
  • 7. 作者簡介

    • 何少寬,美團Android開發(fā)工程師,2015年加入美團,負(fù)責(zé)美團平臺終端業(yè)務(wù)研發(fā)工作。
    • 張穎,美團Android開發(fā)工程師,2017年加入美團,負(fù)責(zé)美團平臺終端業(yè)務(wù)研發(fā)工作。

    總結(jié)

    以上是生活随笔為你收集整理的基本功 | Litho的使用及原理剖析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。