Flutter正式发布1.0release版本,其中有Platform Views能够支持iOS,Android原生View嵌入Flutter中进行展现,如 developers.googleblog.com/2018/12/flu… 这篇文章所述。javascript
对应issue:github.com/flutter/flu… Google已经merge:github.com/flutter/eng…html
业务需求须要Youtube播放器,考虑到有Platform Views支持,采用了原生实现Youtube播放功能,将View嵌入到Flutter中使用。iOS端在实现了视频列表之后,发现看了几个视频之后内存就会爆掉,问题很严重。java
初步怀疑仍是原生UI建立后的循环引用问题,参考 github.com/flutter/plu… 实现了一个简单的UIView,核心代码以下:git
int i = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView example'),
actions: <Widget>[
],
),
body: (i%2 == 0)? Container(color: Colors.red,) : WebView(
initialUrl: 'https://flutter.io',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: null,
),
floatingActionButton: favoriteButton(),
);
}
复制代码
经过不断的点击按钮setState刷新UI达到PlatformView的建立销毁,发现最简单的UIView在通过重复的建立销毁后依然会存在内存泄露的问题,而不使用PlatformView,一样的代码建立销毁Flutter的Widget不会出现问题,这样能够判定业务实现上没有问题,代码出在Flutter engine的底层实现上。github
关于如何使用Flutter engine能够参考 juejin.im/post/5c24ac… 引入engine进行调查,很是方便。web
同时看到flutter engine的issue中有 github.com/flutter/flu… ,实际上是有人遇到一样的问题了,官方没有解决问题把问题关了,只能靠咱们本身进行调查了。bash
由于有以前解决Flutter engine内存泄露的经验,相关内容能够参考 juejin.im/post/5c24ad… 。app
开始调查engine的代码想固然的觉得会是Google在实现PlatformView时有相似的循环引用问题致使建立的原生UI没法被释放。Google在实现PlatformView时会建立FlutterPlatformView和FlutterOverlayView两个View,经过在dealloc添加log方式能够发现建立的View都获得了释放,基本能够判断UIKit这部分没有问题。ide
这时没有什么好办法才使用了Apple的查内存泄露的工具,调查结果以下图所示:函数
能够看到主要的内存泄露来源都是IOSurface,查看堆栈能够看到是 renderbufferStorage:fromDrawable:方法形成的,能够查看苹果官方文档 developer.apple.com/documentati… 知道这个方法实际上是将framebuffer与CAEAGLLayer进行绑定。
熟悉OpenGL的同窗应该能够知道问题基本上能够定位到是Flutter engine使用CAEAGLLayer渲染时,申请的内存没有获得释放致使的。
找到调用 renderbufferStorage:fromDrawable:方法的地方,是在IOSGLRenderTarget中,大概看了一下是基本的OpenGL渲染模块,经过向上查找,在FlutterOverlayView中找到了引用的地方与UIKit方向调查的结果吻合,在实现PlatformView时会一层层传下来建立了IOSGLRenderTarget,层级关系以下所示:
经过调查相关节点的内存释放状况,发现路径中建立的东西都获得了释放,这时候就很困惑了,好像整个调查卡主了。再返回去看IOSGLRenderTarget的渲染流程,最终发如今析构函数中释放建立的OpenGL Framebuffer代码以下:
IOSGLRenderTarget::~IOSGLRenderTarget() {
FML_DCHECK(glGetError() == GL_NO_ERROR);
// Deletes on GL_NONEs are ignored
glDeleteFramebuffers(1, &framebuffer_);
glDeleteRenderbuffers(1, &colorbuffer_);
FML_DCHECK(glGetError() == GL_NO_ERROR);
}
复制代码
终于发现了问题所在,在DeleteFramebuffers时,Google没有先设置上下文,在Flutter这种使用多个context进行渲染的的结构中,进行gl操做时最好都提早设置一下当前对应的上下文,避免其余代码更改了上下文,这边再进行gl操做时操做无效。因此最后咱们只须要加一行代码就能够解决这个大问题,解决代码以下:
IOSGLRenderTarget::~IOSGLRenderTarget() {
[EAGLContext setCurrentContext:context_];
FML_DCHECK(glGetError() == GL_NO_ERROR);
// Deletes on GL_NONEs are ignored
glDeleteFramebuffers(1, &framebuffer_);
glDeleteRenderbuffers(1, &colorbuffer_);
FML_DCHECK(glGetError() == GL_NO_ERROR);
}
复制代码
编译后,工程中使用咱们编译出来的framework,内存问题就获得了解决!之后能够方便的在Flutter工程中使用原生View了!
flutter通用基础库flutter_luakit_plugin
持续更新中...