diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java b/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java index 1d6bccc77a7ea..5944aa0d635ee 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java @@ -37,6 +37,7 @@ public class FlutterSurfaceView extends SurfaceView implements RenderSurface { private final boolean renderTransparently; private boolean isSurfaceAvailableForRendering = false; + private boolean isPaused = false; private boolean isAttachedToFlutterRenderer = false; @Nullable private FlutterRenderer flutterRenderer; @@ -200,6 +201,7 @@ public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) { "Surface is available for rendering. Connecting FlutterRenderer to Android surface."); connectSurfaceToRenderer(); } + isPaused = false; } /** @@ -241,6 +243,7 @@ public void pause() { // Don't remove the `flutterUiDisplayListener` as `onFlutterUiDisplayed()` will make // the `FlutterSurfaceView` visible. flutterRenderer = null; + isPaused = true; isAttachedToFlutterRenderer = false; } else { Log.w(TAG, "pause() invoked when no FlutterRenderer was attached."); @@ -253,8 +256,13 @@ private void connectSurfaceToRenderer() { throw new IllegalStateException( "connectSurfaceToRenderer() should only be called when flutterRenderer and getHolder() are non-null."); } - - flutterRenderer.startRenderingToSurface(getHolder().getSurface()); + // When connecting the surface to the renderer, it's possible that the surface is currently + // paused. For instance, when a platform view is displayed, the current FlutterSurfaceView + // is paused, and rendering continues in a FlutterImageView buffer while the platform view + // is displayed. + // + // startRenderingToSurface stops rendering to an active surface if it isn't paused. + flutterRenderer.startRenderingToSurface(getHolder().getSurface(), isPaused); } // FlutterRenderer must be non-null. diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java b/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java index a151977520784..5e2d8e03dfb67 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java @@ -36,6 +36,7 @@ public class FlutterTextureView extends TextureView implements RenderSurface { private boolean isSurfaceAvailableForRendering = false; private boolean isAttachedToFlutterRenderer = false; + private boolean isPaused = false; @Nullable private FlutterRenderer flutterRenderer; @Nullable private Surface renderSurface; @@ -187,6 +188,7 @@ public void detachFromRenderer() { public void pause() { if (flutterRenderer != null) { flutterRenderer = null; + isPaused = true; isAttachedToFlutterRenderer = false; } else { Log.w(TAG, "pause() invoked when no FlutterRenderer was attached."); @@ -217,7 +219,8 @@ private void connectSurfaceToRenderer() { } renderSurface = new Surface(getSurfaceTexture()); - flutterRenderer.startRenderingToSurface(renderSurface); + flutterRenderer.startRenderingToSurface(renderSurface, isPaused); + isPaused = false; } // FlutterRenderer must be non-null. diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index ff712d4edcc7e..144cf52086b61 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -134,7 +134,7 @@ public void onEngineWillDestroy() { * *
A new {@code FlutterEngine} will not display any UI until a {@link RenderSurface} is * registered. See {@link #getRenderer()} and {@link - * FlutterRenderer#startRenderingToSurface(Surface)}. + * FlutterRenderer#startRenderingToSurface(Surface, boolean)}. * *
A new {@code FlutterEngine} automatically attaches all plugins. See {@link #getPlugins()}. * diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index d68b17bdf7c44..d58e1b791870e 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -221,11 +221,24 @@ public void run() { * Notifies Flutter that the given {@code surface} was created and is available for Flutter * rendering. * + *
If called more than once, the current native resources are released. This can be undesired + * if the Engine expects to reuse this surface later. For example, this is true when platform + * views are displayed in a frame, and then removed in the next frame. + * + *
To avoid releasing the current surface resources, set {@code keepCurrentSurface} to true. + * *
See {@link android.view.SurfaceHolder.Callback} and {@link * android.view.TextureView.SurfaceTextureListener} + * + * @param surface The render surface. + * @param keepCurrentSurface True if the current active surface should not be released. */ - public void startRenderingToSurface(@NonNull Surface surface) { - if (this.surface != null) { + public void startRenderingToSurface(@NonNull Surface surface, boolean keepCurrentSurface) { + // Don't stop rendering the surface if it's currently paused. + // Stop rendering to the surface releases the associated native resources, which + // causes a glitch when showing platform views. + // For more, https://github.com/flutter/flutter/issues/95343 + if (this.surface != null && !keepCurrentSurface) { stopRenderingToSurface(); } @@ -248,8 +261,8 @@ public void swapSurface(@NonNull Surface surface) { /** * Notifies Flutter that a {@code surface} previously registered with {@link - * #startRenderingToSurface(Surface)} has changed size to the given {@code width} and {@code - * height}. + * #startRenderingToSurface(Surface, boolean)} has changed size to the given {@code width} and + * {@code height}. * *
See {@link android.view.SurfaceHolder.Callback} and {@link * android.view.TextureView.SurfaceTextureListener} @@ -260,8 +273,8 @@ public void surfaceChanged(int width, int height) { /** * Notifies Flutter that a {@code surface} previously registered with {@link - * #startRenderingToSurface(Surface)} has been destroyed and needs to be released and cleaned up - * on the Flutter side. + * #startRenderingToSurface(Surface, boolean)} has been destroyed and needs to be released and + * cleaned up on the Flutter side. * *
See {@link android.view.SurfaceHolder.Callback} and {@link * android.view.TextureView.SurfaceTextureListener} diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java b/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java index 48131a5dc545f..58af9a49fd418 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java @@ -37,7 +37,7 @@ public interface RenderSurface { * FlutterRenderer} at the appropriate times: * *