旅行社手机网站建设方案,Muse wordpress,网站不做301可以吗,泉州市知名网站建设公司得益于 Flutter 优秀的跨平台表现#xff0c;混合开发在如今的 App 中随处可见#xff0c;如最近微信公布的小程序新渲染引擎 Skyline 发布正式版也在底层渲染上使用了 Flutter#xff0c;号称渲染速度提升50%。
在现有的原生 App 中引入 Flutter 来开发不是一件简单的事混合开发在如今的 App 中随处可见如最近微信公布的小程序新渲染引擎 Skyline 发布正式版也在底层渲染上使用了 Flutter号称渲染速度提升50%。
在现有的原生 App 中引入 Flutter 来开发不是一件简单的事需要解决混合模式下带来的种种问题如路由栈管理、包体积和内存突增等另外还有一种特殊的情况一个最初就由 Flutter 来开发的 App 也有可能在后期混入、原生 View 去开发。
我所在的团队目前就是处于这种情况Flutter 目前在性能表现上面还不够完美整体页面还不够流畅并且在一些复杂的页面场景下会出现比较严重的发热行为尽管目前 Flutter 团队发布了新的渲染引擎 impeller它在 iOS 上表现优异流畅度有了质的提升但还是无法完全解决一些性能问题且 Android 下 impeller 也还没开发完成。
为了应对当下出现的困局和以后可能出现的未知问题我们期望通过混合模式来扩宽更多的可能性。
路由管理
混合开发下最难处理的就是路由问题了我们知道原生和 Flutter 都有各自的路由管理系统在原生页面和 Flutter 页面穿插的情况下如何统一管理和互相交互是一大难点。目前比较流行的单引擎方案代表框架是闲鱼团队出品flutter_boostflutter 官方代表的多引擎解决方案 FlutterEngineGroup。
单引擎方案 flutter_boost
flutter_boost 通过复用 Engine 达到最小内存的目的下面这张图是它的设计架构图图片来自官方 在引擎处理上flutter_boost 定义了一个通用的 CacheIdflutter_boost_default_engine当原生需要跳转到 Flutter 页面时通过FlutterEngineCache.getInstance().get(ENGINE_ID); 获取同一个 Engine这样无论打开了多少如图中的 A、B、C 的 Flutter 页面时都不会产生额外的Engine内存损耗。
public class FlutterBoost { public static final String ENGINE_ID flutter_boost_default_engine; ...
} 另外双端都注册了导航的接口通过Channel来通知用于请求路由变化、页面返回以及页面的生命周期处理等。在这种模式下这一层Channel的接口处理是重点。
多引擎方案 FlutterEngineGroup
为了应对内存爆炸问题官方对多引擎场景做了优化FlutterEngineGroup应运而生FlutterEngineGroup下的 Engine 共用一些通用的资源例如GPU 上下文、线程快照等生成额外的 Engine 时号称内存占用缩小到 180k。这个程度基本可以视为正常的损耗了。
以上图中的 B、C 页面为例两者都是 Flutter 页面在 FlutterEngineGroup 这种处理下因为它们所在的 Engine 不是同一个这会产生完全的隔离行为也就是 B、C 页面使用不同的堆栈处在不同的 Isolate 中两者是无法直接进行交互的。
多引擎的优点是它可以抹掉上图所示的 F、E、C 和 D、A 等内部路由每次新增 Flutter 页面时全部回调到原生让原生生成新的 Engine 去承载页面这样路由的管理全部由原生去处理一个 Engine 只对应一个 Flutter 页面。
但它也会带来一些额外的处理像上面提到的处在不同 Engine 下的Flutter 页面之间是无法直接交互的如果涉及到需要通知和交互的场景还得通过原生去转发。
关于FlutterEngineGroup的更多信息可以参考官方说明。
性能对比
官方号称 FlutterEngineGroup 创建新的 Engine 只会占用 180k 的内存那么是不是真就如它所说呢下面我们来针对上面这两种方案做一个内存占用测试
flutter_boost
测试机型OPPO CPH2269
测试代码github.com/alibaba/flu…
内存 dump 命令 adb shell dumpsys meminfo com.idlefish.flutterboost.example
条件PSSRSS最大变化1 Native88667165971261052831327M1 Native 1 Flutter114772194284-28217211M2 Native 2 Flutter114490196005577459926M5 Native 5 Flutter120264201997134141411913M10 Native 10 Flutter133678216116
第一次加载 Flutter 页面时增加 27M 左右内存此后多开一个页面内存增加呈现从 1M - 2M - 2.6 M 这种越来越陡的趋势数值只是参考因为其中有 Native 页面只看趋势变化上看
FlutterEngineGroup
测试机型OPPO CPH2269
测试代码github.com/flutter/sam…
内存 dump 命令 adb shell dumpsys meminfo dev.flutter.multipleflutters
条件PSSRSS最大变化1 Native45962140817298223167531M1 Native 1 Flutter75784172492-61020632M2 Native 2 Flutter75174174555745170273.7M5 Native 5 Flutter82625181582855874428M10 Native 10 Flutter91183189024
第一次加载 Flutter 页面时增加 31M 左右内存此后多开一个页面内存增加呈现从 1M - 1.2M - 1.6 M 这种越来越陡的趋势数值只是参考因为其中有 Native 页面只看趋势变化上看
结论
两个测试使用的是不同的 demo 代码不能通过数值去得出孰优孰劣。但通过数值的表现我们基本可以确认两个方案都不会带来异常的内存暴涨完全在可以接受的范围。
PlatformView
PlatformView 也可实现混合 UIFlutter 中的 WebView 就是通过 PlatformView 这种方式引入的。
PlatformView 允许我们向 Flutter 界面中插入原生 View在一个页面的最外层包裹一层 PlatformView路由的管理都由 Flutter 来处理。这种方式下没有额外的 Engine 产生是最简单的混合方式。
但它也有缺点不适合主 Native 混 Flutter 的场景而现在大多都是以主 Native 混 Flutter的场景为主。另外PlatformView 因其底层实现会出现兼容性问题在一些机型下可能会出现键盘问题、闪烁或其它的性能开销具体可看这篇介绍
数据共享
原生和 Flutter 使用不同的开发语言去开发所以在一侧定义的数据结构对象和内存对象对方都无法感知在数据同步和处理上必须使用其它手段。
MethodChannel
Flutter 开发者对 MethodChannel 一定不陌生开发当中免不了跟原生交互MethodChannel 是双向设计即允许我们在 Flutter 中调用原生的方法也允许我们在原生中调用 Flutter 的方法。对 Channel 不太了解的可以看一下官方文档如文档中提到的这个通道传输的过程中需要将数据编解码对应的关系以kotlin为例(完整的映射可以查看文档)
Dart | Kotlin |
| -------------------------- | ----------- |
| null | null |
| bool | Boolean |
| int | Int |
| int, if 32 bits not enough | Long |
| double | Double |
| String | String |
| Uint8List | ByteArray |
| Int32List | IntArray |
| Int64List | LongArray |
| Float32List | FloatArray |
| Float64List | DoubleArray |
| List | List |
| Map | HashMap |本地存储
这种方式比较容易理解将本地存储视为中转站Flutter中将数据操作存储到本地上回到原生页面时在某个时机如onResume去查询本地数据库即可反之亦然。
问题
不管是MethodChannel或是本地存储都会面临一个问题对象的数据结构是独立的两边需要重复定义。比如我在 Flutter 中有一个 Student 对象Android 端也要定义一个同样结构的 Student这样才能方便操作现在我将Student student转成Unit8List传到AndroidChannel中解码成Kotlin能操作的ByteArray再将ByteArray转译成Android中Student对象。
class Student { String name; int age; Student(this.name, this.age);
}
对于这个问题最好的解决办法是使用DSL一类的框架如Google的ProtoBuf将同一份对象配置文件编译到不同的语言环境中便能省去这部分双端重复定义的行为。
图片缓存
在内存方面如果同样的图片在两边都加载时会使得原生和 Flutter 都会产生一次缓存。在 Flutter 下默认就会缓存在ImageCache中原生下不同的框架由不同的对象负责为了去掉重复的图片缓存势必要统一图片的加载管理。
阿里的方案也是如此通过外接原生图片库共享图片的本地文作缓存和内存缓存。它的实现思路是通过自定义ImageProvider和Codec对接外部图库获取到图片数据做解析对接的处理是通过扩展 Flutter Engine。
如果期望不修改Flutter Engine也可通过外接纹理的方式去处理。通过PlatformChannel去请求原生使到图片的外接纹理数据通过TextTure组件展示图片。
// 自定义 ImageProvider 中通过 Channel 去请求 textureId
var id await _channel.invokeMethod(newTexture, {imageUrl: imageUrl,width: width ?? 0,height: height ?? 0,minWidth: constraints.minWidth,minHeight: constraints.minHeight,maxWidth: constraints.maxWidth,maxHeight: constraints.maxHeight,cacheKey: cacheKey,fit: fit.index,cacheOriginFile: cacheOriginFile,
});// ImageWidget 中展示时通过 textureId 去显示图片
SizedBox(width: width,heigt: height,child: Texture(filterQuality: FilterQuality.high,textureId: _imageProvider.textureId.value,),
)总结
不同业务对于混合的程度和要求有所要求并没有万能的方案。比如我团队的情况就是主Flutter混原生在路由管理上我选择了PlatformView这种处理模式这种方式更容易开发和维护后期如果发现有兼容性问题也可过渡到flutter_boost和FlutterEngineGroup上。 链接https://juejin.cn/post/7262616799219482681