如何用 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.mzph.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…

ref:下一个项目为什么要用 SLF4J

ref:http://blog.mayongfa.cn/267.html 阿里巴巴 Java 开发手册 前几天阿里巴巴在云栖社区首次公开阿里官方Java代码规范标准&#xff0c;就是一个PDF手册&#xff0c;有命名规范&#xff0c;让你知道自己原来取的每一个类名、变量名都是烂名字&#xff0c;真替你家未来孩子担心…

计算机突然蓝屏无法启动_为什么计算机无法立即启动?

计算机突然蓝屏无法启动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…

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…

pjax学习

PJAX 介绍 红薯 发布于 2012/04/11 22:06阅读 61K收藏 116评论 11jQuery.Pjax kissy开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f;->>> 介绍 pushState是一个可以操作history的api&#xff0c;该api的介绍和使用请见这里&#xff1a…

SQL Server 2000详细安装过程及配置

说明&#xff1a;这篇文章是几年前我发布在网易博客当中的原创文章&#xff0c;但由于网易博客现在要停止运营了&#xff0c;所以我就把这篇文章搬了过来&#xff0c;虽然现如今SQL Server 2000软件早已经过时了&#xff0c;但仍然有一部分人在使用它&#xff0c;尤其是某些高校…

移动应用ios和网页应用_如何在iOS上一次移动多个应用

移动应用ios和网页应用Apple doesn’t really believe in detailed instruction manuals, so some handy tricks slip through the cracks. One such trick we’ve recently discovered is that you can move multiple app icons at once on iOS. Here’s how. Apple并不真正相…

2018-2019 20165226 Exp9 Web安全基础

2018-2019 20165226 Exp9 Web安全基础 目录 一、实验内容说明及基础问题回答 二、实验过程 Webgoat准备XSS攻击 ① Phishing with XSS 跨站脚本钓鱼攻击② Stored XSS Attacks 存储型XSS攻击③ Reflected XSS Attacks 反射型XSS攻击 CSRF攻击 ① Cross Site Request Forgery(CS…

用 git 同步 Colab 与 Gitlab、Github 之间的文件

Colab 是谷歌提供的免费 Jupyter 服务&#xff0c;可使用 GPU。但由于每次的 VM &#xff08;虚拟机&#xff09;登出后所有文件都会连同&#xff36;&#xff2d;被毁掉。如何将一个项目里的程序或数据同步到 Colab则往往比较麻烦。尽管谷歌盘也可以挂到 Colab 里用&#xff0…