background

Flutter officially released version 1.0 release, including Platform Views can support iOS, Android native View embedded in the Flutter to display, such as developers.googleblog.com/2018/12/flu… In this article.

Corresponding issue:github.com/flutter/flu… Google has merge:github.com/flutter/eng…

Business requirements require Youtube player. Considering the support of Platform Views, the native Youtube playback function is implemented and View is embedded into Flutter for use. After implementing the video list on iOS terminal, it was found that memory would burst after watching several videos, which was a very serious problem.

Business code direction survey

The initial suspicion is that it is a circular reference problem after the creation of the native UI, see github.com/flutter/plu… Implement a simple UIView, the core code is as follows:

  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(),
    );
  }
Copy the code

Click the setState button repeatedly to refresh the UI to achieve the creation and destruction of PlatformView. It is found that the simplest UIView still has the problem of memory leakage after repeated creation and destruction, without using PlatformView. The same code that creates the Widget that destroys the Flutter does not have a problem. This ensures that there is no problem with the business implementation. The code is based on the underlying implementation of the Flutter engine.

Investigate the underlying Engine code direction

For information about how to use the Flutter Engine, see juejin.cn/post/684490… Introducing Engine for investigation is very convenient.

UIKit direction survey

Also see the flutter Engine issue has github.com/flutter/flu… In fact, someone encountered the same problem. The authorities closed the problem without solving it, so we have to rely on our own investigation.

Due to my previous experience in resolving memory leaks of the Flutter Engine, please refer to juejin.cn/post/684490… .

I started investigating the engine code with the assumption that the native UI created by Google would not be released due to a similar problem with circular references when implementing PlatformView. FlutterPlatformView and FlutterOverlayView will be created when Google implements PlatformView. By adding log to dealloc, it can be found that all created views are released. You can basically tell that there’s no problem with the UIKit part.

At this time, there is no good way to use Apple’s memory leak detection tool, and the results of the investigation are shown in the picture below:

Can see the main source of memory leaks are IOSurface, view the stack can see is renderbufferStorage: fromDrawable: method, can view the apple official documentation developer.apple.com/documentati… Know that this method binds the frameBuffer to the CAEAGLLayer.

Those familiar with OpenGL should know that the problem is basically caused by the memory that the Flutter engine does not free when it uses the CAEAGLLayer to render.

OpenGL direction investigation

Find call renderbufferStorage: fromDrawable: method, is in IOSGLRenderTarget, probably looked at is the basic of OpenGL rendering module, via the upward, The references found in FlutterOverlayView are consistent with the results of UIKit direction investigation. When implementing PlatformView, IOSGLRenderTarget will be created layer by layer, as shown below:

By investigating the memory free status of the relevant node, it is confusing to find that everything created in the path is freed, as if the whole investigation is blocked. OpenGL Framebuffer (renderTarget); / / Release 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);
}
Copy the code

In the process of DeleteFramebuffers, Google did not set the context first. In the structure of Flutter, which uses multiple contexts to render, it is better to set the current corresponding context in advance before gl operation to avoid other code changing the context. The operation is invalid when gl operation is performed here. So in the end, we only need to add one line of code to solve the big problem, which is as follows:

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);
}
Copy the code

After compiling, the project uses our compiled framework, and the memory problem is solved! It will be easier to use native View in the Flutter project!

The Flutter technology accumulates relevant links

Flutter general base library flutter_luakit_plugin

Flutter_luakit_plugin Example

How to Build the Flutter Engine

Troubleshooting Flutter Engine Memory Leaks

Fixed memory leaks after the Flutter Engine (available directly)

Fix memory leak after the use of Flutter Engine example

Continuously updated…