作者:閒魚技術-正物
本篇是此係列的第二篇,重點介紹如何讓Flutter能夠開發,實現業務需求。這部分包括:混合棧的管理,混合下的能力補齊和包管理。
混合棧的管理
引入Flutter之後,我們首要面臨的問題便是混合棧的管理。如我們首先實踐的是商品詳情頁,則有一個常見的應用場景:首頁->詳情頁->詳情頁(猜你喜歡點選)->會話頁->詳情頁(會話頂部點選)。如何去解決這種Native 與Flutter任意巢狀的問題呢?
解決方案應具有以下特點:
a.每一個頁面都有一個VC(Activity),保證所有基於VC(Activity)生命週期的邏輯(如埋點等)照常工作
b.不同的Flutter頁面之間可以正常通訊,共享資料
c.Native可以調起任意的Flutter頁面,無論是首次開啟還是之後
d.資源佔用儘可能少,效能儘可能好
e.使用者體驗要同之前無差異
為了解決這些問題,設計如下:
a.所有的Flutter頁面公用一個Flutter例項(FlutterViewController for iOS&FlutterNativeView for Android)
其中iOS可以透過addChildViewController和removeFromParentViewController,直接在FlutterViewController層面複用。

Android由於activity不能add/remove,因此抽取並複用了FlutterNativeView(相對應的FlutterView也是單例),在不同的Activity上覆用。

這種Flutter單例的實現帶來的好處顯而易見。一方面,單個Flutter例項因為位於相同的Isolate,其資料通訊和共享將很容易。另一方面,引擎的預設實現中,每一個Flutter例項會新啟動三個執行緒(IO,GPU和UI),帶來了額外的資源使用,單例項可以避免這個問題。
當然單例的實現也帶來了額外的管理複雜度,但這個相關的複雜度我已經通過外掛的方式支援了。
b.每一個Flutter頁面不僅是一個FlutterPage,也有一個對應的NativeVC(或Activity)與之相對應
通過唯一的id將其關聯,可以方便地使Flutter/Native頁面保持同步。如Native頁面退到Idx時,可以通過Flutter內部的棧回退到對應idx的Flutter頁面。
c.Native和Flutter都基於openUrl來管理頁面跳轉
統一的路由機制可以統一管理所有頁面跳轉相關邏輯,對業務側提供標準一致的服務。
d.所有的頁面跳轉和動畫都通過Native實現
因為原生(iOS/Android)自帶跳轉動畫,而Flutter內部跳轉也自帶動畫,為了使用者體驗,禁止Flutter內部跳轉(push/pop)相關動畫,統一由Native接管,這樣使用者就感受不到差異。
e.使用一個空白Widget作為RootWidget
以便保證Flutter頁面完整退出時(如回到首頁),可以釋放所有的Flutter詳情頁等資源
架構圖如下:

效果圖如下


##待優化部分 1.目前為了保證頁面跳轉時的正常顯示,前一個頁面是截圖,後一個頁面是真正的Flutter頁面,也就是說每一個FlutterWrapperVC(Activity)都保留了一張截圖,帶來了額外的記憶體消耗。
複用Native元件
當前閒魚Flutter業務全頁面採用Flutter實現,但也有一些Native元件的複用。
通過Channel呼叫部分中介軟體
如埋點,網路請求與解析等只能呼叫已有的二方中介軟體,這種情況下基於Platform Channels去呼叫。

使用Dart重寫部分中介軟體
對於一些有標準協議支援(如http)的中介軟體,如阿里雲提供的基於http的檔案上傳服務,可以通過dart重寫的方式去實現。
通過新Window方式,蓋上已有的Native元件
如下圖所示的浮層和鍵盤上的輸入框,均為直接複用Native元件。

視訊的實現
Flutter引擎通過Texture的方式,由Native側提供相應的渲染內容(解碼視訊檔案獲得幀Buffer等)的方式來提供對於視訊的支援。
Flutter的渲染流程略圖

iOS上Flutter視訊渲染邏輯:

Android上Flutter視訊渲染邏輯:

包管理
構建和釋出外掛(包)
1.使用下面命令建立外掛:
flutter create --template=plugin hybrid_stack_manager
複製程式碼
2.開發外掛並在example中測試通過 3.完善README.md, CHANGELOG.md,pubspec.yaml,podspec.json,LICENSE等相關檔案 4.使用命令驗證併發布
cd root-path-to-your-flutter-package
pub publish
複製程式碼
如果出現一個連線需要驗證身份,拷貝到瀏覽器訪問即可。
我們可以將實現的外掛(如網路請求,混合棧管理等)包,Dart包(阿里雲上傳元件等),以多種形式引入。下面以上文中的混合棧管理外掛hybrid_stack_manager為例說明下各種包管理方式:
包名+版本
實現後可釋出到https://pub.dartlang.org,再在pubspec.yaml中基於名稱+版本號引入。

此部分包下載後路徑位於:~/.pub-cache/hosted/pub.dartlang.org/hybrid_stack_manager-0.0.2
包名+git資訊
也可以將程式碼包上傳到可訪問到的git地址,通過git地址+ref的方式引入。

此部分包下載後路徑位於:~/.pub-cache/git/hybrid_stack_manager-d9f6bdf169683fd8642168ec37c5820940ed344a
包名+路徑
也可以將程式碼放在某一資料夾(建議當前Flutter工程根目錄之下),通過路徑引入。

寫在後面
本文著重介紹了混合場景下如何去做業務研發。解決這一問題後,接下來就要解決實際業務開發中遇到的各種相容性/穩定性問題,這些敬請關注本系列的上線篇。
聯絡我們
如果對文字的內容有疑問或指正,歡迎告知我們。
閒魚技術團隊是一隻短小精悍的工程技術團隊。我們不僅關注於業務問題的有效解決,同時我們在推動打破技術棧分工限制(android/iOS/Html5/Server 程式設計模型和語言的統一)、計算機視覺技術在移動終端上的前沿實踐工作。作為閒魚技術團隊的軟體工程師,您有機會去展示您所有的才能和勇氣,在整個產品的演進和使用者問題解決中證明技術發展是改變生活方式的動力。
簡歷投遞:guicai.gxy@alibaba-inc.com