直到最近,Dropbox 都在使用一個(gè)通過(guò) C++ 在 iOS 和 Android 之間共享代碼的移動(dòng)技術(shù)策略。這個(gè)策略背后的想法很簡(jiǎn)單:用 C++ 編寫(xiě)一次代碼,而不是用 Java 和 Objective C 編寫(xiě)兩次代碼?,F(xiàn)在,Dropbox 已經(jīng)完全放棄了這個(gè)策略,轉(zhuǎn)而使用每個(gè)平臺(tái)的原生語(yǔ)言。這個(gè)決定是由于與代碼共享相關(guān)的隱藏成本太高。
直到最近,Dropbox 都在使用一個(gè)通過(guò) C++ 在 iOS 和 Android 之間共享代碼的移動(dòng)技術(shù)策略。這個(gè)策略背后的想法很簡(jiǎn)單:用 C++ 編寫(xiě)一次代碼,而不是用 Java 和 Objective C 編寫(xiě)兩次代碼。早在 2013 年,我們就采用了這個(gè)策略。當(dāng)時(shí),我們的移動(dòng)工程團(tuán)隊(duì)相對(duì)還比較小,但需要支持快速增長(zhǎng)的移動(dòng)路線圖。我們需要找到一種方法,使這個(gè)小團(tuán)隊(duì)可以快速交付大量的 Android 和 iOS 代碼。
現(xiàn)在,我們已經(jīng)完全放棄了這個(gè)策略,轉(zhuǎn)而使用每個(gè)平臺(tái)的原生語(yǔ)言(主要是 Swift 和 Kotlin,這兩種語(yǔ)言在我們剛開(kāi)始制定移動(dòng)策略時(shí)還不存在)。這個(gè)決定是由于代碼共享相關(guān)的隱藏成本太高。下面是我們作為一個(gè)公司在有效共享代碼的成本方面學(xué)到的一些東西。它們都源于同一個(gè)基本問(wèn)題:
以非標(biāo)準(zhǔn)的方式編寫(xiě)代碼,使我們承擔(dān)了一些開(kāi)銷,而如果我們采用廣泛使用的平臺(tái)默認(rèn)選項(xiàng),我們就不必?fù)?dān)心這些開(kāi)銷。這種開(kāi)銷最終比只編寫(xiě)兩次代碼要昂貴得多。
在詳細(xì)介紹我們遇到的所有不同類型的開(kāi)銷之前,我想澄清一下,我們實(shí)際上從來(lái)沒(méi)有達(dá)到用 C++ 開(kāi)發(fā)大部分代碼庫(kù)的地步。采用 C++ 的開(kāi)銷實(shí)際上阻止了我們完全朝著這個(gè)方向前進(jìn)。
同樣值得注意的是,像谷歌和 Facebook 這樣的大公司多年來(lái)一直在開(kāi)發(fā)可擴(kuò)展的代碼共享解決方案。到目前為止,這些解決方案只獲得了有限的采用。雖然你可以利用第三方代碼共享解決方案(如 React Native 或 Flutter)來(lái)避免下面描述的一些開(kāi)銷,但有一些仍會(huì)存在(至少在其中一項(xiàng)技術(shù)獲得支持并成熟之前)。例如, Airbnb 放棄 React Native 的原因與本文中描述的許多相同。
我們可以把我們面臨的不同類型的開(kāi)銷分為四大類。
關(guān)于使用 C++,最容易預(yù)見(jiàn)的開(kāi)銷是需要構(gòu)建框架和庫(kù)。這大致可以分為兩大類:
如果我們繼續(xù)使用平臺(tái)原生語(yǔ)言,那么這些代碼都是不必要的;如果我們使用平臺(tái)原生語(yǔ)言,那么我們對(duì)開(kāi)源項(xiàng)目的貢獻(xiàn)可能會(huì)使更多的開(kāi)發(fā)人員受益。我們有可能在利用開(kāi)源 C++ 庫(kù)方面做得更好,但是 C++ 開(kāi)發(fā)社區(qū)中的開(kāi)源文化并不像在移動(dòng)開(kāi)發(fā)社區(qū)中那樣強(qiáng)大(特別是在幾乎不存在的 C++ 移動(dòng)社區(qū)中)。
注意,這些成本在 C++ 中特別高(與 Python 或 C#等其他可能的非原生語(yǔ)言相比),因?yàn)樗鄙僖粋€(gè)功能齊全的標(biāo)準(zhǔn)庫(kù)。雖說(shuō)如此,C/C++ 是唯一一種編譯器同時(shí)受到谷歌和蘋(píng)果支持的語(yǔ)言,因此,使用另一種語(yǔ)言將會(huì)產(chǎn)生一系列其他需要處理的問(wèn)題。
移動(dòng)生態(tài)系統(tǒng)有許多工具可用來(lái)提高開(kāi)發(fā)效率。移動(dòng) IDE 非常豐富,谷歌和蘋(píng)果投入了大量的資源,力爭(zhēng)在各自的平臺(tái)上為開(kāi)發(fā)人員提供最佳的開(kāi)發(fā)體驗(yàn)。我們遠(yuǎn)離了這些平臺(tái)的默認(rèn)選項(xiàng),也就放棄了其中的一些好處。最值得注意的是,使用平臺(tái)原生語(yǔ)言的調(diào)試體驗(yàn)通常優(yōu)于在平臺(tái)的默認(rèn) IDE 中使用 C++ 代碼的調(diào)試體驗(yàn)。
一個(gè)特別令人難忘的例子是一個(gè) Bug,它在我們的后臺(tái)線程框架中導(dǎo)致了死鎖,使得應(yīng)用程序隨時(shí)都會(huì)崩潰。即使是在處理簡(jiǎn)單的標(biāo)準(zhǔn)棧時(shí),也很難確定這些類型的 Bug。因?yàn)檫@個(gè)問(wèn)題涉及到調(diào)試在 C++ 和 Java 之間來(lái)回運(yùn)行的多線程代碼,所以花了幾周的時(shí)間才確定下來(lái)!
除了失去工具,我們還必須投入時(shí)間構(gòu)建支持 C++ 代碼共享的工具。最重要的是,我們需要一個(gè)定制的構(gòu)建系統(tǒng),它創(chuàng)建了包含 C++ 代碼以及 Java 和 Objective-C 封裝器的庫(kù),并且能夠生成 Xcodebuild 和 Gradle 都能識(shí)別的目標(biāo)文件。這個(gè)系統(tǒng)對(duì)我們的資源是一個(gè)很大的拖累,因?yàn)樗枰粩嗟馗乱灾С謨蓚€(gè)構(gòu)建系統(tǒng)中的更改。
盡管 iOS 和 Android 應(yīng)用程序都是“移動(dòng)應(yīng)用程序”,通常都具有相同的特性和功能,但平臺(tái)本身存在一些影響實(shí)現(xiàn)的差異。例如,應(yīng)用程序在每個(gè)平臺(tái)上執(zhí)行后臺(tái)任務(wù)的方式不同。即使在我們開(kāi)始采用跨平臺(tái)策略時(shí)非常相似的東西,隨著時(shí)間的推移也會(huì)產(chǎn)生很大的差異(例如,與相機(jī)相冊(cè)的交互)。
因此,你甚至不能真正地編寫(xiě)一次代碼就在不同的平臺(tái)上運(yùn)行。你必須花費(fèi)大量的時(shí)間將代碼集成到不同的平臺(tái)中,并編寫(xiě)特定于平臺(tái)的代碼(有時(shí)這些代碼就位于 C++ 層本身!)。
這使得只編寫(xiě)一次代碼理論上的好處沒(méi)有達(dá)到預(yù)期的效果,從而大大降低了這種方法的好處。
最后,但同樣重要的是,培訓(xùn)和 / 或招聘將在我們定制程度非常高的技術(shù)棧上進(jìn)行開(kāi)發(fā)的開(kāi)發(fā)人員的成本。當(dāng) Dropbox 開(kāi)始采用這種移動(dòng)策略時(shí),我們有一個(gè)由經(jīng)驗(yàn)豐富的 C++ 開(kāi)發(fā)人員組成的核心團(tuán)隊(duì)。這個(gè)小組啟動(dòng)了 C++ 項(xiàng)目,并在 Dropbox 培訓(xùn)其他移動(dòng)開(kāi)發(fā)者如何為代碼庫(kù)做貢獻(xiàn)。
隨著時(shí)間的推移,這些開(kāi)發(fā)人員轉(zhuǎn)向其他團(tuán)隊(duì)和其他公司。留下來(lái)的工程師沒(méi)有足夠的經(jīng)驗(yàn)來(lái)填補(bǔ)技術(shù)領(lǐng)導(dǎo)的空缺,并且越來(lái)越難招聘到具有相關(guān) C++ 經(jīng)驗(yàn)、對(duì)移動(dòng)開(kāi)發(fā)感興趣的高級(jí)工程師來(lái)替代他們。
因此,我們最終失去了維護(hù) C++ 代碼庫(kù)的關(guān)鍵專業(yè)知識(shí)。重新獲得這種專業(yè)知識(shí)的唯一方法是大量投資于以下兩個(gè)選擇之一:
除了招聘問(wèn)題,我們自己的技術(shù)棧也導(dǎo)致留住開(kāi)發(fā)人員成為一個(gè)問(wèn)題——移動(dòng)開(kāi)發(fā)者根本不想從事 C++ 項(xiàng)目開(kāi)發(fā)。這導(dǎo)致許多有才華的移動(dòng)工程師離開(kāi)了這個(gè)項(xiàng)目,而不是費(fèi)力地使用一個(gè)維護(hù)得不是很好的自定義技術(shù)棧。總的來(lái)說(shuō),移動(dòng)開(kāi)發(fā)社區(qū)非常有活力——新技術(shù)和新模式層出不窮,并且被迅速采用。最好的開(kāi)發(fā)人員喜歡讓他們的技能保持最新。
在具有標(biāo)準(zhǔn)技術(shù)棧的成熟產(chǎn)品環(huán)境中,跟上最新最好的技術(shù)潮流是一個(gè)挑戰(zhàn)。為了穩(wěn)定性,你犧牲了采用速度。當(dāng)你把自己鎖在一個(gè)自定義技術(shù)棧中,并置身于更廣泛的移動(dòng)生態(tài)系統(tǒng)之外時(shí),這一挑戰(zhàn)將被極大地放大。
盡管編寫(xiě)一次代碼聽(tīng)起來(lái)很劃算,但是相關(guān)的開(kāi)銷使這種方法的成本超過(guò)了所能獲得的好處(結(jié)果證明,這種好處比預(yù)期的要?。?。最后,我們不再通過(guò) C++(或任何其他非標(biāo)準(zhǔn)方式)共享移動(dòng)代碼,而是用平臺(tái)的原生語(yǔ)言編寫(xiě)代碼。
此外,我們希望我們的工程師有一個(gè)愉快的經(jīng)歷,能夠?yàn)樯鐣?huì)作出貢獻(xiàn)。這就是為什么我們決定使我們的實(shí)踐與行業(yè)標(biāo)準(zhǔn)保持一致。
查看英文原文: The (not so) hidden cost of sharing code between iOS and Android
聯(lián)系客服