Gbox開源:比RN和WebView更輕的高性能動態化業務容器,解決首頁動態化的痛點

1 前言

注: 本文中全部的xml均可以直接貼去mock測試,文章中還附有運行預覽的截圖。閱讀本文可能須要您億點點的時間。css

2 咱們要解決什麼問題?

2.1 不講應用場景就是耍流氓!

在線上,對於某些適用於要求強展現、輕交互、高可配場景,RN和WebView顯得不夠靈活,性能表現也不夠好。html

使用RN時要佔據整個Activity,並且Native和Js的通訊損耗不可避,WebView的狀況則更加糟糕,還要lock主線程來加載webkit。這在二級、三級頁面還好,在首頁是絕對不能用這種掉性能的方案的。java

而且對於首頁feed流卡片、一級頁面的活動區塊來講,這些頁面的邏輯自己就不強,並且每每也只是須要局部動態化,因此綜合來看RN和WebView都不是最優選,咱們須要第三條路android

2.2 美團APP團隊的方案

對於這些應用場景,美團APP團隊有本身的一套閉源的動態化容器MTFlexbox來應對,可讓佈局快速開發上線,而且不受發版限制。以前在美團實習期間有幸學習過MTFlexbox,因此這裏我要感謝一下美團APP終端業務研發組的同窗們,以及我本來的Leader。git

這套方案不只承載了美團首頁的動態化,還解決了團首頁在連續滑動過程當中出現FPS波動的問題,詳情能夠參考他們的文章👉Litho在動態化方案MTFlexbox中的實踐程序員

2.3 個人解決方案

實習結束返校以後,本身一直想作一個在美團實習的總結,但又不知道以什麼爲主題比較合適。思索以後,以爲本身對於MTFlexbox理解得以及其適用的業務特色理解的還算深入,因此最終我選擇了嘗試本身去設計實現一個與MTFlexbox功能相似的開源框架Gbox,讓Gbox成爲MTFlexbox的開源替代品。github

注:Gbox使用kotlin開發,在Apache開源協議下發布,我雖說Gbox是MTFlexbox的開源替代品,可是Gbox ≠ MTFlexbox🙅,它不包含MTFlexbox的任何源代碼,也不是MTFlexbox的兼容版本,它是一個徹底基於開源軟件實現的全新開源軟件。web

3 Gbox簡介

3.1 研發歷程

Gbox的開發,從需求分析->設計->技術選型->編碼->bug修復,花了大約我三週的時間。在技術選型時主要評估可移植性(或跨平臺性)和性能指標,最終確認了Litho+Tomcat EL+kotlin的技術選型。json

  • Litho 足夠高效、包體積較小,除了draw操做measure、layout都在異步線程完成,可以知足性能須要,而且在iOS平臺上也有等價的componentkit實現。
  • Tomcat EL 功能強大並且久經考驗。在實現上幾乎沒有外部依賴,後期可使用Google的開源項目J2Objc直接移植到iOS上。
  • kotlin 能夠大幅的減小冗餘代碼,而且使用kotlin的內聯可以以更少的代碼得到更高的性能。

其實框架的渲染層本來是打算使用Flutter實現的,可是代碼寫到一半才發現這玩意過重了(包大小too大),而後又從新回到Litho這條線上進行開發,這期間本身踩了好多Litho、Drawable和Canvas的坑,本身對Android的整個渲染體系也有了更深入的理解。後端

目前爲止,Gbox已經基本基本穩定,但還會有小特性持續補充進來,你能夠在github上👉找到它

3.2 Gbox所支持的特性

Gbox是對業務以及性能友好的:

  • 耗時操做異步化。將本來View體系中的measure、layout搬到異步線程中去,解放主線程,這也是Gbox之因此高效的緣由之一
  • 幹掉佈局層級。直接使用輕量級的Drawable進行渲染,與WebView相比有更大的性能優點

  • 異步圖片加載。使用輕量級Glide做爲圖片加載引擎,全部圖片都可以從網絡加載,而且不會觸發額外的佈局更新
  • 敏捷開發,隨時上線,先後端分離。後端下發佈局+數據的json,可集成在數據接口下發,本地自主解析渲染布局
  • 單容器View接入,基本無入侵性。可用於替換現有的任意一個靜態展現型的View,並支持曝光埋點、點擊埋點、點擊時間處理等事件
  • 提供完整的開發工具鏈。佈局開發可實時預覽,提供佈局實時預覽APP(overview)和mock工具,可經過掃碼鏈接電腦進行實時預覽調試
  • 基於普遍使用的flexbox佈局模型,包含豐富的可配置樣式,例如邊框顏色,圓角,圖片,文本等
  • 強大的佈局內綁定表達式。包括數學運算,for語句,三元表達式,簡單的java方法調用,使用表達式時需使用${}包圍
  • 屏幕適配,佈局使用的單位爲是設備獨立的pt,以設備屏幕寬度爲基準,將屏幕分紅360份,1pt=設備屏幕寬度/360
  • 使用kotlin實現,代碼實現很是簡潔,很適合閱讀學習
  • 對舊邏輯友好,支持原生View嵌入Gbox

3.3 與MTFlexbox對比

對比美團首頁線上方案MTFlexbox,左爲MTFlexbox(美團APP),右爲Gbox(Gbox的實時預覽APP)。

因爲佈局文件字太多,因此我就不直接往文章裏仍了,你能夠在Gbox的github倉庫上拿到👉這個佈局文件

4 開始搭建Gbox開發環境

4.1 安裝Overview APP

使用git clone 源碼:

git clone https://github.com/sanyuankexie/Gbox

因爲項目中使用了APT技術,因此將源碼clone完畢後,須要先rebuild一次。

首先咱們須要安裝overview APP,打開找到overview模塊,將overview APP安裝到你的測試機上。

接下來是最重要的一步,確保你的手機和你的電腦處於同一網絡環境中,推薦是使用熱點。

4.2 執行測試用例

找到mock模塊中的MockTestCase文件。

運行JUnit的@Test,便可在控制檯中生成地址二維碼,你還能夠更改layout和data的路徑使用其餘樣式或者mock數據。注意Android Studio的主題色需爲白色,不然生成的二維碼沒法被手機識別。

(調整爲白色主題👇)

能夠看到二維碼已經在控制檯生成。

此時,使用overview

4.3 開始實時預覽

APP掃描控制檯生成的二維碼,便可預覽電腦上的佈局文件。

打開LiveReload開關,能夠實現實時預覽的效果,在電腦上修改佈局以後,使用Ctrl+S保存,便可刷新到overview APP上。控制檯用於打印埋點和點擊信息(若是有的狀況下)。

那麼Gbox是如何實現實時預覽的呢?

原理其實很簡單,也許你都已經猜到了。mock模塊打開了一個http服務器,overview掃碼拿到的是電腦的ip地址和端口號,而後overview每隔一秒去請求服務器下發佈局和數據,這樣就可完成佈局的實時預覽。附上源碼👉MockSession.kt

5 開始編寫佈局文件

5.1 綁定表達式

在開始編寫佈局以前咱們須要瞭解Gbox的綁定表達式。

Gbox的綁定表達式是基於嵌入式Tomcat(對!就是用在Spring Boot上那個)所使用的EL表達式類庫開發的,因此它支持EL表達式的全部特性,包括Java Bean訪問、方法調用、三元表達式、數學運算等的。

假如你有一個像下面同樣的json:

{
    "number":1000,
    "control":{
        "display":true
    },
    "text":"這段文字不會被顯示"
}

編寫下面的綁定表達式

<?xml version="1.0" encoding="utf-8"?>
<Flex
    width="360"
    background="yellow"
    flexDirection="row">
    <Text
        textSize="30"
        text="${control.display?'其餘文本':text}"
        height="100">
    </Text>
</Flex>

這個Text將不會顯示文本'這段文字不會被顯示',而會顯示'其餘文本'。值得注意的是在綁定表達式中字符串常量使用單引號包裹。

數學運算:

<?xml version="1.0" encoding="utf-8"?>
<Flex
    width="360"
    background="yellow"
    flexDirection="row">
    <Text
        textSize="30"
        text="${number+1000}"
        height="100">
    </Text>
</Flex>

不止是text屬性,你還能夠在任意屬性中使用綁定表達式,以知足你數據展現的須要。

5.2 Flex

因爲Gbox是基於Litho的UI框架,而Litho又是使用yoga這個基於flexbox佈局模型的佈局引擎的,因此首先要支持的就是Flex,顧名思義,就是彈性容器。

Flex的實現很是簡單,你能夠理解爲加強版的LinenerLayout,它支持如下屬性:

首先是flexDirection,它用來指定主軸方向,支持row、column、rowReverse、columnReverse四種排布方式,下面是row和column的截圖,沒有填寫flexDirection時則默認爲row。

<?xml version="1.0" encoding="utf-8"?>
<Flex flexDirection="row">
    <Flex
        background="red"
        width="100"
        height="100">
    </Flex>
    <Flex
        background="blue"
        width="100"
        height="100">
    </Flex>
</Flex>

<?xml version="1.0" encoding="utf-8"?>
<Flex flexDirection="column">
    <Flex
        background="red"
        width="100"
        height="100">
    </Flex>
    <Flex
        background="blue"
        width="100"
        height="100">
    </Flex>
</Flex>

接下來是justifyContent屬性,它標識了其全部子Layout在主軸上的對齊方式,包含flexStart、flexEnd、center、spaceBetween、spaceAround五種,下面我經過編寫一個xml,展現了該效果。

flexStart、flexEnd、center無需多言,而spaceBetween、spaceAround須要解釋一下。

  • spaceBetween是指兩端對齊,項目之間的間隔都相等。
  • spaceAround是指每一個項目兩側的間隔相等。
<?xml version="1.0" encoding="utf-8"?>
<Flex
    width="360"
    background="yellow"
    flexDirection="column">
    <Flex
        justifyContent="flexStart"
        margin="5">
        <Flex
            background="red"
            width="100"
            height="100">
        </Flex>
        <Flex
            background="blue"
            width="100"
            height="100">
        </Flex>
    </Flex>
    <Flex
        justifyContent="flexEnd"
        margin="5">
        <Flex
            background="red"
            width="100"
            height="100">
        </Flex>
        <Flex
            background="blue"
            width="100"
            height="100">
        </Flex>
    </Flex>
    <Flex
        justifyContent="center"
        margin="5">
        <Flex
            background="red"
            width="100"
            height="100">
        </Flex>
        <Flex
            background="blue"
            width="100"
            height="100">
        </Flex>
    </Flex>
    <Flex
        justifyContent="spaceBetween"
        margin="5">
        <Flex
            background="red"
            width="100"
            height="100">
        </Flex>
        <Flex
            background="blue"
            width="100"
            height="100">
        </Flex>
    </Flex>
    <Flex
        justifyContent="spaceAround"
        margin="5">
        <Flex
            background="red"
            width="100"
            height="100">
        </Flex>
        <Flex
            background="blue"
            width="100"
            height="100">
        </Flex>
    </Flex>
</Flex>


屏幕過小,一張截圖截不完。

而後是alignItems,它描述的的子Layout在副軸上的對齊方式,支持flexStart、flexEnd、center、baseline、stretch五種。

其中baseline表示與項目的第一行文字的基線對齊,stretch指定時,若是子Layout未指定高度,則會佔滿父Layout。

編寫下面的xml:

<?xml version="1.0" encoding="utf-8"?>
<Flex
    width="360"
    height="360"
    background="yellow"
    flexDirection="row">
    <Flex
        height="360"
        alignItems="flexStart"
        margin="5">
        <Flex
            background="red"
            width="100"
            height="100">
        </Flex>
        <Flex
            background="blue"
            width="100"
            height="100">
        </Flex>
    </Flex>
    <Flex
        height="360"
        alignItems="flexEnd"
        margin="5">
        <Flex
            background="red"
            width="100"
            height="100">
        </Flex>
        <Flex
            background="blue"
            width="100"
            height="100">
        </Flex>
    </Flex>
    <Flex
        height="360"
        alignItems="center"
        margin="5">
        <Flex
            background="red"
            width="100"
            height="100">
        </Flex>
        <Flex
            background="blue"
            width="100"
            height="100">
        </Flex>
    </Flex>
    <Flex
        height="360"
        alignItems="baseline"
        margin="5">
        <Flex
            background="red"
            width="100"
            height="100">
        </Flex>
        <Flex
            background="blue"
            width="100"
            height="100">
        </Flex>
    </Flex>
    <Flex
        height="360"
        alignItems="stretch"
        margin="5">
        <Flex
            background="red"
            width="100">
        </Flex>
        <Flex
            background="blue"
            width="100">
        </Flex>
    </Flex>
</Flex>

在上面的佈局中,你會發現360這個值會常常出現,沒錯,在Gbox中它是一個特殊值。爲了作屏幕適配,Gbox的大小單位是設備獨立的,它以屏幕寬度做爲基準,將屏幕寬度分爲360份,一個單位的像素值=屏幕寬度像素值/360,這也恰好是2x設計圖紙的大小,相信大家的UI設計師會喜歡Gbox的。

Gbox的全部佈局屬性和層級,最終將被應用到facebook的yoga佈局引擎中去,不管佈局有多複雜您都不須要擔憂,由於全部的計算都是在可指定的Layout線程中進行的,根本不會影響主線程,而且最終生成在屏幕上的View是沒有這些冗餘佈局層級的。有關更多Litho的信息,建議您查閱Litho的相關文檔

5.3 Frame

Frame實現了相似Android上FrameLayout的佈局效果,用於實現Flex難以實現的多層疊加效果。

在Frame上,Gbox採用了比Flex更激進的佈局測量策略。咱們都知道,在Android的FrameLayout中onMeasure會去測量全部的子Layout,最終才能肯定寬高,Gbox中利用Litho的Component的不可變性(線程安全),將這一操做進行了並行化。

Frame擁有獨立的線程池,能夠併發地測量全部地子Layout,最終的結果在一個線程聚集,下圖演示了該過程。

PS: 說人話就是調用了java.util.concurrent.Executors#newCachedThreadPool,而後等待在一個線程等待其餘java.util.concurrent.Future完成,源碼連接👉FrameFactory.kt

編寫下面的佈局實現疊加效果:

<?xml version="1.0" encoding="utf-8"?>
<Frame
    width="360"
    height="360"
    background="yellow"
    flexDirection="row">
    <Flex
        background="red"
        width="100"
        height="100">
    </Flex>
    <Flex
        marginTop="50"
        marginLeft="50"
        background="blue"
        width="100"
        height="100">
    </Flex>
</Frame>

5.4 Image

Image不只僅只是一張簡單的ImageView,它封裝了Glide圖片加載引擎,支持異步加載、圓角裁剪和高斯模糊。

使用url來加載網絡圖片:

Gbox沒有使用Litho的State來實現異步圖片加載,因此不會觸發Litho的佈局更新,而是直接替換底層的Drawble,而後調用invalidateDrawable,刷新髒矩形。

PS: 說人話就是使用了DrawableWrapper.kt

mock所使用的json數據:

{
        "image2": "http://5b0988e595225.cdn.sohucs.com/images/20180606/0a49d21848324503a1e04c4b942a1631.png"
}

編寫xml:

<?xml version="1.0" encoding="utf-8"?>
<Flex
    width="360"
    background="yellow"
    flexDirection="row">
    <Image
        width="360"
        height="360"
        url="${image2}">
    </Image>
</Flex>


borderRadius存在時,內部的圖片會被裁剪:

<?xml version="1.0" encoding="utf-8"?>
<Flex
    width="360"
    background="yellow"
    flexDirection="row">
    <Image
        scaleType="fitXY"
        borderRadius="100"
        width="360"
        height="360"
        url="${image2}">
    </Image>
</Flex>


使用blurRadius和blurSampling控制高斯模糊:

blurRadius爲弧度,值在1-25之間,blurSampling爲採樣率,值要比1大。

Gbox使用renderscript技術將高斯模糊的效率最大化,可以減小使用高斯模糊時圖片出現的延遲時間。

PS: 說人話就是在Glide加載圖片的時候加了個Transformation,源碼連接👉BlurTransformation.kt

<?xml version="1.0" encoding="utf-8"?>
<Flex
    width="360"
    background="yellow"
    flexDirection="row">
    <Image
        blurRadius="25"
        blurSampling="2"
        scaleType="fitXY"
        borderRadius="100"
        width="360"
        height="360"
        url="${image2}">
    </Image>
</Flex>


與ImageView同樣支持scaleType:

細心的朋友會發現,其實上面已經在使用fitXY了,笑~

<?xml version="1.0" encoding="utf-8"?>
<Flex
    width="360"
    background="yellow"
    flexDirection="row">
    <Image
        scaleType="fitEnd"
        width="360"
        height="360"
        url="${image2}">
    </Image>
</Flex>

5.5 Text

Text用於顯示文本,目前支持一下屬性修飾:

  • text用於指定要顯示的文本
  • textAlign用於指定文本對齊方式,支持center、left、right
  • textSize用於指定文本大小
  • textStyle用於指定文本風格如normal(默認)、bold(粗體)
  • maxLines指定最大行數
  • minLines指定最小行數
  • textColor指定字體顏色
<?xml version="1.0" encoding="utf-8"?>
<Flex
    width="360"
    background="yellow"
    flexDirection="column">
    <Text
        textColor="#59a9ff"
        textSize="30"
        text="文本1111">
    </Text>
    <Text
        textColor="red"
        textSize="30"
        text="文本1111">
    </Text>
    <Text
        textColor="blue"
        textSize="30"
        text="文本1111">
    </Text>
</Flex>

5.6 Native

Gbox對傳統業務組件也是友好的。

若是將Gbox接入之後以前寫的自定義View都得從頭編寫的話,那Gbox就失去了快速開發的意義了。因此Gbox也支持原生View的接入。

PS: 說人話就是封裝了com.facebook.litho.ViewCompatComponent,源碼👉NativeFactory.kt

使用type屬性,編寫下面的代碼,就能實現下圖中所展現的樣式。

<?xml version="1.0" encoding="utf-8"?>
<Flex
    width="360"
    background="yellow"
    flexDirection="row">
    <Flex
        flexDirection="row">
        <Text
            marginLeft="10"
            textSize="28"
            text="開關">
        </Text>
        <Native
            marginLeft="10"
            type="ch.ielse.view.SwitchView">
        </Native>
    </Flex>
</Flex>


值得注意的是Native已是佈局樹的葉子節點,這意味着不支持再使用Native包裹其餘節點。

5.7 Scroller

Scroller就是ScrollView,與ScrollView同樣,它只能有一個子View,它由兩個屬性控制樣式:

  • scrollBarEnable是一個布爾值(true或者false)控制是否顯示滾動條。
  • orientation控制橫向仍是豎向(vertical或者horizontal)。
<?xml version="1.0" encoding="utf-8"?>
<Scroller>
    <Flex>
        <Image
            width="360"
            height="600"
            scaleType="fitXY"
            url="${image2}">
        </Image>
    </Flex>
</Scroller>

【這...這就不展現了吧...我實在是以爲這玩意截圖沒啥意義...】

5.8 for

當model數據中有列表數據須要展開時,就須要用到for標籤。

for標籤有三個屬性,在使用時都是必須指定的,分別是var,from,to,index用於指定循環中迭代器的名字,from和to則指定了var的的迭代範圍。
好比你有下面的數據:

{
    "height":1000,
    "itemTexts":["Gbox","Facebook","Litho","Google"]
}

編寫下面的佈局:

<?xml version="1.0" encoding="utf-8"?>
<Flex
    width="360"
    background="yellow"
    flexDirection="column">
    <for var="index" from="1" to="3">
        <Text
            textSize="30"
            text="${itemTexts[index]}">
        </Text>
    </for>
</Flex>

能夠被等價展開成:

<Flex
    width="360"
    background="yellow"
    flexDirection="column">
        <Text
            textSize="30"
            text="${itemTexts[1]}">
        </Text>
        <Text
            textSize="30"
            text="${itemTexts[2]}">
        </Text>
        <Text
            textSize="30"
            text="${itemTexts[3]}">
        </Text>
</Flex>

for標籤var所指定的迭代器只會在for循環所包括的佈局標籤中生效

PS: for標籤是調用了Tomcat EL的ELContext#enterLambdaScopeELContext#exitLambdaScope實現的,代碼我就不在這裏貼了,你能夠👉直接跳轉到github看源碼

5.9 內置函數

目前Gbox還支持一些內置函數,內置函數必須在綁定表達式中才能調用:

utils:check(o:Any)

能夠檢測一個變量是否有效,爲空或者大小爲0的集合或者爲空的字符串都會返回false值

check方法是由kotlin實現的:

fun check(o: Any?): Boolean {
            return when (o) {
                is String -> o.isNotEmpty()
                is Collection<*> -> !o.isEmpty()
                is Number -> o.toInt() != 0
                else -> o != null
            }
        }

在下面的佈局邏輯中,屏幕上不會展現任何東西,由於json中沒有'no_found'這個變量。

<?xml version="1.0" encoding="utf-8"?>
<Flex>
    <Image
        width="360"
        height="360"
        url="${utils:check(no_found)?image2:''}">
    </Image>
</Flex>

draw:gradient(o:Orientation,vararg colors: String)

用於實現漸變色,第一個參數爲漸變色的方向,有t2b(上到下),tr2bl(上右到下左),l2r(右到左),br2tl(下右到上左),b2t(下到上),r2l(右到左),tl2br(上左到下右)八種方向可選,第二個參數是可變參,可傳入若干個顏色的字符串

kotlin的源碼實現:

fun gradient(orientation: GradientDrawable.Orientation, vararg colors: String): GradientDrawable {
            return GradientDrawable(orientation, colors.map {
                parseColor(it)
            }.toIntArray())
        }

編寫xml實現漸變色:

<?xml version="1.0" encoding="utf-8"?>
<Flex>
    <Flex
        width="360"
        height="360"
        background="${draw:gradient(tl2br,'red','blue','yellow')}">
    </Flex>
</Flex>

5.10 其餘通用屬性

除了上述屬性以外還有不少屬性是通用的,受限於篇幅,這裏我對一些比較重要的屬性進行簡單介紹。

background屬性是對全部Widget都通用的,用於爲圖片指定背景,它支持如下三種顯示來源:

  • 圖片的url
  • 以#開頭的十六進制顏色值
  • 漸變色(上面已經演示過了)

以圖片url爲背景

<?xml version="1.0" encoding="utf-8"?>
<Flex>
    <Flex
        width="360"
        height="360"
        background="${image2}">
    </Flex>
</Flex>

borderRadius 用於實現裁剪背景邊界的圓角。

<?xml version="1.0" encoding="utf-8"?>
<Flex>
    <Flex
        borderRadius="100"
        width="360"
        height="360"
        background="${image2}">
    </Flex>
</Flex>

你還可使用borderColorborderWidth爲邊界指定寬度和顏色。

<?xml version="1.0" encoding="utf-8"?>
<Flex>
    <Flex
        borderColor="red"
        borderWidth="10"
        borderRadius="100"
        width="360"
        height="360"
        background="${image2}">
    </Flex>
</Flex>

使用clickUrl並打開overview APP的控制檯,點擊圖片,就能在EventListener中收到點擊事件傳遞下來的信息,它能夠是一個url,供外部跳轉使用。

<?xml version="1.0" encoding="utf-8"?>
<Flex>
    <Flex
        clickUrl="拉菲世界第一可愛"
        borderColor="red"
        borderWidth="10"
        borderRadius="100"
        width="360"
        height="360"
        background="${image2}">
    </Flex>
</Flex>


於此相似的屬性還有reportView(曝光時上報),reportClick(點擊時上報)均可以在控制檯查看。

還有一些通用屬性,我也在這裏也簡單列了一下:

寬高

  • height
  • width

可見性

  • visibility 與Android相似,支持visible、invisible和gone

外邊距

  • margin
  • marginBottom
  • marginTop
  • marginLeft
  • marginRight

內邊距

  • padding
  • paddingTop
  • paddingBottom
  • paddingLeft
  • paddingRight

Flexbox所支持的其餘屬性

  • alignSelf
  • alignContent
  • flexGrow
  • flexShrink
  • ......

對於Flexbox這部分,我真誠地推薦你去看阮一峯老師的文章Flex佈局教程

Gbox中的對應屬性基本上就是將這些屬性使用java的命名風格,取消中位線,第二個單詞開始使用大寫。如在css中的align-content在Gbox中就是alignContent

6 後端集成注意事項

6.1 集成建議

在後端,可能集成者須要創建一個統一的佈局管理系統,包括:

  • 佈局cdn。
  • git聯動的版本控制功能等等。

固然這些也在將來的開發計劃中,歡迎您的PR。

6.2 下發的是json不是xml

值得注意的是,雖然編寫佈局使用的是xml,可是您能夠發現最終mock服務器下發到客戶端的只有json,這是由於在下發佈局時mock服務器已經將xml轉換爲了json,這樣作的目的是爲了佈局集成到數據接口中下發,使用統一工具進行解析,因此Gbox沒有耦合xml的解析模塊,而是最大化的利用了現有基礎設施。因此在後端集成時須要把xml轉換爲json來保存。

7 在新項目中集成Gbox

7.1 遠程依賴Gbox

Gbox使用jitpack進行構建,在你的根項目的build.gradle中添加

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

而後在模塊中依賴

dependencies {
            implementation 'com.github.LukeXeon.flexbox:core:latest.release'
    }

Sync項目,便可將SDK集成到項目中,Gbox的包大小不算太大,約爲1M左右,這其中主要是Facebook的Litho的大小。

7.2 集成Gbox的容器Litho

Gbox基於Litho,因此你還得先找個地方初始化Litho,能夠是Application,或者第一個Activity裏,反正用LithoView以前初始化就好了。

SoLoader.init(this, false);

LithoView是Gbox的容器,在xml中使用:

<com.facebook.litho.LithoView
    android:id="@+id/host"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</com.facebook.litho.LithoView>

而後在Java層中拿到實例,調用setComponentAsync方法,傳入一個新構造的DynamicBox。

mLithoView.setComponentAsync(
                    DynamicBox.create(c)
                        .bind(data)
                        .layout(layout)
                        .eventListener(this)
                        .build()
                );

其中layout是一個com.guet.flexbox.NodeInfo類的實例,它用來描述一顆佈局樹,是整棵佈局樹的樹根。

而data則是綁定到佈局中的數據,它支持多種數據格式自動匹配,分爲如下兩種狀況:

  • 在項目中依賴了Gson的狀況下,data能夠直接是String、byte[]、Reader、InputStream這些數據源。
  • 若是項目中沒有Gson,那麼Gbox也支持嵌套的多層級Map<String,Object>以及標準的java bean(有getter,setter)做爲數據源。

在overview APP中,我使用了Retrofit來簡化了這一過程。

添加Retrofit的依賴:

implementation 'com.squareup.retrofit2:retrofit:2.6.2'
    implementation 'com.squareup.retrofit2:converter-gson:2.6.2'

用Retrofit實例化接口:

package com.guet.flexbox.overview;

import com.guet.flexbox.NodeInfo;

import java.util.Map;

import retrofit2.Call;
import retrofit2.http.GET;

public interface MockService {
    @GET("/data")
    Call<Map<String, Object>> data();

    @GET("/layout")
    Call<NodeInfo> layout();
}

mMockService = new Retrofit.Builder()
        .baseUrl(url)
        .client(new OkHttpClient())
        .addConverterFactory(GsonConverterFactory.create())
        .build()
        .create(MockService.class);

EventListener用來處理事件回調,目前有三種事件點擊、上報點擊和上報曝光。

package com.guet.flexbox

enum class EventType {
    CLICK,
    REPORT_CLICK,
    REPORT_VIEW
}

package com.guet.flexbox

interface EventListener {
    fun onEvent(type: EventType, action: String?)
}

所傳入的action,就是你在xml中編寫的字符串所解析獲得的結果。

8 結語

8.1 吐槽

這裏吐槽一下,寫文檔是真的難受,但沒有高質量的文檔是不行的啊,別人會看不懂你在幹啥,因此還真是驗證了那句老話:「程序員想要的是內容詳實的文檔,但本身歷來不寫也最討厭不寫文檔的同事」。

8.2 若是你跑不起來

對於以前有同窗拉了個人代碼出現沒跑起來的狀況,我在這裏說一聲抱歉。如今我在github上的倉庫如今也分了master和develop分支,develop分支供我本身開發測試新特性使用,master分支爲穩定的主分支用於jitpack打包,develop分支的代碼穩定後,我纔會和入master,這樣應該就不會出現以前那樣的問題了。

新框架多多少少會有些小問題,Gbox代碼中的註釋我也會在往後補充,還請各位海函,發現了問題你能夠直接給我提issue,我會在github上跟進,或者直接給我發郵件imlkluo@qq.com,感謝您的支持。

8.3 關於新特性

如下是未來會進行嘗試的的方向:

  • 與Android Studio集成的IDEA插件
  • 將繪製操做也放到異步線程中去
  • 基於componentkit和EL(J2Objc)的iOS側

8.3 小尾巴

最後的最後,求star!求star!求star!重要的事情說三遍,請各位大佬幫幫忙,點一下玩一年,開源不花一分錢!

相關文章
相關標籤/搜索