天天看點

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

作者:閑魚技術-正物

寫在前面

目前,閑魚用戶端已經實作了基于Flutter的商品詳情頁的全量重構,線上效果良好。從alpha一路走來,我們遇到了很多問題,或基于原理,或透過社群,或與官方合作,都一個個解決了,是時候梳理和總結下,也希望為其他的開發者們,尤其是已有工程中引入Flutter(混合場景)實作漸進式重構帶來啟發和幫助。

鑒于存在多個問題一個原因或解法的情況,而本系列的重點在于說明各種問題的解決方案與思路,就不一一列出問題。所有調試/熱重載相關的Flutter均為Debug模式的Flutter,不再特殊說明。

本系列文章包含三篇:引入篇,運作篇,上線篇。引入篇重點介紹工程研發體系;運作篇介紹混合情景下的棧管理與能力補齊等;上線篇介紹相容/穩定性保障及方法。

工程研發體系的關鍵點包括:

a.混合工程下的Flutter研發結構

混合工程中一個全局視角的的研發結構如何。

b.工程結構

已有的Native工程如何引入Flutter,工程結構如何組織,如何管理Flutter環境,如何去編譯建構,內建打包等。

c.建構優化

這裡主要介紹如何去針對Flutter的工具鍊(flutter_tools,Intellij插件等)進行調試與優化。

d.Native啟動下的Flutter調試

不同于Flutter啟動下的一體化調試,這種Native啟動(Xcode/Android Studio啟動,或點選圖示打開應用)下的Flutter調試,我稱之為分離式調試。分離式調試可以簡化flutter_tools帶來的複雜度,提高調試的穩定性和靈活性。

e.Native啟動下的Flutter熱重載

同d。

f.聯合調試

即同時調試Flutter和Android/iOS。

g.持續內建

即混合環境下的Flutter建構與持續內建。

環境說明

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

混合工程下的Flutter研發結構

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

工程結構

這部分的核心邏輯是如何在最小改動已有iOS/Android工程的前提下運作Flutter。我們可以将Flutter部分了解成為一個單獨的子產品,通過pod庫(iOS),aar庫(Android)的方式,由CocoaPods和Gradle引入到主工程。

具體的原理與實踐請參見:

深入了解flutter的編譯原理與優化 Flutter混合工程改造實踐 Add Flutter to existing apps

其中,我們将整套Flutter環境作為Git Submodule統一管理,以保證團隊内環境一緻,遇到的個性化的問題/需求能夠統一處理。

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

建構優化篇

編譯速度的優化(Android)

問題:Android在由Flutter啟動時建構緩慢。

原因:在flutter工具鍊(flutter_tools)的邏輯中,未找到android/app/build.gradle時,會運作gradle build進而執行多個編譯配置的建構,而不是gradle assembleDebug。

解法:重構Android工程,使工程應用Module對應的build.gradle位于android/app下,進而符合flutter_tools的邏輯。

原理:flutter_tools的調試

a.修改flutter_tools.dart,使之可列印參數

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

b.删除flutter/bin/cache/flutter_tools.stamp使得flutter_tools可以被重建

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

c.從flutter運作建構,擷取其入口參數

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

d.用Intellij(或Android Studio下同)打開flutter_tools工程,建立Dart Command Line App,并基于步驟c獲得的入參配置"Program arguments"

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

e.開始你的flutter_tools調試之旅吧‘

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

編譯速度的優化(iOS)

問題:Flutter建構報"Observatory connection never became ready.",造成建構中斷

原因:重構前我們的工程全量編譯時間較長(1000+檔案全量編譯時長>10min),而Flutter Intellij插件有個逾時邏輯,使得建構中斷。

解法a(下策):定制Flutter Intellij插件(修改下面代碼中的逾時時間),編譯插件,并替換Android Studio中的Flutter插件。更合理的解法是提PR,但這一路基本上都是在馬不停蹄地解決各種産品化中的問題,是以...(最新版本已去除此邏輯)

原理:

前往檢視Flutter Intellij源碼
Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

事實上,我們使用IDE開發Flutter時,有下面的一個邏輯流程:

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

解法b(中策):iOS工程的子產品拆分和Pod(Framework)化,主工程建構依賴編譯好的Framework,大大加快了建構時間。

原理:子產品化+預編譯Framework

解法c(上策):Native視角下的Flutter調試

原理:Native啟動下,Flutter的調試與熱重載

Native視角下的Flutter調試

Flutter啟動下的Flutter的調試與熱重載邏輯

實際上,當Native工程配置好Flutter支援後,Flutter啟動下做的事情主要有:

a.檢查是否需要重新生成flutter_tools.snapshot。

b.基于pubspec.yaml擷取依賴(pub packages get),并生成插件描述檔案.flutter-plugins和pubspec.lock。

c.基于Flutter配置(如Framework路徑,Debug/Release模式,是否開啟Dart2等),生成Generated.xcconfig(iOS)和local.properties(Android)。

d.基于gradle和xcodebuild建構應用(Flutter相關建構請參見前文中深入了解flutter的編譯原理與優化)。

e.基于adb和lldb啟動應用。

f.等待應用中Flutter啟動,尋找Observatory端口,通過Dart Debugger連接配接以便調試。

g.尋找到端口後同步Hot Reload依賴的檔案,同時透過Daemon監聽指令(如使用者點選插件按鈕)實作Full Restart或Hot Reload。

換個角度來看,如果我們能夠解決Native啟動下的Dart調試和Hot Reload,由flutter_tools造成的編譯慢等問題将不是問題,且可解決調試環境不穩定的情況(如我們的場景下,應用啟動後,僅當使用者點選進入詳情頁面的時候才會啟動Flutter,此時flutter_tools才能去發現Observatory端口,調試和熱重載,常有不好用的情況)。當從Xcode啟動(或點選桌面圖示啟動,不再重複)包含了Debug模式Flutter内容的iOS(Android Studio啟動Android類似,這裡不再重複)應用時,我們需要關注abcfg。而abc除非flutter_tools或pubspec.yaml或Flutter配置變化等,否則都不需要重新執行。fg則是研發依賴的調試與熱重載,必須考慮此模式下如何支援。

Native啟動下的Flutter的調試與熱重載邏輯

a.尋找iOS裝置上Observatory端口

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

或者指令行通過idevicesyslog擷取,此處涉及到libimobiledevice庫,其包含了idevicesyslog,iproxy等指令。

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

可以看到iOS裝置上Observatory啟動了一個xxxx的端口(端口号随機)。

b.透過iproxy将iOS裝置上端口xxxx映射到本機端口yyyy

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

c.可以看到waiting for connection,此時就可以通路

http://127.0.0.1:yyyy/#/vm

打開Observatory如下:

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

可以使用Observatory去檢查諸多dart相關的記憶體,調試等,這裡不展開。

也可以通過IDE連結去調試:

d.配置Dart Remote Debug

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

這裡需要注意的是端口要使用剛轉發到電腦的端口yyyy,搜尋源碼路徑是Flutter工程的根目錄。

e.配置好之後點選Debug按鈕,連接配接到調試端口

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

f.成功後可以看到Debugger顯示Connected(如果沒有顯示,再點選一次綠色的調試按鈕‍️)

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

g.之後便可以正常地使用IDE設定斷點和調試dart(Flutter)代碼

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

Native視角下的Flutter熱重載

a.啟動App,進入Flutter頁面,查找Observatory端口xxxx,并轉發到電腦yyyy(同上面ab)

b.在Flutter工程目錄下,執行flutter attach --debug-port=yyyy

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

c.修改dart源代碼,然後在b中Terminal中輸入r(這一輸入位于上圖中'To quit,press"q"'之後)

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

這裡我們将超贊文案換成了贊。

d.可以看到Terminal顯示"Initializing hot reload...Reloaded...",結束後,裝置上變更生效(左下角文案變成了贊)

Flutter新銳專家之路:工程研發體系篇寫在前面環境說明混合工程下的Flutter研發結構工程結構建構優化篇Native視角下的Flutter調試Native視角下的Flutter熱重載Native與Flutter聯調持續內建

Android下,Native啟動的的Flutter調試/熱重載類似iOS,不同的是擷取端口時可通過IDE logcat或者adb logcat | grep Observatory,端口轉發使用adb forward。

Native與Flutter聯調

上文中已經介紹了如何在任意時刻(Flutter啟動後)調試Flutter。此外我們還可以使用Android Studio的Attach Debugger to Android Process來調試Android,這就實作了Android與Flutter聯調。同樣,結合Xcode的Attach to Process,可以實作iOS與Flutter聯調。

持續內建

目前團隊包括Native同學和Flutter同學,是以我們區分了Flutter模式和Native模式。有一台公共裝置(Mac Mini)安裝了Flutter環境并負責Flutter相關的建構,建構好的産物以aar(Android)或pod庫(iOS)的形式內建到Native工程下(可以認為Flutter相關的代碼就是一個子產品),用于建構最終産物apk(Android)或ipa(iOS)的CI平台最終也通過産物方式內建Flutter并打包。

更多細節請參見:

閑魚flutter混合工程持續內建的最佳實踐

寫在後面

本文着重介紹了混合場景下的工程研發體系。解決這一問題後,接下來就要解決實際業務開發中遇到的問題。比如Native與Flutter互相跳轉場景下的棧如何管理,Flutter不能實作的功能(平台特性等)如何去補全,Flutter Plugin/Dart Package包管理的方式有哪些等,這些敬請關注本系列的運作篇。

聯系我們

如果對文本的内容有疑問或指正,歡迎告知我們。

閑魚技術團隊是一隻短小精悍的工程技術團隊。我們不僅關注于業務問題的有效解決,同時我們在推動打破技術棧分工限制(android/iOS/Html5/Server 程式設計模型和語言的統一)、計算機視覺技術在移動終端上的前沿實踐工作。作為閑魚技術團隊的軟體工程師,您有機會去展示您所有的才能和勇氣,在整個産品的演進和使用者問題解決中證明技術發展是改變生活方式的動力。

履歷投遞:[email protected]

繼續閱讀