"A fast app is great, but a smooth app is even better."

使用Flutter beta版上線了一個APP的故事

2018年的11月底,我第一次打開Flutter的官網,想看看Flutter到底是什麼;3個星期後,我們趕在Apple的App Store審覈團隊聖誕節休假前,提交了第一個使用Flutter開發的App。當然,是iOS和Android雙端同時提交。

我們使用Flutter開發的產品是一個圖片feed流,作爲一個模塊嵌入到一個美顏相機裏面。

在接下來的2個月內,我們保持着每2個星期發版的頻率,成功上線了以下核心功能:

  • 登陸
  • 作品發佈
  • 作品刪除
  • 關注他人
  • 作品點贊
  • 作品評論
  • 消息中心
  • 發現feed流
  • 關注feed流(你所關注的發佈者的動態feed流)
  • 各個頁面時長的數據統計

再來看看我們整個Feed流團隊有多少人:

  • 產品經理:1人
  • 作品質量把控:1人
  • 後端開發:2人
  • iOS開發:1人
  • Android開發:2人
  • Web端開發:2人
  • 測試:2人

以上是第一個版本發佈後的團隊組成。其實,我們的第一個版本期間,開發只有4人(後端,iOS,Android,Web,各1人)。

在使用Flutter的這幾個月內,我被Flutter這特立獨行的跨端思想和優秀的表現所感動。從一開始的全身每個細胞都在牴觸到短短几個星期之後就差點成爲腦殘粉,我經歷了難忘的一次“真香”之旅。

思緒回到我們決定使用Flutter的那一天,我們做了一個冷靜之後看起來十分激進和冒險的技術選型。因爲我們當時的場景是:Flutter beta版 + 和已有的native APP混合 + 已有的native是一個相機類App + Flutter開發的功能是一個feed流。這個組合在當時的場景下是十分苛刻的。接下來我具體解釋一下其中的挑戰在哪裏:

  • Flutter beta :因爲是beta版本,所以框架功能不面面俱到,也存在bug。
  • Flutter和已有的native混合 :因爲當時使用的是beta版本,並沒有官方的集成方案。混合模式下如何開發,調試,打包,集成之後對整個App包大小的影響有多大,都是挑戰。
  • 已有的native是一個相機類App :相機類的App本身佔用的內存就相對來說很大。
  • feed流: feed流功能,本身對性能要求高,因爲刷起來需要流暢,因爲圖片很多,對內存也是有極高的要求。
  • 上線時間短 :第一個版本,必須要趕在聖誕節前上,從項目立項到上線,不過3個星期的時間。
  • 上線頻率高 :保持每2周發一個新版。

但是,上面的這些挑戰,Flutter都很好地消化了。基於我個人的開發體驗來講,是因爲Flutter具備以下優點:

  • 高性能 :我們的核心是feed流,對於一個feed流來說,滑起來 流暢 是重要的指標。因爲Flutter的高性能的特性,我們的feeds能達到 60fps
  • 雙端一致 :因爲我們的功能是iOS和Android雙端都要支持,因爲Flutter優秀的跨端技術,使得我們寫一份代碼,可以同時在雙端運行,並且保持雙端UI,功能等高度的一致性。
  • 減少測試時間 :因爲雙端的高度一致,測試同學不需要每一端都事無鉅細地測試,特別是UI的部分,只需要測試一端便行。
  • 高效的開發效率 :Flutter提供豐富又高度定製化的組件,加上Flutter擁有Hot Reload,使得你在秒級的時間內看到你的代碼更改,而不是像以前那樣哪怕只是1px的改動,都需要經歷重新的編譯,打包,安裝,可能2分鐘過去之後,才能看到最後的結果。

看到這裏的同學,腦子裏面可能一直縈繞着一個問號:你一直在說的Flutter到底是什麼?

Flutter是什麼?

Flutter

Flutter是谷歌推出的跨平臺UI框架,目前已經支持的平臺有: mobiledesktopweb 。Flutter的目標是以上平臺能支持到以下對應的操作系統或平臺:

  • mobile: iOS && Android
  • desktop: macOS, Windows,Linux
  • web:mobile && PC

就目前來說,mobile端的支持是最成熟的。desktop端僅支持macOS,web端有beta版可以使用,但是還不是十分成熟。

Flutter VS React Native

說到跨平臺的技術或者框架,我們可能自然會想到React Native。雖然很多產品和公司現目前已經停止了對React Native的使用,但是不可否認地是,在Flutter之前,React Native是最爲大家熟悉的跨端UI框架。

所以,這裏簡單地做一個Flutter和React native的對比。二者最大的不同在於實現跨端的原理不同,從而導致了二者在相關的指標上的差異。所以,接下來,我會就二者的跨端原理做一下簡單地比較。

flutter vs react native

上圖以在Android端爲例。

假如我們需要一個Native端的button,React Native的原理是,使用JavaScript寫一個button,由C++寫的Bridge把JavaScript寫的button轉換爲Java寫的button,最終被編譯爲機器碼。

但是在Flutter裏面,採用一門新的語言(Dart)來寫button,因爲Dart也是基於VM的一門語言,所以,Dart可以直接被編譯爲機器語言。

所以,我們可以看出Flutter沒有采用‘Bridge’,這樣帶來的好處是:

  • 高性能 :因爲省去了JavaScript和Native之間的轉換過程。
  • 雙端統一 :不論是在iOS端還是Native端,都是採用Dart語言編寫UI,底層都是採用Skia這個圖形庫繪製。

以上,我們對Flutter和React Native做了簡單地對比,相信大家已經能看出Flutter天然具有的一些優勢了。接下來,我們更全面地來認識一下Flutter優秀的特性:

Flutter有什麼特性

  • 開發效率高
  • 雙端一致的UI
  • 豐富而美麗的UI
  • 媲美Native一般的性能
  • open source

開發效率高: Flutter擁有hot reload功能,每一次修改代碼之後,只需要保存,不論是在模擬器上還是在連接的真機,都可以在秒級的時間內,馬上看到效果,而不用再像以前以下需要經歷重新的編譯,打包,安裝。

雙端一致的UI:React Native是把JavaScript代碼轉換爲Java或者Swift語言,最終調用平臺各自的渲染機制來渲染UI。而Flutter不論是在iOS還是Android端都統一採用Skia(一個二維圖形庫)來渲染UI,這樣就從根源上解決了由於平臺不一致帶來的UI不一致的問題。

豐富和美麗的UI:Flutter內置的Material Design和Cupertino(iOS風格)組件,能讓你的App擁有現代化的漂亮的UI。又因爲在上一點提到的,因爲採用了Skia,你頁面上的每一個像素都是Skia畫的,所以你可以對你的組件進行高度的定製化。

媲美Native一般的性能:Flutter使用Dart作爲它的編程語言,dart的編譯器會把你寫的Flutter代碼直接編譯成機器碼,從而帶來跟native一樣的性能。

如果只是平白地羅列以上的這些優點,可能無法令人信服。接下來我們就更底層,更細緻地來論證以上觀點。

首先,我們來深入瞭解一下Dart這門開發語言,因爲它對Flutter高效的開發效率,和高性能都是至關重要的。

爲什麼Futter採用Dart語言

在瞭解Dart之前,我們先來認識2個概念: AOT,JIT

  • AOT: Ahead-of-Time
  • JIT: Just-in-Time

AOT是在運行前,已經完全編譯好;而JIT則是在運行中會進行分析和編譯。AOT的優點是運行速度快,因爲它不需要在運行時再進行分析和編譯,因爲它已經提前編譯好了。相對的,JIT的運行速度慢,因爲它在運行的過程中會停下來做分析和編。

思考一下,在日常開發native app的時候,我們希望代碼修改可以以最快的速度被看到,而不用每一次都需要經歷編譯,打包,安裝。相應地,當我們在使用一個native app的時候,我希望它能很快響應我的操作,比如滑動的時候,頁面很流暢;動畫也會不會卡頓。

爲了滿足以上2種需求,我們希望擁有一種結合了JIT和AOT的技術。事實上,Dart正是如此。

在我們開發的時候(debug mode)的時候,Dart採用JIT的模式,我們前面提到的Hot Reload也正是依賴於此。Hot Reload的工作原理是通過把修改後的源代碼文件塞給Dart的虛擬機(VM),等虛擬機根據最新的屬性和方法更新類文件之後,Flutter會自動重新構建組件樹(widget tree),從而你可以迅速地看到你修改的結果。

在打包的時候(release mode)的時候,Dart採用AOT模式。AOT模式的好處是使得用戶可以在很短的時間內啓動App,在使用App的時候,也會很流暢,因爲所有的東西都已經被編譯好了。

Dart的線程

如果你瞭解Java,C++,或者Swift等,你知道這些支持多線程併發的語言,採用的是一種叫做 搶佔式調度(Preemptive scheduling) 的機制。搶佔式調度,即操作系統給每一個進程都分配一定的CPU佔用時間,當進程A的時間已經花完,這時候就該輪到進程B來佔用CPU了。但是,當不同的進程或者線程使用了同一個資源(比如同一段內存)就會造成 資源競爭(Race condition)

資源競爭可以造成嚴重的後果,比如讓你的App崩潰掉,或者造成數據的丟失。經典地解決資源競爭的辦法是加鎖,但是加鎖本身又能帶來別的問題,比如死鎖和資源飢餓。

Preemptive Scheduling

Dart則採用了另外一種思路。線程到了Dart裏面叫做 isolate 。不同的isolate之間是不共享內存的,也是獨立做垃圾回收的。

我們的一個Dart程序執行在一個isolate裏面。在一個isolate裏面,所有的事件都是通過eventLoop的方式來進行異步處理。

相應地,Dart提供futures,async, await來處理異步請求。這使得我們在渲染UI的同時,可以進行一個HTTP的請求或者讀取文件之類的的操作,但是不會造成頁面的卡頓。

Dart統一了UI編寫

Dart不僅是從語言特性層面爲Flutter的性能起到至關重要的作用,在對開發效率的提高上也是十分終於的。前面我們已經提到了Hot Reload功能以來與Dart的JIT編譯模式,除此之外Dart統一的UI組件編寫方式,也對我們日常的開發效率起到了非常大作用。我們先來看一段使用Dart編寫一個Flutter組件的示例:

dart-widget

Dart編寫一個組件的方式,和iOS,Android,Web端的方式都不一樣。

傳統的Web端開發,是把一個組件所需要的HTML,CSS, JavaScript分開到不同的模塊裏面。假如你需要改動css,你得先跳到css所有的領域(可能是另外一個單獨的css文件,或者假如你使用vue,那麼就是在這個.vue文件的style模塊)。然而,在Dart裏面,一個組件的dom,樣式,事件處理都是作爲一個組件的屬性存在,他們都是在一個地方,既不會分開到不同的文件,也不會分開到不同的模塊。

我記得我一開始寫Flutter的組件的時候,非常地不習慣,甚至產生抗拒心理。因爲Flutter的組件結構方式與我寫了多年的web端組件寫法是完全對立的。但是,當我寫了2個星期後(我在網上到討論,一般大家的過渡期也是2個周),開始覺得這種寫法是如此自然又高效, 甚至開始懷疑以前自己寫的那些代碼:web端那種把HTML, CSS, JavaScritp分開的形式,是不是本身是種錯誤。也開始反思,爲什麼我從來沒有懷疑過這種既定規則的合理行。

因爲我自己沒有長期iOS端和Android端開發的經驗,我不知道從iOS端的Layout佈局和Android端的XML佈局轉換到Dart,是怎樣的心路歷程。但是,下面是我從網上找到的一些感想:

native

Dart dev tool

Dart提供了一些工具來幫助你日常的開發和調試,其中一個是非常厲害的工具就是Dart dev tool。舉一個例子:下圖展示的是一個在Flutter裏面常見的bug:子元素溢出了。這個時候你打開Dart dev tool,你可以看到這個組件的佈局,在這個工具上會顯現這個元素相關的一些屬性值,給你提供排除bug的思路。比如這個例子裏,我們看到flex的值爲‘null’,這可能是bug的原因,你可以通過下拉框選擇一個flex的值,看是否可以解決這個bug:

dar-dev-tool

是否推薦項目採用Flutter

前面花了很大的篇幅來介紹我和我實際使用Flutter上線了一個App的故事和感受,也從Skia和Dart層面去分析了爲什麼Flutter具備有那麼多的優點,而不只是官方宣傳。那麼最後的最後,作爲一個還十分年輕的技術,flutter是否適合在項目上使用呢?

沒有一個技術是完美的,但是除了一些不可抗力的因素外,我們去做一個技術選型,依據的標準應該是它的優點是否超越它的缺點。不如我們再次來總結一下Flutter比較核心的的優點和缺點:

Flutter的優點:

  • 跨端,跨平臺
  • 雙端高度一致的UI
  • 漂亮的UI
  • 高性能
  • 開發效率高

Flutter目前存在的缺點:

  • 包的大小不算小(特別是混合項目)
  • 目前github上的open的issue有7000多
  • Flutter的error message不友好
  • 可能會有內存泄漏的問題(常見在iOS端)

Flutter從2018.02發佈beta版,2018.12發佈1.0版本,短短2年時間,到現在github上的start數量已經有81.6K。不論是國內還是國外,已經有大量的使用Flutter開發的產品,比如 Realtor.com Real Estate Search, Google Assistant,鹹魚等。

當下,Flutter和Fuchsia(谷歌正在研發的一個新的操作系統)都是谷歌的重心,所以大概率Flutter不會成爲一個爛尾的項目。而且,就Flutter目前擁有的成績證明,它已經足夠優秀,何況它還這麼年輕。

所以從我自己的角度來說, 十分推薦採用Flutter 。可能web端和desktop端目前還不那麼成熟,但是 native端可以大膽嘗試

更多精彩洞見,請關注微信公衆號:ThoughtWorks洞見

相關文章