Dropbox 從支持到放棄使用 C++ 跨 iOS、Android 平台開發的始末

寧願寫兩遍代碼,也不用C++跨iOS、Android平台開發:
https://kknews.cc/zh-tw/tech/y3g4knj.html

該文章發布於 2019年中

Dropbox 最近宣布將放棄用 C++ 編寫跨 iOS、Android 平台代碼,轉而使用各平台的原生框架(Swift / Kotlin),理由是代碼共享相關的隱藏成本太高。有趣的是,2014 年,Dropbox 在給 Facebook 的開發人員做分享時,卻曾直言寫兩套代碼會帶來種種弊端,推薦使用 C++ 進行跨平台開發。為什麼幾年前的經驗之談卻成了幾年後的技術債?C++ 因何躺槍?跨平台開發前景如何?本文試圖一一為你講解。


事件綜述

編寫一次,隨處運行。這句話不知道被多少工具和框架寫在開篇介紹中,用以吸引更多公司和開發者。然而,作為這一策略忠實的實踐者,Dropbox 近日宣布放棄這一想法,寧願為不同的平台寫兩遍代碼,也不用 C++ 跨 iOS、Android 兩大平台共享代碼的方式進行開發。

早在 2013 年,Dropbox 就採用上述策略進行移動開發,這背後的想法很簡單:用 C++ 編寫一次代碼,而不是用 Java 和 Objective-C 編寫兩次。那時,整個移動工程團隊相對還比較小,但需要支持快速增長的移動路線圖。因此,公司希望找到一種方法,使這個小團隊可以快速交付大量 Android 和 iOS 代碼。

如今,Dropbox 完全放棄了這個策略,轉而使用各個平台的原生語言(主要是 Swift 和 Kotlin,這兩種語言在剛開始制定移動策略時還不存在)。這個決定的主要原因是代碼共享相關的隱藏成本太高,這源於同一個基本問題:

以非標準的方式編寫代碼,使 Dropbox 承擔了一些開銷,而如果採用廣泛使用的平台默認選項,就不必擔心這些開銷,這種開銷最終比只編寫兩次代碼要昂貴得多。


C++ 的問題?

隨後,Dropbox 總結了成本太過高昂的主要原因,比如自定義框架和庫的開銷、自定義開發環境的開銷、處理不同平台差異的開銷以及培訓、招聘和留住開發人員的開銷,這與 C++ 本身存在的問題密切相關。但是,C/C++ 是唯一一種編譯器同時受到谷歌和蘋果支持的語言,如果使用其他語言將會產生一系列更麻煩的問題。

1、自定義框架和庫的開銷

關於使用 C++,最容易出現的開銷就是構建框架和庫,這大致可以分為兩類:


  • 框架,讓開發者與主機環境交互,構建一個功能齊全的移動應用程式。例如 Djinni 、用於在後台和主線程中運行任務的框架等。
  • 一些取代本可以在平台原生語言中使用的默認語言或開源標準的庫。例如:用於 JSON(反)序列化的 json11 ;C++ 非可空指針 nn 。

如果使用平台原生語言,那麼這些代碼都是不必要的。當然,並不排除個別開發者可以很好地利用開源 C++ 庫,但是 C++ 開發社區中的開源文化並不像移動開發社區那麼強大(特別是幾乎不存在的 C++ 移動社區)。這些成本在 C++ 中特別高(與 Python 或 C# 等其他可能的非原生語言相比),因為它缺少一個功能齊全的標準庫。

雖然 C++ 標準委員會為了這門語言做了大量工作,甚至為了 C++20 的新特性舉辦了一場歷史上規模最大的會議(180 人參會),試圖通過會議確定哪些特性可以加入新版本,比如 Concepts、Ranges、Modules、Coroutines 等,但大部分開發人員並不認可此次調整,並將部分新特性歸結為「語法糖」,在社交平台進行吐槽。

國外的一位遊戲開發人員接連在社交平台發表看法,甚至發表了一篇長文聲明自己不看好 C++20 的新特性,並認為新版本沒有解決最關鍵的問題,這讓其未出世就被群嘲。可見,整個 C++ 社區是存在一定問題的,很難讓其達到移動開發社區中的活躍度,很多問題也是懸而未決。

2、自定義開發環境的開銷

移動生態系統有許多工具可用來提高開發效率,但 C++ 社區就不太行。開發者必須投入時間構建支持 C++ 代碼共享的工具。甚至,Dropbox 親自做了定製構建系統,創建了包含 C++ 代碼以及 Java 和 Objective-C 封裝器的庫,並且生成 Xcodebuild 和 Gradle 都能識別的目標文件。這個系統是一個很大的成本支出和拖累,因為需要不斷更新以支持兩個構建系統中的更改。

3、處理不同平台差異的開銷

儘管 iOS 和 Android 應用程式都是「移動應用程式」,通常具有相同的特性和功能,但平台本身存在一些影響實現的差異,甚至不能真正地編寫一次代碼就在不同的平台上運行,必須花費大量時間將代碼集成到不同的平台中,並編寫特定於平台的代碼(有時這些代碼就位於 C++ 層)。

4、培訓、招聘和留住開發人員的開銷

如今,市面上越來越難招聘到具有相關 C++ 經驗、對移動開發感興趣的高級工程師。當然,如果願意提高招聘成本,給出更有吸引力的薪水,這個問題不難解決。但是,怎麼把這些開發人員留住也是一個大問題。事實上,移動開發社區有大量新技術和新模式層出不窮,移動開發者可能根本不想從事 C++ 項目開發。

如上種種,通過 C++ 共享移動代碼的方式讓開發成本變得非常高昂,這也讓曾經的支持者 Dropbox 不得不選擇放棄。


跨平台開發的 Yes or No

"Write once, run anywhere" 一直以來就是開發者的夢想。但從 Dropbox 的經驗來看,實現之路堪稱曲折甚至反覆。

我們發現了一個特別有意思的新聞:2014 年,Dropbox 的開發人員給 Facebook 的開發人員做了個講座,分享了他們使用 C++ 做跨平台開發的經驗之談。他們特別提到,iOS 和 Android 平台代碼庫的不一致會帶來一系列問題:

  • 開發和維護成本成倍增加。
  • 開發團隊需要多次修復同樣的缺陷。
  • 針對某個平台報告的缺陷可能會被另一個平台忽視掉。
  • 不同平台的 App 行為可能會有預料之外的細微差異。
  • 性能優化成本高昂並且與平台相關。

Dropbox 賴以克服上述這些問題的策略基礎就是為所有 UI 無關的代碼建立一個共享的跨平台 C++ 庫,例如數據和網絡部分的邏輯。UI 部分還是使用原生代碼編寫,以便儘可能地利用平台對動畫特效和設備傳感器等的支持,並且確保充分的響應速度。

5 年過去,當年的經驗之談似乎變成了 Dropbox 的技術債,他們最終放棄了用 C++ 做跨平台開發,轉而用各平台的原生語言做開發工作。


跨平台開發,是時興潮流還是明日黃花?

2018 年,Airbnb 放棄使用 React Native,回歸使用原生技術,究其原因主要是:項目龐大後,維護困難;第三方庫良莠不齊;兼容上需要耗費更多精力……

騰訊客戶端工程師方秋枋接受 InfoQ 採訪時曾表示:

這些都是跨平台框架的通病。跨平台意味著需要花費很多時間來解決平台差異性問題,同時要面臨第三方庫不夠原生平台豐富健壯的現狀。跨平台其實是犧牲部分功能和體驗,換取開發速度和一致性的權衡,並不是業務開發的銀彈。

微信團隊在跨平台開發方面的解決方案是:

目前微信團隊並沒有統一使用一套跨平台開發框架。我們也在積極探索各種可能性。

微信小程序,是跨平台開發的第一、二階段的融合做法,利用 Webview 作為渲染容器,通過規定 Web 技術棧的子集(WXML、WXSS),讓客戶端獲得更多的渲染性能優化空間,同時通過規範化組件的使用,逐步引入原生控制項代替 Webview 渲染。

除此之外,我們還探索了基於 C++ 進行跨平台開發。實質上還是第二階段的做法,只不過是使用 C++ 作為中間語言,去編寫業務代碼和調用原生控制項。為什麼要採用 C++ 呢? 因為 C++ 安全性會更高一點,同時性能也會更佳。可以應用在一些對安全性要求比較高的場景。


Flutter 會是跨平台開發的終極之選嗎?

這個問題很有意義。能實現跨平台開發的框架也五花八門,讓人眼花繚亂。最流行的跨平台框架有 Xamarin、PhoneGap、Ionic、Titanium、Monaca、Sencha、jQuery Mobile、React Native、Flutter 等等。但這些工具的表現也是高低有別,各有千秋。熱度不減的只有 React Native 和 Flutter,原因在於這二者背後有 Facebook 和 Google 背書,開發者更加信任。

Flutter 的優勢在於:它完全免費,徹底開源;可以用來更快地創建應用;具有出色的用戶介面(UI);節省代碼量;可接入平台原生功能;最適合 MVP 開發(最小化可行產品);較老的設備也使用相同 UI 運行應用;減少測試工作量;更豐富的社區支持;較低的維護難度;內置來自 Dart 的包管理器。

其中不少特性如節省代碼量、接入平台原生功能、支持較老設備、較低維護難度等等,都擊中了跨平台開發的痛點。業界認為,目前跨平台開發沒有一個完美的解決方案,相對來說 Flutter 在兼容上做得好一點,通過自底向上自研框架來儘可能減少平台差異,但是可靠性仍需進一步檢驗,引入 Flutter 需要大家對自己公司業務有非常清晰的認知,並權衡好利弊。

隨著 Google 對 Flutter 的持續加碼,也許在不久的未來,Flutter 將成為跨平台開發的最佳解決方案。


程式語言編年史

程式語言編年史原文 下面這張圖片描繪了整個程式語言的歷史。包括各種程式語言的發明人、程式語言的特點和適用領域、被什麼網站或公司使用等 (檢視 完整高清圖 )。 之所以會有那麼多不同的程式語言是因為設計程式語言的初衷不同、對語言學習曲線的追求不同、不同程式之間的執行成本差異...