内容简介:Flutter正式发布1.0release版本,其中有Platform Views可以支持iOS,Android原生View嵌入Flutter中进行展示,如对应issue:业务需求需要Youtube播放器,考虑到有Platform Views支持,采用了原生实现Youtube播放功能,将View嵌入到Flutter中使用。iOS端在实现了视频列表以后,发现看了几个视频以后内存就会爆掉,问题很严重。
Flutter正式发布1.0release版本,其中有Platform Views可以支持iOS,Android原生View嵌入Flutter中进行展示,如 developers.googleblog.com/2018/12/flu… 这篇文章所述。
对应issue: github.com/flutter/flu…
业务需求需要Youtube播放器,考虑到有Platform Views支持,采用了原生实现Youtube播放功能,将View嵌入到Flutter中使用。iOS端在实现了视频列表以后,发现看了几个视频以后内存就会爆掉,问题很严重。
业务代码方向调查
初步怀疑还是原生UI创建后的循环引用问题,参考 github.com/flutter/plu… 实现了一个简单的UIView,核心代码如下:
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的底层实现上。
底层engine代码方向调查
关于如何使用Flutter engine可以参考 juejin.im/post/5c24ac… 引入engine进行调查,非常方便。
UIKit方向调查
同时看到flutter engine的issue中有 github.com/flutter/flu… ,其实是有人遇到同样的问题了,官方没有解决问题把问题关了,只能靠我们自己进行调查了。
因为有之前解决Flutter engine内存泄露的经验,相关内容可以参考 juejin.im/post/5c24ad… 。
想当然的开始调查engine的代码想当然的以为会是Google在实现PlatformView时有类似的循环引用问题导致创建的原生UI无法被释放。Google在实现PlatformView时会创建FlutterPlatformView和FlutterOverlayView两个View,通过在dealloc添加log方式可以发现创建的View都得到了释放,基本可以判断UIKit这部分没有问题。
这时没有什么好办法才使用了Apple的查内存泄露的工具,调查结果如下图所示:
可以看到主要的内存泄露来源都是IOSurface,查看堆栈可以看到是 renderbufferStorage:fromDrawable:方法造成的,可以查看苹果官方文档 developer.apple.com/documentati… 知道这个方法其实是将framebuffer与CAEAGLLayer进行绑定。
熟悉OpenGL的同学应该可以知道问题基本上可以定位到是Flutter engine使用CAEAGLLayer渲染时,申请的内存没有得到释放导致的。
OpenGL方向调查
找到调用 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了!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 一行一行源码分析清楚AbstractQueuedSynchronizer
- 一行一行手敲webpack4配置
- HashMap源码全解析从一道面试题说起:请一行一行代码描述下hashmap put方法
- 幻术,一行代码实现镂空效果
- 一行代码逃逸 Safari 沙箱
- 一行代码实现 UIView 镂空效果
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Machine Learning
Kevin Murphy / The MIT Press / 2012-9-18 / USD 90.00
Today's Web-enabled deluge of electronic data calls for automated methods of data analysis. Machine learning provides these, developing methods that can automatically detect patterns in data and then ......一起来看看 《Machine Learning》 这本书的介绍吧!