Flutter實踐——實現一個動態頁
Flutter是Google新出的跨平臺開發框架,目前國內外已經有許多產品將Flutter用到正式的生產環境,本文用Flutter實現了一個瀑布流的動態頁面。
動態頁
1、頁面效果
廢話不多說,先上圖,如下是分別用natvie和Flutter開發的頁面:
左邊是Native頁面,右邊是Flutter頁面,可以看到Flutter的UI與natvie的幾乎沒有差別,但是Flutter並不像RN一樣使用Native的UI進行繪製,而是在Canvas上自己繪製,可以從如下的檢視層級圖中看出:
2、跨平臺覆蓋率
1. Android支援4.1及以上的版本,裝置要求是arm cpu 2. iOS支援 iOS8及以上,裝置是iPhone 4S及以上 複製程式碼
官網上的描述如下:

- 結論 Flutter對Android和iOS的支援基本上已經覆蓋了市場上的絕大部分使用者。
3、開發成本
- 適配層程式碼量小 Flutter是以Native作為出發點,在平臺畫布(Canvas)上構建一套獨立的繪製體系,獨立進行繪製和渲染,再通過粘合層抹平平臺差異,達到接近原生程式體驗的前提下的跨平臺框架。因為Flutter有自己獨立的繪製體系,因此相對於React Native和Weex來講,對Native的依賴更小,所以Flutter和Native之間適配層的程式碼量很少,Flutter裡面適配層的程式碼都是用於資料的傳輸。
- Dart語言開發 同時Flutter使用Dart語言開發,Dart本身的高效特性可節約大量的開發成本。
這裡列出開發同一個頁面,native和Flutter的程式碼量:
native | Flutter | |
---|---|---|
程式碼量 | 1346 | 668 |
4、包大小
Flutter 主要包含三部分,分別是:
1. sdk大小:so庫(Android)/Framework(iOS) 2. Dart程式碼的構建產物(Android和iOS) 3. Channel 層的Android程式碼(iOS這部分在構建產物中) 4. 資原始檔 複製程式碼
Flutter為了效能和除錯方便,在debug模式和release模式下的編譯方式不同,導致so庫和構建產物的大小相差很大,並且Flutter頁面越複雜,構建產物的大小越大,其中
1. 在debug模式下,so庫打入了x86_64、x86、arm64-v8a,總共22.28M,構建產物大小8.94M,總共31.2M 2. 在release模式下,so庫只有armeabi-v7a,總共3.46M,構建產物大小4.19M,總共7.65M 複製程式碼
而我們整合進工程的都是release模式,所以包大小至少增加7.65M,以下是Flutter、React Natvie、Weex包大小的比較:
Flutter | React Native | Weex | |
---|---|---|---|
大小 | >7.65M | 5M | 8.15M |
- 結論 Flutter的so庫及jar包的大小其實和React Native、Weex相差不大,甚至還要小一些,但是由於Flutter與React Native、Weex的實現方式不同,用Flutter寫的頁面的構建產物會打入到APP裡面,從而導致Flutter整合到APP裡面的大小要大一些,而且頁面越複雜,構建產物的大小越大。
5、記憶體
往下滑動列表,記錄頁面記憶體的使用情況


- 在native頁面中,Java記憶體、Native記憶體、Graphics記憶體都在增長
- 在Flutter頁面中,Java記憶體、Native記憶體幾乎不變,只有Graphics記憶體在增長,在官網上可以查到對Graphics記憶體的解釋:
- 結論 因為Flutter使用的記憶體不是Java記憶體,所以不會導致OOM。
6、首屏時間
首屏時間:頁面開啟,拉取資料,到第一幀顯示出來的時間。
native | Flutter | |
---|---|---|
首屏時間(ms) | 284.69 | 372.92 |
- 結論 Flutter首屏時間要慢於natvie頁面,慢100ms左右,Flutter首次的渲染速度還是略慢,之後的渲染速度就很快了。
7、頁面的流暢度
往下滑動列表,統計頁面的FPS,監測頁面的流暢度。
native | Flutter | |
---|---|---|
FPS | 50 | 55 |
- 結論 在滑動時,Flutter的流暢度比native要好一些。
8、 Flutter引擎crash率
截止到2018.6.16號,以下是RDM上報的Flutter相關的Crash資料:
錯誤型別 | 上報次數 | 影響使用者 | 影響裝置 |
---|---|---|---|
java.lang.NumberFormatException | 199 | 132 | 192 |
SIGBUS(BUS_ADRERR) | 94 | 5 | 6 |
SIGABRT | 2 | 2 | 1 |
SIGSEGV(SEGV_MAPERR) | 3 | 2 | 1 |
總共 | 298 | 141 | 200 |
上報的Flutter成功初始化的次數總共是6896796次,crash率只有0.005%
- 結論 可以看到Flutter的crash情況是特別少的,crash率只有0.005%,說明Flutter引擎很穩定。
###9、Flutter頁面異常情況 截止到2018.6.16號,監測到Flutter頁面總共開啟3172次,其中上報的error總共有19個,這些error都是Flutter Framework層的問題,Flutter頁面出現error,不會造成crash,但有可能會造成頁面異常,比如無法滑動或無法互動等。
開啟次數 | 頁面出現error的次數 | error率 | |
---|---|---|---|
Fluter頁面 | 3172 | 19 | 0.59% |
- 結論 上報的error都是Flutter Framework層的問題,但是一方面出錯的概率很小,另一方面Flutter Framework層都是用Dart來實現,而且更重要的一點是Framework層對於我們來說是白箱的,我們可以直接修改Framework層的程式碼,也意味著我們可以做更加深入的優化。
Flutter工程化實踐
1、cs協議資料獲取
因為資料都是通過cs協議來拉取的,而在Flutter層實現一個cs的拉取,還需要管理登入態,實在是一個複雜的事情,好在Flutter有與Native通訊的機制,但是Flutter與Native通訊只能傳基本型別,不能直接傳物件,傳輸支援的型別如下圖:

那怎麼把cs資料從Native傳給Flutter呢?好在Dart支援pb,我們可以把要使用的pb檔案,編譯成Dart程式碼,在Dart層組裝好pb的request並轉化為byte傳給Native,在Natvie層傳送cs協議,接受到byte格式的資料,在傳送給Flutter,Flutter把byte在轉化為物件使用。如下圖:

pb編譯成Dart程式碼如下圖:
2、iOS上crash問題
在開發過程中,當一直下拉拖動列表載入更多時,iOS很容易crash,crash日誌如下:

經過分析發現原來是在下拉載入更多的時候,一直在載入圖片,導致OOM。
於是去分析Flutter的圖片載入機制,發現Flutter雖然提供了一個ImageCache來快取載入的圖片,但他只能粗略的指定最大快取圖片張數,不能精確的使用記憶體大小來設定快取池容量,而且更坑的是在載入圖片的時候不能裁剪圖片尺寸,都是以原尺寸來載入圖片,查遍所有資料,也沒有看到Dart層能對圖片進行裁剪的方法,而我們瀑布流裡面的圖片都是大圖,所以導致在下拉載入更多的額時候,iOS的記憶體使用量一直在增加,最終導致OOM。
知道Flutter圖片快取策略太簡單、以及不能對圖片裁剪的問題後,接著就對Flutter的圖片載入機制做了如下改造,Flutter拿到圖片的url之後,自己不去載入,而是將url交給natvie處理,使用native的圖片庫將圖片下載並裁剪尺寸儲存在本地,然後把圖片在本地的地址傳給Flutter載入,成功解決了Flutter在iOS上的crash問題。流程圖如下:
