Airbnb: 移動開發(fā)的下一個時代

本文翻譯自What’s Next for Mobile at Airbnb

這是我們關(guān)于React Native的系列文章的第五篇,描述我們在React Native上的經(jīng)驗和我們下一步在Airbnb移動端上的工作。

Exciting Times Ahead

在嘗試React Native時,我們也同時加快在原生上的努力。今天,我們在產(chǎn)品中有了大量令人驚艷的項目。這些項目的靈感來自于我們在學(xué)習(xí)React Native時的經(jīng)驗。

服務(wù)驅(qū)動渲染(Server-Driven Rendering)

雖然我們不再使用React Native,但我們?nèi)匀豢吹搅酥粚懸环莓a(chǎn)品代碼的價值。我們?nèi)匀皇忠蕾囄覀兘y(tǒng)一的設(shè)計語言系統(tǒng)(design language system , DLS),很多界面在 Android 和 iOS 中看起來是一樣的。

一些團隊嘗試并開始為強大的server-driven rendering框架達成一致。在這些框架中,server發(fā)送數(shù)據(jù)到設(shè)備來描述渲染的組件、界面配置和發(fā)生的交互行為。不同移動平臺解析數(shù)據(jù)并渲染原生界面或者完全使用DLS的組件。

大量使用Server-driven rendering帶來了一些挑戰(zhàn),這里是我們的一些解決方案:

  • 安全更新組件定義的同時保持向后兼容
  • 對于跨平臺組件共享類型定義
  • 在運行時響應(yīng)事件,比如按鈕點擊或用戶輸入
  • 保存內(nèi)部狀態(tài)時在不同JSON-driven界面之間轉(zhuǎn)換
  • 不要在build-time時實現(xiàn)全部用戶組件的渲染,我們嘗試用Lona格式來處理。

Server-driven rendering框架已經(jīng)提供了大量的價值,允許我們直接遠程試驗或更新功能。

Epoxy Components

2016年我們開源了Android的 Epoxy 。Epoxy 是一個能夠使混合 RecyclerViews、 UICollectionViews 和 UITableViews 變得簡單的框架?,F(xiàn)在,大多數(shù)新的界面使用Epoxy。如此做允許我們打破每個界面形成獨立的組件,實現(xiàn)懶渲染?,F(xiàn)在,我們有了 Android 和 iOS 的 Epoxy。

iOS上如下:

BasicRow.epoxyModel(
  content: BasicRow.Content(
    titleText: "Settings",
    subtitleText: "Optional subtitle"),
  style: .standard,
  dataID: "settings",
  selectionHandler: { [weak self] _, _, _ in
    self?.navigate(to: .settings)
  })

在 Android 上,我們寫了 DSLs in Kotlin 來實現(xiàn)組件的易寫和類型安全:

basicRow {
  id("settings")
  title(R.string.settings)
  subtitleText(R.string.settings_subtitle)
  onClickListener { navigateTo(SETTINGS) }
}

Epoxy Diffing

React中,從render返回組件列表,React的性能關(guān)鍵是組件能評判代表你想要繪制的實際界面或網(wǎng)頁的數(shù)據(jù)模型。我們?yōu)镋poxy創(chuàng)建了相似的概念。在Epoxy中,你可以在 buildModels 中為所有界面聲明模型。配合優(yōu)雅的Kotlin,DSL從概念上與React非常相似,看起來如下:

override fun EpoxyController.buildModels() {
  header {
    id("marquee")
    title(R.string.edit_profile)
  }

  inputRow {
    id("first name")
    title(R.string.first_name)
    text(firstName)
    onChange {
      firstName = it
      requestModelBuild()
    }
  }
// Put the rest of your models here...
}

你的數(shù)據(jù)變化的時候,可以調(diào)用requestModelBuild(),界面會被重繪制。
iOS中代碼如下:

override func itemModel(forDataID dataID: DemoDataID) -> EpoxyableModel? {
    switch dataID {
    case .header:
        return DocumentMarquee.epoxyModel(
            content: DocumentMarquee.Content(titleText: "Edit Profile"),
            style: .standard,
            dataID: DemoDataID.header)
    case .inputRow:
        return InputRow.epoxyModel(
            content: InputRow.Content(
                titleText: "First name",
                inputText: firstName)
            style: .standard,
            dataID: DemoDataID.inputRow,
            behaviorSetter: { [weak self] view, content, dataID in
                view.textDidChangeBlock = { _, inputText in
                    self?.firstName = inputText
                    self?.rebuildItemModel(forDataID: .inputRow)
                }
        })
    }
}

A New Android Product Framework (MvRx)

最近開發(fā)中最令人興奮的是一個新的框架,我們內(nèi)部稱為MvRx。MvRx集中了 Epoxy、Jetpack、RxJava 和 Kotlin 的許多準則,從React到比之前更容易更無縫構(gòu)建場景。它是一個更靈活的框架,基于我們從React觀測到的最好的開發(fā)模式。它也是線程安全的,幾乎所有都運行在主線程之外,確?;瑒雍蛣赢嬃鲿?。

目前為止,框架已經(jīng)在很多場景中使用,幾乎消除了處理生命周期的必要。我們目前通過大量的 Android 產(chǎn)品磨練框架,如果繼續(xù)成功的話未來計劃開源。以下是創(chuàng)建一個網(wǎng)絡(luò)請求功能界面的完整代碼:

data class SimpleDemoState(val listing: Async<Listing> = Uninitialized)

class SimpleDemoViewModel(override val initialState: SimpleDemoState) : MvRxViewModel<SimpleDemoState>() {
    init {
        fetchListing()
    }

    private fun fetchListing() {
        // This automatically fires off a request and maps its response to Async<Listing>
        // which is a sealed class and can be: Unitialized, Loading, Success, and Fail.
        // No need for separate success and failure handlers!
        // This request is also lifecycle-aware. It will survive configuration changes and
        // will never be delivered after onStop.
        ListingRequest.forListingId(12345L).execute { copy(listing = it) }
    }
}

class SimpleDemoFragment : MvRxFragment() {
    // This will automatically subscribe to the ViewModel state and rebuild the epoxy models
    // any time anything changes. Similar to how React's render method runs for every change of
    // props or state.
    private val viewModel by fragmentViewModel(SimpleDemoViewModel::class)

    override fun EpoxyController.buildModels() {
        val (state) = withState(viewModel)
        if (state.listing is Loading) {
            loader()
            return
        }
        // These Epoxy models are not the views themself so calling buildModels is cheap. RecyclerView
        // diffing will be automaticaly done and only the models that changed will re-render.
        documentMarquee {
            title(state.listing().name)
        }
        // Put the rest of your Epoxy models here...
    }

    override fun EpoxyController.buildFooter() = fixedActionFooter {
        val (state) = withState(viewModel)
        buttonLoading(state is Loading)
        buttonText(state.listing().price)
        buttonOnClickListener { _ -> }
    }
}

MvRx簡單地構(gòu)建Fragment,可持續(xù)話,TTI追蹤和大量其他特性。
iOS 相似的框架也在早期測試中。

Iteration Speed

React Native換回原生之后最明顯的一件事情就是迭代速度。從我們的可靠測試中看,從一兩秒到等待15分鐘是不可接受的。幸運的是,我們能夠提供一些必要的安慰。

我們構(gòu)建 Android 和 iOS 的基本部件,使你只需編譯app的一部分,其中包括一個launcher和所依賴的庫。

在 Android 中,使用gradle product flavors。我們的框架如下:

新的中間件能使工程師創(chuàng)建和開發(fā)更輕巧的 app。配合IntelliJ module unloading明顯改善build和MacBook Pro的IDE性能。

我們用腳本來創(chuàng)建新測試風(fēng)格,過去的幾個月我們已經(jīng)創(chuàng)建了超過20個。用新的flavors開發(fā)平均比之前快2.5倍,5分鐘以上的build的比例下降15倍。

對于此,我們用了動態(tài)生成。

相似的,在iOS中的框架如下:

同樣的系統(tǒng)下,build速度快3到8倍

總結(jié)

很高興成為一個不懼怕新技術(shù)的公司,我們努力保持高質(zhì)量、速度和開發(fā)的經(jīng)驗的增長。最后,React Native是一個優(yōu)秀的工具,給了我們在移動端開發(fā)的更多思考。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,094評論 25 709
  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,210評論 3 119
  • n有些人, 你見到第一眼,就感覺很舒服; 有的人, 即使認識了好幾年, 見面也是尷尬的幾句寒暄。 人和人之間, 一...
    芮倩閱讀 519評論 0 1
  • 還記得在某一個清晨或者深夜,傾盡了所有,或許并非所有,只為挽留那心中的美好,但挽留不到,夢醒來,不一定挽留到的就是...
    缺愛的X閱讀 184評論 0 0
  • 1.Xcode插件神器:http://blog.csdn.net/woaifen3344/article/deta...
    扶光啟玄閱讀 899評論 0 3

友情鏈接更多精彩內(nèi)容