如何用 Flutter 實現混合開發?閑魚公開源代碼實例

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

3f1ab90484880bb596ece6899c0384fe58e.jpg

具有一定規模的 App 通常有一套成熟通用的基礎庫,尤其是阿里系 App,一般需要依賴很多體系內的基礎庫。那么使用 Flutter 重新從頭開發 App 的成本和風險都較高。所以在 Native App 進行漸進式遷移是 Flutter 技術在現有 Native App 進行應用的穩健型方式。

今天我們來看看,閑魚團隊如何在這個實踐過程中沉淀出一套獨具特色的混合技術方案。

現狀及思考

閑魚目前采用的混合方案是共享同一個引擎的方案。這個方案基于這樣一個事實:任何時候我們最多只能看到一個頁面,當然有些特定的場景你可以看到多個 ViewController ,但是這些特殊場景我們這里不討論。

我們可以這樣簡單去理解這個方案:我們把共享的 Flutter View 當成一個畫布,然后用一個 Native 的容器作為邏輯的頁面。每次在打開一個容器的時候我們通過通信機制通知 Flutter View 繪制成當前的邏輯頁面,然后將 Flutter View 放到當前容器里面。

這個方案無法支持同時存在多個平級邏輯頁面的情況,因為你在頁面切換的時候必須從棧頂去操作,無法再保持狀態的同時進行平級切換。舉個例子:有兩個頁面A,B,當前B在棧頂。切換到A需要把B從棧頂 Pop 出去,此時B的狀態丟失,如果想切回B,我們只能重新打開B之前頁面的狀態無法維持住。

如在 pop 的過程當中,可能會把 Flutter 官方的 Dialog 進行誤殺。而且基于棧的操作我們依賴對 Flutter 框架的一個屬性修改,這讓這個方案具有了侵入性的特點。

32d917f03ebc6297c88b2b3337e0f1b9f8c.jpg

新一代混合技術方案 FlutterBoost

重構計劃

在閑魚推進 Flutter 化過程當中,更加復雜的頁面場景逐漸暴露了老方案的局限性和一些問題。所以我們啟動了代號 FlutterBoost(向C++ Boost庫致敬)的新混合技術方案。這次新的混合方案我們的主要目標有:

  • 可復用通用型混合方案
  • 支持更加復雜的混合模式,比如支持主頁Tab這種情況
  • 無侵入性方案:不再依賴修改 Flutter 的方案
  • 支持通用頁面生命周期
  • 統一明確的設計概念

跟老方案類似,新的方案還是采用共享引擎的模式實現。主要思路是由 Native 容器 Container 通過消息驅動 Flutter 頁面容器 Container,從而達到 Native Container與 Flutter Container 的同步目的。我們希望做到 Flutter 渲染的內容是由 Naitve 容器去驅動的。

簡單的理解,我們想做到把 Flutter 容器做成瀏覽器的感覺。填寫一個頁面地址,然后由容器去管理頁面的繪制。在 Native 側我們只需要關心如果初始化容器,然后設置容器對應的頁面標志即可。

主要概念

447ad0492826336fbe031b6ca056d63fef9.jpg

Native 層概念

  • Container:Native 容器,平臺 Controller,Activity,ViewController
  • Container Manager:容器的管理者
  • Adaptor:Flutter 是適配層
  • Messaging:基于 Channel 的消息通信

Dart 層概念

  • Container:Flutter 用來容納 Widget 的容器,具體實現為 Navigator 的派生類
  • Container Manager:Flutter 容器的管理,提供 show,remove 等 Api
  • Coordinator: 協調器,接受 Messaging 消息,負責調用 Container Manager 的狀態管理。
  • Messaging:基于 Channel 的消息通信

關于頁面的理解

在 Native 和 Flutter 表示頁面的對象和概念是不一致的。在 Native,我們對于頁面的概念一般是 ViewController,Activity 。而對于 Flutter 我們對于頁面的概念是 Widget 。我們希望可統一頁面的概念,或者說弱化抽象掉 Flutter 本身的 Widget 對應的頁面概念。換句話說,當一個 Native 的頁面容器存在的時候, FlutteBoost 保證一定會有一個 Widget 作為容器的內容。所以我們在理解和進行路由操作的時候都應該以 Native 的容器為準, Flutter Widget 依賴于 Native 頁面容器的狀態。

那么在 FlutterBoost 的概念里說到頁面的時候,我們指的是 Native 容器和它所附屬的 Widget 。所有頁面路由操作,打開或者關閉頁面,實際上都是對 Native 頁面容器的直接操作。無論路由請求來自何方,最終都會轉發給 Native 去實現路由操作。這也是接入 FlutterBoost 的時候需要實現 Platform 協議的原因。

另一方面,我們無法控制業務代碼通過 Flutter 本身的 Navigator 去 push 新的 Widget 。對于業務不通過 FlutterBoost 而直接使用 Navigator 操作 Widget 的情況,包括 Dialog 這種非全屏 Widget,我們建議是業務自己負責管理其狀態。這種類型 Widget 不屬于 FlutterBoost 所定義的頁面概念。

理解這里的頁面概念,對于理解和使用 FlutterBoost 至關重要。

與老方案主要差別

前面我們提到老方案在 Dart 層維護單個 Navigator 棧結構用于 Widget 的切換。而新的方案則是在 Dart 側引入了 Container 的概念,不再用棧的結構去維護現有的頁面,而是通過扁平化 key-value 映射的形式去維護當前所有的頁面,每個頁面擁有一個唯一的 id 。這種結構很自然的支持了頁面的查找和切換,不再受制于棧頂操作的問題,之前的一些由于 pop 導致的問題迎刃而解。也不需要依賴修改 Flutter 源碼的形式去進行頁面棧操作,去掉了實現的侵入性。

實際上我們引入的 Container 就是 Navigator 的,也就是說一個 Native 的容器對應了一個 Navigator 。那這是如何做到的呢?

多 Navigator 的實現

Flutter 在底層提供了讓你自定義 Navigator 的接口,我們自己實現了一個管理多個 Navigator 的對象。當前最多只會有一個可見的 Flutter Navigator ,這個Navigator 所包含的頁面也就是我們當前可見容器所對應的頁面。

Native 容器與 Flutter 容器( Navigator )是一一對應的,生命周期也是同步的。當一個 Native 容器被創建的時候, Flutter 的一個容器也被創建,它們通過相同的 id 關聯起來。當 Native 的容器被銷毀的時候, Flutter 的容器也被銷毀。 Flutter 容器的狀態是跟隨 Native 容器,這也就是我們說的 Native 驅動。由 Manager 統一管理切換當前在屏幕上展示的容器。

我們用一個簡單的例子描述一個新頁面創建的過程:

  1. 創建 Native 容器( iOS ViewController,Android Activity or Fragment )。
  2. Native 容器通過消息機制通知 Flutter Coordinator 新的容器被創建。
  3. Flutter Container Manager 進而得到通知,負責創建出對應的 Flutter 容器,并且在其中裝載對應的 Widget 頁面。
  4. 當 Native 容器展示到屏幕上時,容器發消息給 Flutter Coordinator 通知要展示頁面的 id 。
  5. Flutter Container Manager 找到對應 id 的 Flutter Container 并將其設置為前臺可見容器。

這就是一個新頁面創建的主要邏輯,銷毀和進入后臺等操作也類似有 Native 容器事件去進行驅動。

官方提出的混合方案

基本原理

Flutter 技術鏈主要由 C++實現的 Flutter Engine 和 Dart 實現的 Framework 組成(其配套的編譯和構建工具我們這里不參與討論)。Flutter Engine 負責線程管理,Dart VM 狀態管理和 Dart 代碼加載等工作。而 Dart 代碼所實現的 Framework 則是業務接觸到的主要 API,諸如 Widget 等概念就是在 Dart 層面 Framework 內容。

一個進程里面最多只會初始化一個 Dart VM。然而一個進程可以有多個 Flutter Engine,多個 Engine 實例共享同一個 Dart VM。

我們來看具體實現,在 iOS 上面每初始化一個 FlutterViewController 就會有一個引擎隨之初始化,也就意味著會有新的線程(理論上線程可以復用)去跑 Dart 代碼。Android 類似的 Activity 也會有類似的效果。如果你啟動多個引擎實例,注意此時Dart VM 依然是共享的,只是不同 Engine 實例加載的代碼跑在各自獨立的 Isolate。

官方建議

引擎深度共享

在混合方案方面,我們跟 Google 討論了可能的一些方案。Flutter 官方給出的建議是從長期來看,我們應該支持在同一個引擎支持多窗口繪制的能力,至少在邏輯上做到 FlutterViewController 是共享同一個引擎的資源的。換句話說,我們希望所有繪制窗口共享同一個主 Isolate。

但官方給出的長期建議目前來說沒有很好的支持。

多引擎模式

我們在混合方案中解決的主要問題是如何去處理交替出現的 Flutter 和 Native 頁面。Google 工程師給出了一個 Keep It Simple 的方案:對于連續的 Flutter 頁面(Widget)只需要在當前 FlutterViewController 打開即可,對于間隔的 Flutter 頁面我們初始化新的引擎。

例如,我們進行下面一組導航操作:

我們只需要在 Flutter Page1 和 Flutter Page3 創建不同的 Flutter 實例即可。

這個方案的好處就是簡單易懂,邏輯清晰,但是也有潛在的問題。如果一個 Native頁面一個 Flutter 頁面一直交替進行的話,Flutter Engine 的數量會線性增加,而Flutter Engine 本身是一個比較重的對象。

多引擎模式的問題

  • 冗余的資源問題:多引擎模式下每個引擎之間的 Isolate 是相互獨立的。在邏輯上這并沒有什么壞處,但是引擎底層其實是維護了圖片緩存等比較消耗內存的對象。想象一下,每個引擎都維護自己一份圖片緩存,內存壓力將會非常大。
  • 插件注冊的問題:插件依賴 Messenger 去傳遞消息,而目前 Messenger 是由 FlutterViewController(Activity) 去實現的。如果你有多個 FlutterViewController ,插件的注冊和通信將會變得混亂難以維護,消息的傳遞的源頭和目標也變得不可控。
  • Flutter Widget 和 Native 的頁面差異化問題: Flutter 的頁面是 Widget, Native 的頁面是 VC 。邏輯上來說我們希望消除 Flutter 頁面與 Naitve 頁面的差異,否則在進行頁面埋點和其它一些統一操作的時候都會遇到額外的復雜度。
  • 增加頁面之間通信的復雜度:如果所有 Dart 代碼都運行在同一個引擎實例,它們共享一個 Isolate ,可以用統一的編程框架進行 Widget 之間的通信,多引擎實例也讓這件事情更加復雜。

因此,綜合多方面考慮,我們沒有采用多引擎混合方案。

總結

目前 FlutterBoost 已經在生產環境支撐著在閑魚客戶端中所有的基于 Flutter 開發業務,為更加負復雜的混合場景提供了支持,穩定為億級用戶提供服務。

我們在項目啟動之初就希望 FlutterBoost 能夠解決 Native App 混合模式接入 Flutter 這個通用問題。所以我們把它做成了一個可復用的 Flutter 插件,希望吸引更多感興趣的朋友參與到 Flutter 社區的建設。在有限篇幅中,我們分享了閑魚在 Flutter 混合技術方案中積累的經驗和代碼。歡迎興趣的同學能夠積極與我們一起交流學習。

擴展補充

性能相關

在兩個 Flutter 頁面進行切換的時候,因為我們只有一個 Flutter View 所以需要對上一個頁面進行截圖保存,如果 Flutter 頁面多截圖會占用大量內存。這里我們采用文件內存二級緩存策略,在內存中最多只保存2-3個截圖,其余的寫入文件按需加載。這樣我們可以在保證用戶體驗的同時在內存方面也保持一個較為穩定的水平。

頁面渲染性能方面, Flutter 的 AOT 優勢展露無遺。在頁面快速切換的時候, Flutter 能夠很靈敏的相應頁面的切換,在邏輯上創造出一種 Flutter 多個頁面的感覺。

Release1.0的支持

項目開始的時候我們基于閑魚目前使用的 Flutter 版本進行開發,而后進行了 Release 1.0 兼容升級測試目前沒有發現問題。

接入

只要是集成了 Flutter 的項目都可以用官方依賴的方式非常方便的以插件形式引入 FlutterBoost ,只需要對工程進行少量代碼接入即可完成接入。 詳細接入文檔,請參閱GitHub主頁官方項目文檔。


原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。

轉載于:https://my.oschina.net/u/1464083/blog/3050245

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/279520.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/279520.shtml
英文地址,請注明出處:http://en.pswp.cn/news/279520.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Silverlight之工具箱使用1

我們在開發Silverlight項目時必定需要使用VS自帶的一些控件,但是這些有限的控件有時候難以滿足開發時的需求,因此MS給我們大家提供另外一套工具,來緩解Silverlight開發包的不足。此工具箱免費下載地址是:http://silverlight.codep…

apple tv設置_如何設置Apple HomePod

apple tv設置Apple’s HomePod smart speaker is finally here. If you bought one and are eager to get going, here’s how to set it up. 蘋果的HomePod智能揚聲器終于來了。 如果您購買了一個并且渴望上手,請按照以下步驟進行設置。 First off, before you eve…

leetcode 128最長連續序列

方法一&#xff1a;使用快排&#xff1a; //排序法&#xff0c;時間O(nlogn)&#xff0c;使用STL&#xff0c;只是驗證一下思想&#xff0c;非正解&#xff1b; class Solution { public:int longestConsecutive(vector<int>& nums) {sort(nums.begin(),nums.end());…

8月19學習練習[兩三個TableView并排顯示]

要求&#xff1a;在一個view中顯示兩個tableView&#xff0c;要求左右顯示的內容以及行數不一樣&#xff0c;且左邊每行顯示兩張圖片&#xff08;分別3個一輪回&#xff0c;2個一輪回&#xff09;并且顯示中國的城市名&#xff0c;右邊顯示水果名。點擊時分別顯示城市名或水果名…

word多級列表創建目錄_如何在Microsoft Word中創建和使用多級列表

word多級列表創建目錄Microsoft Word lets you easily create and format multilevel lists in your documents. You can choose from a variety of formatting options, including bulleted, numbered, or alphabetized lists. Let’s take a look. Microsoft Word使您可以輕松…

如何將多個Android Wear手表與單個手機配對

When it comes to “regular” wristwatches, a lot of people have different watches for different activities. It makes sense—a sporty watch for the gym, a nicer watch for the office, and a casual watch for everything else. If you want to live this life with…

Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析(3)...

提供引用計數器的類RefBase我們就暫時介紹到這里&#xff0c;后面我們再結合智能指針類一起分析&#xff0c;現在先來看看強指針類和弱指針類的定義。強指針類的定義我們在前面介紹輕量級指針的時候已經見到了&#xff0c;就是sp類了&#xff0c;這里就不再把它的代碼列出來了。…

ref:下一個項目為什么要用 SLF4J

ref:http://blog.mayongfa.cn/267.html 阿里巴巴 Java 開發手冊 前幾天阿里巴巴在云棲社區首次公開阿里官方Java代碼規范標準&#xff0c;就是一個PDF手冊&#xff0c;有命名規范&#xff0c;讓你知道自己原來取的每一個類名、變量名都是爛名字&#xff0c;真替你家未來孩子擔心…

洛谷P5055 【模板】可持久化文藝平衡樹(FHQ Treap)

題面 傳送門 題解 日常敲板子.jpg //minamoto #include<bits/stdc.h> #define R register #define inline __inline__ __attribute__((always_inline)) #define fp(i,a,b) for(R int i(a),I(b)1;i<I;i) #define fd(i,a,b) for(R int i(a),I(b)-1;i>I;--i) #define …

計算機突然藍屏無法啟動_為什么計算機無法立即啟動?

計算機突然藍屏無法啟動With the newer, more powerful hardware and improved operating systems that we have available to use these days, why does it still take as long as it does to fully boot a computer up each time? 借助我們如今可以使用的更新&#xff0c;更…

CCNA課堂練習:OSPF的介紹及配置

CCNA淺談OSPF的配置 今天我們來談談路由器OSPF的配置&#xff0c;那我先來介紹一下OSPF的特點&#xff1a;1、對網絡發生的變化能夠快速響應2、當網絡發生變化的時候發送觸發式更新?3、支持VLAN 4、管理方便ospf引用了區域的概念&#xff0c;區域分兩種&#xff1a;1、骨干區域…

vcenter 6.7 (vcsa)部署指南

閑言少敘&#xff0c;直達心靈。 一、部署提要1.1 vCenter Server Appliance(VCSA )6.7下載地址https://pan.baidu.com/s/1WUShsC23E2qIIBg7MPR87w 6lzb 二、安裝部署VCSA分為兩個階段安裝&#xff0c;下面我們開始第一階段2.1 打開之后&#xff0c;直接點擊安裝按鈕2.2部署設備…

如何停止Internet Explorer 11的建議站點?

Internet Explorer automatically suggests addresses and search results based on the partial address you’re typing out. If this feature irritates you, read on as we learn how to turn it off. Internet Explorer會根據您鍵入的部分地址自動建議地址和搜索結果。 如…

什么是SG?+SG模板

先&#xff0c;定義一下 狀態Position P 先手必敗 N x先手必勝 操作方法&#xff1a; 反向轉移 相同狀態 不同位置 的一對 相當于無 對于ICG游戲&#xff0c;我們可以將游戲中每一個可能發生的局面表示為一個點。并且若存在局面i和局面j&#xff0c;且j是i的后繼局面(即局面i可…

【桌面虛擬化】之三 Persistent vs NonP

作者&#xff1a;范軍 &#xff08;Frank Fan&#xff09; 新浪微博&#xff1a;frankfan7 在【桌面虛擬化】之二類型及案例中我們探討了桌面虛擬化的兩種架構&#xff0c;HostedVirtual Desktop (VDI) 和 Published Desktop/App. 本文深入分析其中VDI的兩種桌面類型&#xff0…

H5 video 開發問題及相關知識點

相關鏈接&#xff1a;H5 video 的使用H5 video 全屏播放? video點播與直播H5 video目前所有瀏覽器都支持的視頻格式是MP4格式&#xff0c;所以mp4應當是點播web視頻的首選格式。而在直播視頻上&#xff0c;H5 video只在移動端原生支持HLS流的直播視頻(Mac safari video標簽也支…

Mybatis-Generator自動生成XML文件以及接口和實體類

整合了MySQL和Oracle配置文件生成方法 這個是整個文件夾的下載地址&#xff1a;http://www.codepeople.cn/download 主要給大家介紹一下generatorConfig.xml文件的配置&#xff0c;以及生成后的文件。 generatorConfig.xml <?xml version"1.0" encoding"UTF…

如何在Windows 10上設置默認Linux發行版

Windows 10 now allows you to install multiple Linux environments, starting with the Fall Creators Update. If you have multiple Linux environments, you can set your default and switch between them. Windows 10現在允許您從Fall Creators Update開始安裝多個Linux…

mysql全備份+增量備份筆記總結

備份基礎知識 冷備&#xff08;cold backup&#xff09;&#xff1a;需要關mysql服務&#xff0c;讀寫請求均不允許狀態下進行&#xff1b; 溫備&#xff08;warm backup&#xff09;&#xff1a; 服務在線&#xff0c;但僅支持讀請求&#xff0c;不允許寫請求&#xff1b; 熱備…

pjax學習

PJAX 介紹 紅薯 發布于 2012/04/11 22:06閱讀 61K收藏 116評論 11jQuery.Pjax kissy開發四年只會寫業務代碼&#xff0c;分布式高并發都不會還做程序員&#xff1f;->>> 介紹 pushState是一個可以操作history的api&#xff0c;該api的介紹和使用請見這里&#xff1a…