Flutter 開(kāi)發(fā) (2)優(yōu)雅的 Flutter 組件化 混編方案

背景

此篇文章,主要針對(duì)想要在原有Native工程的基礎(chǔ)上集成Flutter的需求,所提供的混編方案的探討。

Flutter 官方已經(jīng)給出了混編方案:https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps#ios 這種方案有 優(yōu)點(diǎn),也有 缺點(diǎn) 。

1. 官方方案的優(yōu)缺點(diǎn)

(1)優(yōu)點(diǎn):
  • 不需要每次 Run 起來(lái)之后,先進(jìn)行 同步flutter代碼(組件化Flutter后,因?yàn)榻M件化后flutter代碼已經(jīng)變?yōu)閒ramework,所以每次進(jìn)來(lái)需要先熱更新同步代碼)
  • 不需要單獨(dú)搞一個(gè)組件進(jìn)行集成,管理組件的版本,發(fā)布等。
(2)缺點(diǎn):
  • 會(huì)非常耦合工程,需要修改工程配置,添加 BUILD PHASE 調(diào)用 flutter 中 xcode_backend.sh 腳本 去編譯 Flutter。

  • 如果使用pod管理,那么還需修改xcconfig配置。

  • 因?yàn)樾枰{(diào)用 Flutter 的編譯腳本,所以這種方式集成后,團(tuán)隊(duì)內(nèi)所有組員電腦和打包機(jī),都必須安裝Flutter環(huán)境才能編譯成功。

2. Flutter 組件化混編方案

(1)優(yōu)點(diǎn):
  • 不需修改 原有 xcconfig 配置。
  • 不需要添加 Run Script 腳本。
  • 運(yùn)行不需要依賴(lài) Flutter 環(huán)境。
(2)缺點(diǎn)
  • 需要單獨(dú)管理一個(gè) flutter私有索引庫(kù)。
  • 開(kāi)發(fā)加載 Flutter 頁(yè)面 首次需要熱更新 進(jìn)行刷新同步 Flutter 代碼。
(3)混編方案 實(shí)現(xiàn)核心思想
  • 通過(guò)查看 Flutter 編譯腳本xcode_backend.sh 和 測(cè)試單獨(dú)引入編譯產(chǎn)物,發(fā)現(xiàn)其實(shí) 只要擁有 Flutter 的編譯產(chǎn)物,項(xiàng)目就可以接入 Flutter 的功能。
  • 所以說(shuō)只要把Flutter編譯好的產(chǎn)物,放在工程里,那么就無(wú)需配置每次調(diào)用 xcode_backend.sh 腳本,也無(wú)需強(qiáng)耦合Flutter環(huán)境,不需要所有組員安裝Flutter環(huán)境,只需要有發(fā)布開(kāi)發(fā)需求的同學(xué)進(jìn)行安裝即可。
  • 這就是Flutter組件化的實(shí)現(xiàn)核心點(diǎn)。
(4)Flutter 核心編譯產(chǎn)物
  • App.framework:dart業(yè)務(wù)源碼相關(guān)文件,在 Debug 模式下就是一個(gè)很小的空殼,在 Release 模式下包含全部業(yè)務(wù)邏輯。
  • flutter_assets:Flutter依賴(lài)的靜態(tài)資源,如字體,圖片等。
  • Flutter.framework:Flutter庫(kù)和引擎。

目錄

  • Flutter組件化 - 混編方案
  • Flutter組件化 - 斷點(diǎn)調(diào)試
  • Flutter組件化 - 發(fā)布更新
  • Flutter組件化 - 一些坑點(diǎn)

一、Flutter組件化 - 混編方案

1. Git倉(cāng)庫(kù)存放 - 示例說(shuō)明

主要分為3個(gè)倉(cāng)庫(kù),分別存放Native項(xiàng)目、Flutter 工程源碼、Flutter 編譯產(chǎn)物私有pod庫(kù)。
flutter 工程創(chuàng)建,使用 flutter create -t module my_flutter 命令

2. 項(xiàng)目目錄 - 示例
  • Flutter_iOS :iOS開(kāi)發(fā)主項(xiàng)目。

  • flutter_library :Flutter 項(xiàng)目的開(kāi)發(fā)源碼。

  • FlutterSDK :Flutter 源碼的編譯產(chǎn)物,所構(gòu)建的私有 pod 庫(kù)。

3. 混編方案說(shuō)明
  • 根據(jù) 只要擁有 Flutter 的編譯產(chǎn)物,項(xiàng)目就可以接入 Flutter 的功能 的核心思想,我們?nèi)绻M(jìn)行組件化Flutter混編,那么大概思路是:
有組件化環(huán)境 - 混編方案說(shuō)明

(1)在 flutter 項(xiàng)目目錄下,執(zhí)行 flutter build ios 針對(duì) Flutter 項(xiàng)目進(jìn)行編譯打包,生成 Flutter 編譯產(chǎn)物。

Flutter 的產(chǎn)物分為兩種模式,一個(gè)是 Debug 模式,采用 JIT(Just In Time)的方式,好處是可以支持熱更新,方便調(diào)試,,但是性能比較慢。
另一種是 AOT(Ahead Of Time)release 模式,好處是性能比較好。

通過(guò) flutter build ios --debug 可打包出 Debug 下的 Flutter 編譯產(chǎn)物。

flutter build ios 命令依賴(lài)于 Flutter 生成的 Runner 工程,所以要確保 Runner 工程能夠編譯成功,這樣才能生成 flutter 編譯產(chǎn)物。如果遇到編譯失敗,可以檢查下 bundle id 修改一下,使用自己的證書(shū)。如下圖示例:

(2)針對(duì)編譯產(chǎn)物,制作 Flutter SDK 私有庫(kù), podspec 指定 App.framework、engine、flutter_assets 路徑。

# podspec 有省略
Pod::Spec.new do |s|
  s.name         = "FlutterSDK"
  s.vendored_frameworks = 'Framework/*.framework', 'Framework/engine/*.framework'
  s.resources = 'Framework/flutter_assets'
end

(3)上傳 Flutter SDK 私有庫(kù)項(xiàng)目到云端私有pod索引庫(kù)。(如何制作私有 pod 索引庫(kù),可搜索查看相關(guān)資料,這里不細(xì)說(shuō)了)

(4)iOS 主項(xiàng)目指定 Podfile ,拉取云端Flutter私有庫(kù)到本地。

沒(méi)有組件化環(huán)境 - 混編方案說(shuō)明
  • 沒(méi)有組件化環(huán)境的項(xiàng)目,并且不會(huì)建立私有索引庫(kù)。
  • 那么只有手動(dòng) 執(zhí)行 flutter build ios 命令后,將編譯產(chǎn)物手動(dòng)拖拽到iOS項(xiàng)目中。
4. 最后效果

如下圖,可以看到最終工程只引用了一個(gè)私有 pod 庫(kù)。

總結(jié)

  • 對(duì) flutter 項(xiàng)目執(zhí)行 flutter build ios 命令,生成編譯產(chǎn)物。
  • 針對(duì)編譯產(chǎn)物,制作為私有 pod 庫(kù)。
  • 主工程通過(guò) cocoapods 引入私有 pod 庫(kù)。

二、Flutter 組件化 - 斷點(diǎn)調(diào)試

因?yàn)槭蔷幾g后的資源接入,我們還需要保證 Flutter 開(kāi)發(fā)的同學(xué)可以正常調(diào)試。

目錄

  • 單獨(dú)運(yùn)行 Flutter 工程調(diào)試
  • 同時(shí)調(diào)試 iOS 和 Flutter(不支持?jǐn)帱c(diǎn))
  • 同時(shí)調(diào)試 iOS 和 Flutter(支持?jǐn)帱c(diǎn))
注意點(diǎn):
  • 確保,已經(jīng)安裝 Android Studio(用于打開(kāi) Flutter 工程)
  • 確保,項(xiàng)目中依賴(lài)的 Flutter 打包出來(lái)的是 Debug 版本,Release 版本是無(wú)法熱更新和調(diào)試的(使用 flutter build ios --debug 打包 debug 版本)
1. 單獨(dú)運(yùn)行 Flutter 工程調(diào)試 (只適合和 Native 沒(méi)有太多關(guān)聯(lián)的工程,比較少用)

使用 Android Studio 打開(kāi) Flutter 工程目錄

選擇好真機(jī)或者模擬器,然后點(diǎn)擊 Run 按鈕

這樣 Run 起來(lái)后,我們就可以在 Flutter 項(xiàng)目中打斷點(diǎn)調(diào)試。這種方法 只適合和 Native 沒(méi)有關(guān)聯(lián)的工程,比較少用。

2. 同時(shí)調(diào)試 iOS 和 Flutter(不支持Flutter斷點(diǎn)的方式)

這種方法,需要先打開(kāi) Xcode 運(yùn)行到 Flutter 頁(yè)面,再進(jìn)行附加 Flutter 端口號(hào)。

  • 使用 Xcode 打開(kāi) iOS 項(xiàng)目,運(yùn)行起 Flutter 頁(yè)面。

  • 會(huì)發(fā)現(xiàn)會(huì)輸出一行日志,其中有一個(gè)端口號(hào)我們記錄下來(lái),例如:

flutter: Observatory listening on http://127.0.0.1:60455/
  • 然后 使用 Android Studio 打開(kāi) Flutter 項(xiàng)目(不點(diǎn)擊運(yùn)行),在 底部的 終端框中輸入 flutter attach --debug-port=60455,端口號(hào)替換為xcode 日志輸出的端口號(hào)。

Gif 演示

總結(jié)

  • 先打開(kāi) iOS 工程,運(yùn)行起 Flutter 頁(yè)面,得到一個(gè)日志輸出的端口號(hào)。
  • 然后 Android Studio 打開(kāi) Flutter 工程,在底部終端處輸入 flutter attach --debug-port=日志輸出端口號(hào),然后附加成功即可。
  • 注意點(diǎn):附加成功后,在終端處,按小寫(xiě) r 只會(huì)刷新有有修改的文件,按大寫(xiě)的 R 會(huì)全部刷新,如果使用組件化的話,每次進(jìn)入 Flutter 頁(yè)面,Android Studio 附加成功后,都要先按大寫(xiě) R 全部刷新一次 同步到最新代碼。否則還會(huì)顯示舊的頁(yè)面。
3. 同時(shí)調(diào)試 iOS 和 Flutter(同時(shí)支持Flutter 和 Xcode斷點(diǎn)的方式)

這種方法,需要先打開(kāi) Android Studio 選擇 Attach Debugger to Android Process 等待 Flutter 頁(yè)面連接,然后在 iOS 端,運(yùn)行到 Flutter 頁(yè)面,Android Studio 就會(huì)附加成功。

  • 首先打開(kāi) Flutter 工程,直接點(diǎn)擊 Attach Debugger to Android Process,然后會(huì)等待 Flutter 頁(yè)面連接。

  • 然后運(yùn)行 iOS 工程,進(jìn)入 Flutter 頁(yè)面。

  • 然后就會(huì)發(fā)現(xiàn) Android Studio 已經(jīng)顯示在 同步文件了(Syncing files to device 張大森的 iPhone...

  • 同步完成即連接成功。

注意坑點(diǎn)

  • 我們可以看到 閃電符號(hào) Flutter Hot Reload 和 返回綠色符號(hào) Flutter Hot Restart
  • Flutter Hot Reload 為局部刷新,比如某個(gè)文件有改動(dòng),才會(huì)同步刷新此頁(yè)面。Flutter Hot Restart可以理解為全部刷新,在 Android Studio 面板上也有對(duì)應(yīng)按鈕,相應(yīng)也有對(duì)應(yīng)快捷鍵。
  • 按照 Flutter 組件化的開(kāi)發(fā)方式,我們首次附加連接成功后,一定要遵循一個(gè)步驟,先 點(diǎn)擊 Flutter Hot Restart 進(jìn)行全部刷新,再點(diǎn)擊 Flutter Hot Reload 局部刷新。因?yàn)楸救税l(fā)現(xiàn),如果最后一次刷新點(diǎn)擊的是 Flutter Hot Restart 按鈕,那么發(fā)現(xiàn)斷點(diǎn)會(huì)不生效,只有點(diǎn)擊 Flutter Hot Reload 后 觸發(fā)的斷點(diǎn)才會(huì)生效。

演示 Gif

  • 打斷點(diǎn).gif

總結(jié)

  • Android Studio 打開(kāi) Flutter 工程 ,點(diǎn)擊 Attach Debugger to Android Process
  • 然后運(yùn)行 Flutter 頁(yè)面。
  • 然后點(diǎn)擊Flutter Hot Restart (同步最新 Flutter 代碼),在點(diǎn)擊 Flutter Hot Reload 確保斷點(diǎn)能夠生效。

三、Flutter組件化 - 發(fā)布更新

發(fā)布大概流程

(1)對(duì) Flutter 工程 執(zhí)行 flutter build ios 或 flutter build ios --debug 生成編譯產(chǎn)物。
(2)把編譯產(chǎn)物復(fù)制移動(dòng)到 Flutter 私有庫(kù)目錄下。
(3)打包 上傳更新私有庫(kù)內(nèi)容。
(4)主工程拉取最新版本。

版本更新說(shuō)明
  • 開(kāi)發(fā)期間:基本只用熱更新進(jìn)行開(kāi)發(fā)代碼。
  • 發(fā)布版本:一般可在上線前進(jìn)行發(fā)布,所以組件版本更新,用的比較少。

四、一些坑點(diǎn)

(1)FlutterViewController 不釋放
  • 加載 Flutter 頁(yè)面后,返回后,VC 不會(huì)釋放。閑魚(yú)有大神研究過(guò)這個(gè)問(wèn)題,不過(guò)目前我們沒(méi)有找到解決方案去釋放 VC。
  • 我們使用單例持有了 VC,只能做到不每次進(jìn)行疊加內(nèi)存,不重新創(chuàng)建。每次進(jìn)入 Flutter 頁(yè)面都先重置一下。
  • 參考文章:http://www.itdecent.cn/p/9ff7a9a5dfec
(1)不支持x86_64
  • 可以用xcode跑下,生成App.framewok,然后 lipo 命令合并下。
  • 我們目前不支持模擬器,這種方案沒(méi)有進(jìn)行測(cè)試。

其它

  • 如果有更好的調(diào)試方法,坑點(diǎn)解決方法,混編方法,歡迎交流反饋下。
下一篇文章 (主要涉及到 Flutter 開(kāi)發(fā)的一些知識(shí))
  • 教你 Flutter 如何與原生進(jìn)行交互 待更新

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容