大規(guī)模采用 Kotlin 替代 Java?我們應(yīng)該知道這些利弊
當(dāng)大規(guī)模采用一門新語言時(shí),有許多不同的因素需要考慮,因?yàn)槭虑榭赡軙l(fā)生巨大的變化。對于許多人來說,選擇一門語言可以說是取決于個(gè)人偏好,但在 LinkedIn,我們有一個(gè)基礎(chǔ)團(tuán)隊(duì),負(fù)責(zé)評估這些基本技術(shù)決策的影響。最近,我們經(jīng)歷了評估 Android 開發(fā)語言的過程。從移動(dòng)基礎(chǔ)設(shè)施團(tuán)隊(duì)的角度來看,我想與你分享一下,我們在評估語言時(shí)所采取的步驟,以及為什么我們最終選擇了 Kotlin 語言。
方法
大規(guī)模采用移動(dòng)語言有許多不同的因素需要考慮。這樣一個(gè)決定會對開發(fā)人員的生產(chǎn)力、招聘、培訓(xùn)成本以及最終產(chǎn)品產(chǎn)生巨大的影響。例如,你可能會提高開發(fā)人員的生產(chǎn)力,并能夠交付更多的功能,但是必須付出應(yīng)用程序大小增加的代價(jià),這最終會影響用戶的下載。為了更好地理解采用特定語言的投資和回報(bào),我們認(rèn)為,公正的技術(shù)分析是我們做出決定的基礎(chǔ)。一旦做出了客觀的評估,我們就可以制定一個(gè)最適合整個(gè)公司移動(dòng)開發(fā)的路線圖。
技術(shù)分析
在 Linkedin,Android 開發(fā)跨越了不同形式和規(guī)模的團(tuán)隊(duì)——從開發(fā)大型應(yīng)用程序的大型團(tuán)隊(duì),到開發(fā)多個(gè)中小型應(yīng)用程序的小型團(tuán)隊(duì),再到只專注于構(gòu)建工具和基礎(chǔ)設(shè)施的團(tuán)隊(duì)。為了盡可能全面地執(zhí)行評估,我們成立了一個(gè)委員會,由每個(gè)應(yīng)用程序團(tuán)隊(duì)的高級開發(fā)人員、基礎(chǔ)設(shè)施團(tuán)隊(duì)和構(gòu)建工具團(tuán)隊(duì)的代表組成。在 LinkedIn 的 Android 開發(fā)背景下,這些是我們研究的領(lǐng)域。
語言特性
當(dāng)谷歌宣布將 Kotlin 作為 Android 開發(fā)的官方支持編程語言時(shí),我們都非常激動(dòng)。Kotlin 作為一門語言,具有許多 Java 世界中沒有的新特性,比如空值安全(null-safety)、擴(kuò)展、數(shù)據(jù)類、協(xié)程等等。雖然我們享受了這些新特性帶來的所有好處,但是 Kotlin 還在設(shè)計(jì)時(shí)刪除了某些 Java 特性,比如直接原始類型、靜態(tài)成員、檢查異常等等。在我們的分析中,我們深入了解了語言設(shè)計(jì)決策背后的想法,并研究了反編譯代碼來理解每個(gè)特性。這有助于我們將來制定指導(dǎo)方針,并對我們發(fā)布的代碼充滿信心。
兼容性
在 LinkedIn 的規(guī)模下,如果我們決定遷移到一種新的語言,遷移將是一個(gè)很長的過程,尤其是對于包含數(shù)百萬行代碼的代碼庫。正如理解每種語言的特性很重要一樣,考慮互操作性也同樣重要。幸運(yùn)的是,Kotlin 和 Android Studio 之間的集成經(jīng)常提供建議并幫助你重構(gòu)代碼。然而,歸根結(jié)底,它們是兩種不同的語言,讓它正常工作只是一個(gè)開始。在此過程中,你肯定希望構(gòu)建一些增強(qiáng)功能。例如,我們遇到的一些問題是為靜態(tài)成員添加 JVM 注解,并確保 Java API 把單一抽象方法(SAM)轉(zhuǎn)換 參數(shù)放在最后,這樣 Kotlin 編譯器就可以使用拖尾 lambda 語法 。
擴(kuò)展性
構(gòu)建時(shí)間是移動(dòng)工程師最優(yōu)先考慮的問題之一。使用 Kotlin,我們預(yù)計(jì)構(gòu)建時(shí)間會增加。LinkedIn 的移動(dòng)開發(fā)從數(shù)千行代碼到數(shù)百萬行代碼不等;因此,構(gòu)建時(shí)間的增加可能有很大差異。這可能不會立即引起注意,但隨著時(shí)間的推移,我們會密切關(guān)注轉(zhuǎn)向 Kotlin 所導(dǎo)致的時(shí)間增加,尤其是 LinkedIn 的規(guī)模下。為了測試構(gòu)建時(shí)間的長短,我們使用Android Studio Poet 創(chuàng)建了包含不同模塊數(shù)量、不同 Kotlin 和 Java 代碼行的 Android 項(xiàng)目作為模擬,同時(shí)保持類似的項(xiàng)目大小和依賴關(guān)系圖。我們使用Gradle Profiler 測量了清除構(gòu)建和增量構(gòu)建,以了解構(gòu)建時(shí)間花在哪里。
考慮到該項(xiàng)目的規(guī)模,它有可能影響從開發(fā)到生產(chǎn)的許多不同方面。運(yùn)行時(shí)性能是該分析的另一個(gè)關(guān)鍵領(lǐng)域。我們在 Kotlin 和 Java 中實(shí)現(xiàn)了幾種算法作為測試工具,并在物理設(shè)備上運(yùn)行它們。為了使實(shí)驗(yàn)更加真實(shí),我們還在這些測試中啟用了ProGuard 。研究結(jié)果與“Kotlin 和 Java 在 Android 運(yùn)行時(shí)上的性能評估 ”結(jié)果一致。有趣的是,Kotlin 中的大部分開銷來自空安值全特性,該特性使方法的每個(gè)調(diào)用都具有非可選參數(shù),都不能被編譯器映射到基本類型。這就導(dǎo)致了對 Intrinsics.checkParameterIsNotNull 的調(diào)用,它要為我們實(shí)驗(yàn)中的大部分開銷負(fù)責(zé)。
應(yīng)用程序的大小是由許多因素造成的。按照谷歌的說法 ,初始的 Kotlin 標(biāo)準(zhǔn)庫導(dǎo)入將使應(yīng)用程序的大小增加大約 1MB,隨著時(shí)間的推移,更多的 Java 代碼被轉(zhuǎn)換成 Kotlin,由于 Kotlin 字節(jié)碼方法的增加,應(yīng)用程序?qū)⒆兊酶?。幸運(yùn)的是,ProGuard 能夠從 Kotlin 生成的代碼中去除并大大減少未使用的方法。我們創(chuàng)建了示例應(yīng)用程序,并將它們從 Java 轉(zhuǎn)換為純 Kotlin,以了解方法數(shù)量和應(yīng)用程序大小的開銷。
語言升級
移動(dòng)開發(fā)的前景在不斷變化,了解升級過程非常重要。在過去,我們看到語言升級伴隨著不兼容的更改、構(gòu)建失敗和許多意外錯(cuò)誤,這些錯(cuò)誤最終迫使我們鎖定主干,甚至執(zhí)行語言升級。JetBrains 承諾為 Kotlin 提供兩種形式的向后兼容性:
新編譯器仍將可以使用舊二進(jìn)制文件(但是舊編譯器可能無法識別新二進(jìn)制文件,比如 javac 1.6,它無法讀取由 javac 1.8 編譯的類)。
舊二進(jìn)制文件將可以在運(yùn)行時(shí)繼續(xù)和新二進(jìn)制文件一起工作,而新代碼可能需要新依賴項(xiàng)。
在源代碼兼容性方面,Kotlin 遵循 API 棄用警告的模式,一旦升級到新的編譯器版本,該警告就會逐漸變?yōu)殄e(cuò)誤。在過去,我們看到升級通常會給社區(qū)足夠的時(shí)間來適應(yīng)這些更改。例如,KT-21515 中的語言行為變化被標(biāo)記為警告,并在接下來的 1.2.x 補(bǔ)丁版本中得以保留,直到 Kotlin 1.3。然而,值得注意的是,JetBrains 并沒有聲明將在主要版本更改中提供哪種類型的兼容性。
開發(fā)者生態(tài)系統(tǒng)
Kotlin 能夠輕而易舉地與 Android Studio 搭配使用并不奇怪,因?yàn)?JetBrains 是這兩款產(chǎn)品背后的主要貢獻(xiàn)者。大多數(shù)(如果不是全部的話)Android Studio 工具都可以無縫地與 Kotlin 一起工作,包括調(diào)試器、內(nèi)存分析器、Android 分析器等等。值得一提的 Kotlin 特性包括 Kotlin 自動(dòng)轉(zhuǎn)換和 Kotlin 反編譯器。后者讓你深入了解 Kotlin 代碼的底層工作機(jī)制。
LinkedIn 使用 Gradle 作為 Android 和 Java 產(chǎn)品的實(shí)際構(gòu)建系統(tǒng)。借助 Kotlin- Android Gradle 插件,Kotlin 在 Gradle 中可以順暢地工作。Gradle 企業(yè)版提供遠(yuǎn)程緩存解決方案。Kotlin 1.2.21 允許 Kotlin 項(xiàng)目使用構(gòu)建緩存。具體來說,對于 Gradle 構(gòu)建緩存,它還需要使用 Kotlin Gradle Plugin 1.2.20 和 Gradle 4.3 或更高版本??偟膩碚f,由于采用 Kotlin 而引起的構(gòu)建工具的變化很少。然而,我們的分析主要集中在 Gradle 上,可能不適用于其他構(gòu)建系統(tǒng),比如 Buck。
正如我們關(guān)注開發(fā)人員的生產(chǎn)力一樣,質(zhì)量也是我們要考慮的另一個(gè)重要因素。編寫測試對于確保代碼質(zhì)量至關(guān)重要。我們花了一些時(shí)間來理解測試生態(tài)系統(tǒng)的局限性及其與其他語言的兼容性。這不僅包括測試框架,還包括代碼覆蓋率分析、CI 管道,最重要的是,在開始編寫 Kotlin 測試之前,在不違背測試標(biāo)準(zhǔn)的情況下所要采取的步驟。該分析還包括 Android 中的主要靜態(tài)分析工具Android Lint ,它現(xiàn)在使用UAST 支持 Java 和 Kotlin 分析及樣式檢查工具。
行業(yè)分析
自從 Kotlin 于 2017 年在谷歌 I/O 大會上宣布成為 Android 開發(fā)的一等語言以來,越來越多的工程師開始接受 Kotlin。Stack Overflow 2018 調(diào)查顯示,Kotlin 是第二受歡迎的編程語言。Kotlin 還被認(rèn)為是未來語言中增長最快的語言,與 Swift、Go、Haskell 和 Rust 并列第一。據(jù)谷歌報(bào)道,2017 年,當(dāng) Kotlin 被宣布為 Android 開發(fā)的第一語言時(shí),Google Play 中 9% 的應(yīng)用程序已經(jīng)在使用 Kotlin。一年后,即 2018 年 5 月,谷歌宣布這一數(shù)字已增至 35%,同比增長近 6 倍。
我們分析了Google Play 商店中的前 300 個(gè)免費(fèi)應(yīng)用程序 ,并根據(jù)它們的 DEX 文件大小進(jìn)行了篩選,以了解有多少應(yīng)用程序在大規(guī)模使用 Kotlin。雖然這個(gè)數(shù)字包含了所有第三方庫的代碼,但它能很好地反映應(yīng)用程序代碼的大小。作為一個(gè)初步步驟,我們排除了 APK 中沒有專門 Kotlin 文件夾的應(yīng)用程序,該文件夾將包含 Kotlin 運(yùn)行時(shí)使用的一些 Kotlin 內(nèi)置文件。由于代碼的混淆,計(jì)算 Kotlin 在代碼中的百分比非常棘手。但是,我們使用apktool 將所有 DEX 文件轉(zhuǎn)換為 SMALI 代碼,并發(fā)現(xiàn)了兩個(gè)不同的線索,它們標(biāo)識了用 Kotlin 編寫的類。對于沒有從.source 屬性中刪除原始文件名的應(yīng)用程序,我們可以提取擴(kuò)展名 *.kt。對于其他應(yīng)用程序,我們尋找 kotlin.Metadata 類??偟膩碚f,我們能夠建立一個(gè)圖表來顯示應(yīng)用程序及其類的數(shù)量、代碼大小和 Kotlin 百分比。這讓我們了解到社區(qū)有多成熟,以及我們可以從合作伙伴那里得到多少支持。
起初,大多數(shù)庫所有者認(rèn)為,在自己的代碼中使用 Kotlin 實(shí)現(xiàn)細(xì)節(jié)是不成熟的,不應(yīng)該強(qiáng)加給用戶。隨著 Kotlin 社區(qū)變得越來越活躍,這種觀點(diǎn)已經(jīng)開始轉(zhuǎn)變。Square 是許多最受歡迎的 Android 開源庫的所有者,它宣布他們的核心Okio 庫已經(jīng)用Kotlin 重寫 ,并且他們將在未來的所有版本中使用Kotlin。其他一些庫項(xiàng)目已經(jīng)選擇遵循谷歌針對Android KTX 提供的示例,并發(fā)布了一個(gè)用 Java 編寫的基本庫,在其中單獨(dú)的構(gòu)件中添加了特定于 Kotlin 的擴(kuò)展點(diǎn)。例如,F(xiàn)asterXML 的 Jackson 項(xiàng)目添加了一個(gè)用于序列化和反序列化 Kotlin 類的Kotlin 模塊 ,以及 Mockito,該項(xiàng)目正在研究添加一個(gè)針對 Kotlin 優(yōu)化過的 mock 語法 。
全方位考察
除了上面列出的要點(diǎn),我們還花時(shí)間研究了與 LinkedIn 內(nèi)部工具系統(tǒng)的集成,包括崩潰報(bào)告、審查流程和 CI 管道。我們不斷與社區(qū)和其他公司的開發(fā)人員進(jìn)行溝通,包括谷歌和 JetBrains,不僅要了解他們的方向,還要確保我們的決定與未來的 Android 開發(fā)保持一致。人才始終是我們首先要考慮的因素之一;因此,與招聘和培訓(xùn)有關(guān)的成本也是我們分析的重要部分。
總的來說,我們從社區(qū)聽到了各種好處和證詞。綜合分析,我們能夠建立一個(gè)路線圖,使 LinkedIn 進(jìn)入下一個(gè)使用 Kotlin 的 Android 開發(fā)時(shí)代,并且相信,我們可以最小化技術(shù)風(fēng)險(xiǎn),已經(jīng)了解了對招聘和培訓(xùn)的影響,能夠保持技術(shù)標(biāo)準(zhǔn),并不斷為我們的會員提供價(jià)值。