本篇將帶你深入瞭解 Flutter 中打包和外掛安裝等原理,幫你快速完成 Flutter 整合到現有 Android 專案,實現混合開發支援。
前文:
一、前言
隨著各種跨平臺框架的不斷湧現,很多時候我們會選擇混合開發模式作為腳手架 ,因為企業一般不會把業務都壓在一個框架上,同時除非是全新專案,不然出於對原有業務重構的 成本和風險 考慮,都會選擇混合開發去嘗試入坑。
但是混合開發會對 打包、構建和啟動等流程熟悉度要求較高 ,同時遇到的問題也更多,以前我在 React Native
也寫過類似的文章 :《從Android到React Native開發(四、打包流程解析和釋出為Maven庫)》 ,而這方面是有很多經驗可以通用的,所以適當的混開模式有利於避免一些問題,同時只有瞭解 Flutter
整體專案的構建思路,才有可能更舒適的躺坑。
額外嘮叨一句,跨平臺的意義更多在於解決多端邏輯的統一 ,至少避免了邏輯重複實現,所以企業剛開始,一般會選擇一些輕量級業務進行嘗試。
二、打包
一般跨平臺混合開發會有兩種選擇:
- 1、將
Flutter
整體框架依賴和打包指令碼都整合到主專案中。 - 2、以
aar
的完整庫整合形式新增到主專案。
兩種實現方法各有利弊:
-
第一種方式可以更方便執行時修改問題,但是對主專案“汙染”會比較高,同時改動會大一些。
-
第二種方式 需要單獨除錯後,更新
aar
檔案再整合到專案中除錯,但是這類整合方式更乾淨,同時Flutter
相關程式碼可獨立執行測試,且改動較小。
一般而言,對於普通專案我是建議以 第二種方式整合到專案中的 ,通過新建一個 Flutter
工程,然後對工程進行元件化指令碼處理,讓它 既能以 apk形式單獨執行除錯,又能打包為aar形式對外提供支援。
相信對於原生平臺熟悉的應該知道,我們可以通過簡單修改專案gradle
指令碼,讓它快速支援這個能力,如下圖片所示,圖片中為省略的部分指令碼程式碼,完整版可見 flutter_app_lib 。
我們通過了 isLib
標記為去簡單實現了專案的打包判斷,當專案作為 lib
釋出時,設定 isLib
為 true,之後執行 ./gradlew assembleRelease
即可 ,剩下的工作依舊是 Flutter
自身的打包流程,而對於打包後的 aar
檔案直接在原生專案裡引入即可完成依賴。
而一般接入時,如果需要 token
、使用者資料等資訊,推薦提供定義好原生介面,如 init(String token, String userInfo)
等,然後通過MethodChannel
將資訊同步到 Flutter
中。
對於原生主工程,只需要接入 aar
檔案,完成初始化並開啟頁面,而無需關心其內部實現,和引入普通依賴並無區別。
你可能需要修改的還有
AndroidManifset
中的啟動MainActivity
移除,然後新增一個自定義Activity
去繼承FlutterActivity
完成自定義。
三、外掛
如果普通情況下,到上面就可以完成 Flutter
的整合工作了,但是往往事與願違,一些 Flutter
外掛在提供功能時,往往是通過原生層程式碼實現的,如 flutter_webview
、android_intent
、device_info
等等,那這些程式碼是怎麼被引用的呢?
這裡稍微提一下,用過
React Native
的應該知道,帶有原生程式碼的React Native
外掛,在npm
安裝以後,需要通過react-native link
命令完成安裝處理。 這個命令會觸發指令碼修改原生程式碼,從而修改gradle
指令碼增加對外掛專案的引用,同時修改java
程式碼實現外掛的模版引入,這使得專案在一定程度被外掛“汙染”。
在 React Native
中帶有原生程式碼的外掛,會被以本地 Module
工程的方式引入,那 Flutter
呢?
其實原理上 Flutter
帶有原生程式碼的外掛,在外掛安裝後,也是會以本地 Module Project
的形式引入 ,但是它整個過程更加巧妙,讓開發中對這個過程幾乎無感。
如下圖所示,不知道你注意過沒有,在外掛安裝之後,所有帶原生程式碼的外掛,都會以路徑和外掛名的key=value
形式 存在 .flutter-plugins
檔案中。
而在 android
工程的 settings.gradle
裡,如下圖所示,會通過讀取該檔案將 .flutter-plugins
檔案中的專案一個個 include
到主工程裡。
之後就是主工程裡的 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
指令碼的引入了,這個指令碼一般在於 flutterSDK/packages/flutter_tools/gradle/
目錄下,如下程式碼所示,其中最關鍵的部分同樣是 讀取 .flutter-plugins
檔案中的專案,然後一個一個再 implementation
到主工程裡完成依賴。
自此所有原生程式碼的 Flutter
外掛,都被作為本地 Module Project
的形式引入主工程了 ,最後指令碼會自動生成一個 GeneratedPluginRegistrant.java
檔案,實現原生程式碼的引用註冊, 而這個過程對你完全是無感的。
說了那麼多就是為了說明,既然外掛是被當作本地 Module Project
的形式引入,那麼這時候按照原來直接打包 aar 是會有問題的:
`Android` 預設 `gradle` 指令碼打包時,對於 `project` 和遠端依賴只會打包引用而不會打包原始碼和資源。
所以這時候就需要 fat-aar
的加持了,關於 fat-aar
的詳細概念可見 :《從Android到React Native開發(四、打包流程解析和釋出為Maven庫)》 ,這裡可以簡單理解為,這是一個支援將引用程式碼和資源到合併到一個 aar 的外掛。
如下程式碼所示,我們在原本的元件化指令碼上,通過增加 apply plugin: 'com.kezong.fat-aar'
引入外掛,然後參考 Flutter
指令碼對 .flutter-plugins
檔案中的專案進行 embed
依賴引用即可 ,這時候再打包出的 aar
檔案即為完整 Flutter
專案程式碼。
完整版可見 flutter_app_lib 。
四、堆疊
最後需要說的問題就是堆疊了。
如果說混合開發中最難處理的是什麼,那一定是各平臺之間的堆疊管理,一般情況下我們都會避免混合堆疊的相互呼叫 ,但是面對不得不如此為之的情況下,閒魚給出了他們的答案:fluttet_boost
。
我們知道 Flutter
整個專案都是繪製在一個 Surface
畫布上,而fluttet_boost
將堆疊統一到了原生層,通過一個單例的 flutter engine
進行繪製。
每個 FlutterFragment
和 FlutterActivity
都是一個 Surface
承載容器,切換頁面時就是切換 Surface
渲染顯示,而對於不渲染的頁面通過 Surface
截圖快取畫面顯示。
這樣整個 Flutter
的路由就被對映到原生堆疊中,統一由原生頁面堆疊管理,Flutter
內每 push
一個頁面就是開啟一個 Activity
。
flutter_boost
截止到我測試的時間 2019-05-16, 只支援 1.2之前的版本。flutter_boost
的整體流程相對複雜,同時對於Dialog
的支援並不好,且業務跳轉深度太深時會出現黑屏問題。
自此,第十四篇終於結束了!(///▽///)
資源推薦
- Github : github.com/CarGuo
- 本文Demo :github.com/CarGuo/flut…
- 推薦完整程式碼 :github.com/CarGuo/GSYG…
完整開源專案推薦:
文章
《Flutter完整開發實戰詳解(一、Dart語言和Flutter基礎)》
《Flutter完整開發實戰詳解(四、Redux、主題、國際化)》
《Flutter完整開發實戰詳解(六、 深入Widget原理)》
《Flutter完整開發實戰詳解(十、 深入圖片載入流程)》
《Flutter完整開發實戰詳解(十一、全面深入理解Stream)》
《Flutter完整開發實戰詳解(十二、全面深入理解狀態管理設計)》