首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Flutter单引擎和外接纹理内存优化探索之路

Flutter单引擎和外接纹理内存优化探索之路

原创
作者头像
老码小张
发布于 2020-01-15 02:06:56
发布于 2020-01-15 02:06:56
5.7K4
举报
文章被收录于专栏:玩转全栈玩转全栈

背景

今年九月初,王者人生Android端及iOS端正式接入flutter跨平台方案来提升开发效率。

接入flutter之后,我们成功使用flutter上线了首页一起玩赢福利,上线之后,我们的优化工作也一直紧锣密鼓的进行着,其中最为突出的三个问题是【flutter热修复,flutter单引擎,flutter利用原生图片缓存来减少整体内存占用】。

flutter的热更新

着手研究flutter热更新是为了应对现网出现flutter相关的bug好紧急修复,这个在我前面的文章《带你不到80行代码搞定Flutter热更新》中已经提到,这个问题我们目前已经解决了,这里不再啰嗦。

flutter单引擎

着手研究flutter单引擎,是因为对于以原生接入flutter这种形式的项目来说,因为并不是一个纯粹的flutter项目,因此,可能会出现以下这样的导航方式中间过程,而且可能会存在多次。

而且,出现flutter通过调用原生jsbridge在开一个flutter也是有可能的发生的,当出现这样一种情况时,很明显,flutter会有多个实例,那么我们的flutter引擎内存占用是否会有多份呢?

带着这个问题,我研究了一下flutter的启动流程,也记录了一下过程《flutter启动流程简析》,而这个过程让我明白了我们起初的接入方式做不到单引擎,但是如果我们换另外一种方式,可以很巧妙的做到单引擎,下面来探索这个过程。

通过下图,可以看到,FlutterView存在两个版本,这还是在一个flutter版本中,如图所示:

而我们最初的接入方式是采用的io.flutter.view 包下的FlutterView

它是通过Flutter.createView()创建的,相关部分的省略代码可以参考:

代码语言:txt
AI代码解释
复制
public static FlutterView createView(@NonNull final Activity activity, @NonNull Lifecycle lifecycle, String initialRoute) 
。。。
if (nativeView == null) {
                this.mNativeView = new FlutterNativeView(activity.getApplicationContext());
            } else {
                this.mNativeView = nativeView;
            }

            this.dartExecutor = this.mNativeView.getDartExecutor();
            this.flutterRenderer = new FlutterRenderer(this.mNativeView.getFlutterJNI());
            this.mIsSoftwareRenderingEnabled = FlutterJNI.nativeGetIsSoftwareRenderingEnabled();

可以看到dartExecutorFlutterRendererNativeView等都是耦合在FlutterView中的。所以这个FlutterView在的话,引擎就会一直在。然后,我们关注一下Flutter.createView()的时候,flutter给加上的生命周期回调函数。

这里重点关注一下onPause,onStop,这里为了便于理解,我特意补充了fragment生命周期图

fragment生命周期

很明显,单我们栈里面出现两个flutter模块的时候,被覆盖的,就是前一个flutter模块会走

onPause

onStop

注意,这里并没有走onDestroyView,和onDetach,往下翻可以看到我贴的日志。

回过头来看看我们之前的接入方式,在生命周期回调中看看,跟进去,你会发现,并没有对DartExecutor等做释放操作。

所以,以**io.flutter.view** 包下的**FlutterView**接入flutter的方式,在有多个**flutter**实例的情况下,是会出现多分引擎内存占用的,而且因为引擎**代码耦合**在**FLutterView**中的原因,我们很难做到单引擎。

所以,我们另外一种接入方式可以做到单引擎吗?

那么,对比使用另外一个FlutterView的接入方式来看,我们发现有一个代理类FlutterActivityAndFragmentDelegate中,代理的生命周期函数:

有释放引擎的操作,这就意味着,这意味着,如果这个代理类派上用场,那么,如果我们出现两个flutter模块,前一个的引擎是否会释放呢?

用图说话最简单的了,如图所示

所以,biubiubiu,我将接入方式进行了重构,当然,用上了这个代理类,然后,走走上面的导航流程,用Profiler工具看看FLutterEngin实例:

恩,切换到第二个flutter模块,gc一下,看看发现确实只有一个引擎了。做法其实很简单。

  1. 继承io.flutter.embedding.android.FlutterFragment
  2. 写一个引擎提供者EngineProvider
  3. 串联起来就OK啦。 部分代码可以参考:
代码语言:txt
AI代码解释
复制
//引擎提供者
public class TipEngineProvider {
    public static FlutterEngine obtain() {
        return new FlutterEngine(IGameApplication.getIGameApplicationContext());
    }
}
。。。。。。
//fragment中-
@Override
    public void onAttach(@NonNull Context context) {
        FlutterMain.startInitialization(IGameApplication.getIGameApplicationContext());
        FlutterMain.ensureInitializationComplete(IGameApplication.getIGameApplicationContext(), (String[]) null);
        super.onAttach(context);
    }
    public FlutterEngine provideFlutterEngine(@NonNull Context context) {
        return TipEngineProvider.obtain();
    }
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        ShimPluginRegistry pluginRegistry = new ShimPluginRegistry(flutterEngine);
        GeneratedPluginRegistrant.registerWith(pluginRegistry);
        new TipMethodChannelProxy().init(this, flutterEngine);
    }

图片优化,利用原生缓存

第一次爬坑,利用flutter平台提供的PlatFromView,

包装原生的ImageView,做到了利用原生图片缓存,详情可以参考我写的这篇文章

Flutter利用原生控件加载图片,馋原生的图片缓存

在图片较少时,这种方式固然可以,但是一旦出现像列表加载图片的场景,性能问题就出现了,当使用列表加载多张图片时,滑动起来会非常卡。所以PlatformView不适合用于列表,仅仅适合用户页面呈现单一控件的情景,比如地图,比如单个的视频播放器,有很多引用列表展示视频,使用PlatformView实现的那些视频播放插件很显然不适合,我们可以发现,flutter团队视频播放器https://pub.dev/packages/video_player的实现就不是platfomView,是使用的外接纹理。

第二次爬坑,利用Texture 外接纹理。

Google了一下,很不幸,flutter外接纹理渲染图片的demo非常少,仅仅找到了官方的VideoPlayer可以看看源码中联系texture和原生的代码,这里贴出比较重要的部分。

代码语言:txt
AI代码解释
复制
 @override
  Widget buildView(int textureId) {
    return Texture(textureId: textureId);
  }
  EventChannel _eventChannelFor(int textureId) {
    return EventChannel('flutter.io/videoPlayer/videoEvents$textureId');
  }
代码语言:txt
AI代码解释
复制
private void setupVideoPlayer(
      EventChannel eventChannel, TextureRegistry.SurfaceTextureEntry textureEntry, Result result) {
    ....
    surface = new Surface(textureEntry.surfaceTexture());
    exoPlayer.setVideoSurface(surface);
    setAudioAttributes(exoPlayer);
    ....
    Map<String, Object> reply = new HashMap<>();
    reply.put("textureId", textureEntry.id());
    result.success(reply);

我们可以看到最重要的两行代码是这个:

代码语言:txt
AI代码解释
复制
surface = new Surface(textureEntry.surfaceTexture());
exoPlayer.setVideoSurface(surface);

参考其原理,那我们要将bitmap渲染上去,是不是只要想办法把bitmap扔给surface,然后在合适的时机手动触发surface的一些回调,比如unlockCanvasAndPost就可以将bitmap渲染出来,既然视频都可以做样做不卡,一张bitmap应该不会存在性能问题才是,恩,这是理论上的,但是,这方面的这些方面的demo没有找到,不知道如何推进,可以留着后面继续研究。

那么还有其他方式吗?

继续在Google汪洋大海中寻找,发现讲原理倒是一堆一堆的,真正比较关键的地方缺没给出,直到我发现了这篇文章提到了如何去使用flutter的外接纹理,但是其实对于我来说,离贴bitmap有一定的距离,虽然只是贴了一个背景色而已,但是意义非凡,剩下的就是将原生缓存库中的bitmap拿到,并且贴上去就OK了。

所干就干,但是真的那么容易吗?经过研究,发现越陷越深,我不得不去了解一下**OpenGL**。

Android OpenGLES2.0(一)——了解OpenGLES2.0

Android OpenGLES2.0(八)——纹理贴图之显示图片

大致了解到 纹理映射是将2D的纹理映射到3D场景中的立体物体上 ,然后,OpenGL ES 的世界是3D的,但是手机屏幕能够给我展示的终究是一个平面,只不过是在绘制的过程中利用色彩和线条让画面呈现出3D的效果。OpenGL ES将这种从3D到2D的转换过程利用投影的方式使计算相对使用者来说变得简单可设置。

textureId->绘制->初始化glcontext->生成贴图->flutter

最终可以看看效果:

其中勾选导航栏按钮,表示使用flutter提供的image来加载图片,不勾选表示使用了外接纹理

可以看到,这次使用texture外接纹理,渲染图片,在列表加载多图情况下,滑动也非常流畅。

另外,这里也对比一下两种情况下帧率,发现在滑动列表时,外接纹理这种和flutter原生表现一致,基本上是可以满足性能要求的。:

使用外接纹理的方式:

使用flutter原生的Image

目前,插件仅仅实现了Android版本,已经开源了,目前支持webp,gif解析。iOS版本在开发中,相信很快就能出来。

在这个方案的实现过程中,请教过踩过这些坑的同事,特别鸣谢raymondguo,azraellong 。当然,我们的优化之路还将继续进行着,我知道我们并没将这个工作做到极致,只是目前可用而已,遇到新的问题,肯定还需要继续想办法突破。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
4 条评论
热度
最新
你好,请问用这个开源代码 ios出现内存泄漏
你好,请问用这个开源代码 ios出现内存泄漏
回复回复点赞举报
你好作者,请教下,这种单引擎应该是每打开一个新页面都会重新new一个FlutterEngine? 每次进入下个页面前上个页面的引擎会被回收,那么这样每次重新创建引擎是否有性能问题,像闲鱼的boost好像是维护一个引擎单例?
你好作者,请教下,这种单引擎应该是每打开一个新页面都会重新new一个FlutterEngine? 每次进入下个页面前上个页面的引擎会被回收,那么这样每次重新创建引擎是否有性能问题,像闲鱼的boost好像是维护一个引擎单例?
回复回复点赞举报
开源代码 链接失效了
开源代码 链接失效了
回复回复点赞举报
收藏了,挺有意义的
收藏了,挺有意义的
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
flutter单引擎方案
假设有两个模块,FlutterA,FlutterB,我们利用io.flutter.embedding.android.FlutterFragment下面的接入方式来接入flutter的话,下图,展示的是FlutterA模块,拉起一个独立的FLutterB模块,此时,会依照顺序发生下面的生命周期函数。
老码小张
2020/01/08
2.7K1
flutter单引擎方案
谈一谈Flutter外接纹理
当我们用flutter做实时视频渲染时,往往是要对视频或者相机画面做滤镜处理的,如图:
音视频开发进阶
2020/08/27
4.4K0
谈一谈Flutter外接纹理
【 flutter 】2w 字详细解析引擎初始化、启动流程源码
flutter 可以分层三层,第一层是我们 dart 的代码,包括 UI 组件、动画、Gesture 等等,也就是每次我们新建 dart 文件,需要 import 的那些包里的类:
圆号本昊
2021/12/30
1.7K0
【 flutter 】2w 字详细解析引擎初始化、启动流程源码
从源码看Flutter Android端的启动流程
Flutter在Android中的渲染载体就是Flutter容器,通常是以Activity和Fragment的形式承载,虽然也有FlutterView,但是需要单独处理的关联方法太多,所以不太建议使用,这篇文章将分析Flutter在Android中的加载和启动流程,了解Flutter是如何在Android中加载并渲染的。
用户1907613
2024/04/10
3590
从源码看Flutter Android端的启动流程
5分钟彻底搞懂Flutter中PlatFormView与Texture
想要在flutter想显示原生的东东,大家知道,一般有两种方式,一种是PlatformView,另外一种是Texture(俗称外接纹理)。其中PlatformView区分Android和iOS,在Android平上上叫做 AndroidView,而在iOS平台,叫UIKitView。而今天,我要说的是,
老码小张
2020/02/17
15.3K3
5分钟彻底搞懂Flutter中PlatFormView与Texture
Flutter Android 端 FlutterView 相关流程源码分析
前面系列文章我们分析了 FlutterActivity 等相关流程,知道一个 Flutter Android App 的本质是通过 FlutterView 进行渲染。当时由于篇幅限制,我们没有进入详细分析,这里作为一个专题进行简单分析。
工匠若水
2021/08/22
1.7K0
Flutter混编工程之打通纹理之路
Flutter的图片系统基于Image的一套架构,但是这东西的性能,实在不敢恭维,感觉还停留在Native开发至少5年前的水平,虽然使用上非常简单,一个Image.network走天下,但是不管是解码性能还是加载速度,抑或是内存占用和缓存逻辑,都远远不如Native的图片库,特别是Glide。虽然Google一直在有计划优化Flutter Image的性能,但现阶段,体验最佳的图片加载方式,还是通过插件,使用Glide来进行加载。
用户1907613
2022/12/12
1.1K0
flutter接入现有的app详细介绍
接入的方式,我是参考的官方的介绍文档,我这里尝试的是android的接入方式,还算比较顺利。
老码小张
2018/08/14
2.6K1
flutter启动流程跟踪简析
我们项目接入flutter由来已久,采用的是混栈开发的方式,没有办法,因为项目开发了比较久,全部替换为flutter实现想想也不太现实,混栈开发的过程中,我们遇到了一些问题,当时都是一脸懵逼,貌似最后都是很侥幸的通过万能的Google给汤坑汤沟去了,说来也是惭愧,本来很简单的事情,却需要花费一些不必要的时间,所以,当时就想如果有时间,一定要好好研究下flutter的原理,我们接入flutter,flutter到底是如何启动的,启动的过程中他做了一些什么,假如以后需要做性能优化,我们该从何处入手。
老码小张
2019/12/03
1.8K0
flutter启动流程跟踪简析
Flutter Android 端 FlutterEngine Java 相关流程源码分析
我们在 Flutter Android 端的 Java 层代码中经常看到 FlutterEngine、FlutterEngineGroup、FlutterEngineCache 等相关类的使用,你是不是也经常搞不清他们的关系和作用?本文就是对他们的一个解剖分析,由于 Flutter 2 版本对这块做了大调整,所以我们的分析以 2.2.3 版本为例分析。
工匠若水
2021/08/22
1.5K0
Flutter 深入探索混合开发的技术演进
关于 Flutter 混合 PlatformView 的实现已经介绍过两次,随着 5 月份谷歌 IO 的接近,新的 PlatformView 实现应该也会随之而来,本次就从头到尾来一个详细的关于 PlatformView 的演进总结。
GSYTech
2022/05/13
1.3K0
Flutter 深入探索混合开发的技术演进
5分钟搞定Flutter与Android 的交互(内附大量Flutter学习资源)
Flutter 说到底只是一个 UI 框架,很多功能都需要通过原生的 Api 来实现,那么就会涉及到 Flutter 和 Native 的交互,因为本人不懂 iOS 开发,所以只能讲下 Flutter 同 Android 的交互。
陈宇明
2020/12/16
2.5K0
Flutter Android 端 Activity/Fragment 流程源码分析
前面文章我们分析了 flutter 在 android 端编译命令相关流程,我们接下来需要先分析一下 Flutter Android 端 framework 平台实现代码(非 native engine 引擎部分),下面以一个纯 Flutter App 为例展开分析。
工匠若水
2021/08/22
1.3K0
Flutter 多引擎渲染,在稿定 App 的实践
发这篇文章的原因主要是关于 multiple-flutters[1] Flutter 多引擎的介绍也好,实践也好,可参考的资源实在太少,包括官方的 issues 也没很多有价值的信息,前几个月确实在坑的泥潭里死去活来。但好在已经走出了一条羊肠小道,可供大家参考。
Swift社区
2022/12/12
1.2K0
Flutter 多引擎渲染,在稿定 App 的实践
[-综合篇-] 相机、OpenGL、视频、Flutter和SurfaceView
认识一个类,相当于结交一位朋友;看一篇源码,相当于一次顶级的会话; 读一个框架,相当于见证一段思想;做一个程序,相当于创造一个生命; 一次Git提交,相当于记录一次成长;生活也许并非那么美好,但一切可以这么崇高。----张风捷特烈
张风捷特烈
2020/04/30
3.3K0
[-综合篇-] 相机、OpenGL、视频、Flutter和SurfaceView
[ - OpenGLES3.0 - ] 第四集 视频接入OpenGLES3.0实现特效
@张风捷特烈 2020.12.08 未允禁转 我的公众号:编程之王 联系我--邮箱:1981462002@qq.com -- 微信: ~ END ~
张风捷特烈
2020/12/09
1.8K0
[ - OpenGLES3.0 - ]  第四集  视频接入OpenGLES3.0实现特效
成熟项目的Flutter快速引入以及Flutter、Native混合开发探究
又有两个月没有发文了,最近我司逐渐开始在成熟的项目中引入 Flutter 作为一种新的开发方式。作为比较早吃螃蟹的人,我也在组内为三四十个同学做了一次 Flutter 相关的分享。因为涉及了一些内部信息所以等我脱敏整理好之后会用专门的一篇文章进行相关的分享,大家可以开始期待了,哈哈。至于本篇文章,我会讲一些有意思的东西——成熟项目的Flutter快速引入 与 Flutter、Native混合开发,希望大家能多多点赞关注。
何时夕
2019/08/21
2.1K0
成熟项目的Flutter快速引入以及Flutter、Native混合开发探究
Flutter 实现视频全屏播放逻辑及解析
相信做过移动端视频开发的同学应该了解,想要实现视频从普通播放到全屏播放的逻辑并不是很简单,比如在 GSYVideoPlayer 中的动态全屏切换效果,就使用了创建全新的 Surface 来替换实现:
GSYTech
2020/02/24
3.6K0
Android 集成 Flutter | 与交互
使用 Flutter 已经有一段时间了,开发体验还是非常好的,但是一般我们在正式使用 Flutter 的时候很少会去创建一个纯 Flutter 项目,而是需要在之前的项目中已集成的方式来编写 Flutter。这篇文章将以如何在 Android 项目中集成 Flutter 和 如何在两者之间进行交互为主要内容。
345
2022/02/11
2.3K0
Android 集成 Flutter | 与交互
Flutter 1.20 下的 Hybrid Composition 深度解析
在以前的 《Android PlatformView 和键盘问题》 一文中介绍过混合开发上 Android PlatformView 的实现和问题,原本 Android 平台上为了集成如 WebView、MapView等能力,使用了 VirtualDisplays 的实现方式。
GSYTech
2020/08/11
2.3K0
Flutter 1.20 下的 Hybrid Composition 深度解析
推荐阅读
相关推荐
flutter单引擎方案
更多 >
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档