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 532e692358981..680e91305a0cb 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -108,8 +108,9 @@ public FlutterRenderer(@NonNull FlutterJNI flutterJNI) { public void onResume(@NonNull LifecycleOwner owner) { Log.v(TAG, "onResume called; notifying SurfaceProducers"); for (ImageReaderSurfaceProducer producer : imageReaderProducers) { - if (producer.callback != null) { - producer.callback.onSurfaceCreated(); + if (producer.callback != null && producer.notifiedDestroy) { + producer.notifiedDestroy = false; + producer.callback.onSurfaceAvailable(); } } } @@ -462,6 +463,13 @@ final class ImageReaderSurfaceProducer // will be produced at that size. private boolean createNewReader = true; + /** + * Stores whether {@link Callback#onSurfaceDestroyed()} was previously invoked. + * + *

Used to avoid signaling {@link Callback#onSurfaceAvailable()} unnecessarily. + */ + private boolean notifiedDestroy = false; + // State held to track latency of various stages. private long lastDequeueTime = 0; private long lastQueueTime = 0; @@ -689,6 +697,7 @@ public void onTrimMemory(int level) { cleanup(); createNewReader = true; if (this.callback != null) { + notifiedDestroy = true; this.callback.onSurfaceDestroyed(); } } diff --git a/shell/platform/android/io/flutter/view/TextureRegistry.java b/shell/platform/android/io/flutter/view/TextureRegistry.java index caeb799d7598a..501378f419cbf 100644 --- a/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -96,8 +96,8 @@ interface SurfaceProducer extends TextureEntry { /** * Sets a callback that is notified when a previously created {@link Surface} returned by {@link - * SurfaceProducer#getSurface()} is no longer valid, either due to being destroyed or being - * changed. + * SurfaceProducer#getSurface()} is no longer valid due to being destroyed, or a new surface is + * now available (after the previous one was destroyed) for rendering. * * @param callback The callback to notify, or null to remove the callback. */ @@ -106,18 +106,65 @@ interface SurfaceProducer extends TextureEntry { /** Callback invoked by {@link #setCallback(Callback)}. */ interface Callback { /** - * Invoked when a previous surface is now invalid and a new surface is now available. + * An alias for {@link Callback#onSurfaceAvailable()} with a less accurate name. + * + * @deprecated Override and use {@link Callback#onSurfaceAvailable()} instead. + */ + @Deprecated(since = "Flutter 3.27", forRemoval = true) + default void onSurfaceCreated() {} + + /** + * Invoked when an Android application is resumed after {@link Callback#onSurfaceDestroyed()}. * - *

Typically plugins will use this callback as a signal to redraw, such as due to the - * texture being resized, the format being changed, or the application being resumed after - * being suspended in the background. + *

Applications should now call {@link SurfaceProducer#getSurface()} to get a new + * {@link Surface}, as the previous one was destroyed and released as a result of a low memory + * event from the Android OS. + * + *

+       * {@code
+       * void example(SurfaceProducer producer) {
+       *   producer.setCallback(new SurfaceProducer.Callback() {
+       *     @override
+       *     public void onSurfaceAvailable() {
+       *       Surface surface = producer.getSurface();
+       *       redrawOrUse(surface);
+       *     }
+       *
+       *     // ...
+       *   });
+       * }
+       * }
+       * 
*/ - void onSurfaceCreated(); + default void onSurfaceAvailable() { + this.onSurfaceCreated(); + } /** - * Invoked when a previous surface is now invalid. + * Invoked when a {@link Surface} returned by {@link SurfaceProducer#getSurface()} is invalid. + * + *

In a low memory environment, the Android OS will signal to Flutter to release resources, + * such as surfaces, that are not currently in use, such as when the application is in the + * background, and this method is subsequently called to notify a plugin author to stop + * using or rendering to the last surface. + * + *

Use {@link Callback#onSurfaceAvailable()} to be notified to resume rendering. + * + *

+       * {@code
+       * void example(SurfaceProducer producer) {
+       *   producer.setCallback(new SurfaceProducer.Callback() {
+       *     @override
+       *     public void onSurfaceDestroyed() {
+       *       // Store information about the last frame, if necessary.
+       *       // Potentially release other dependent resources.
+       *     }
        *
-       * 

Typically plugins will use this callback as a signal to release resources. + * // ... + * }); + * } + * } + *

*/ void onSurfaceDestroyed(); } diff --git a/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java b/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java index 0ed6a4754a589..bf733fb8e08e4 100644 --- a/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java +++ b/shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java @@ -762,7 +762,7 @@ public void ImageReaderSurfaceProducerIsCreatedOnLifecycleResume() throws Except TextureRegistry.SurfaceProducer.Callback callback = new TextureRegistry.SurfaceProducer.Callback() { @Override - public void onSurfaceCreated() { + public void onSurfaceAvailable() { latch.countDown(); } @@ -771,6 +771,9 @@ public void onSurfaceDestroyed() {} }; producer.setCallback(callback); + // Trim memory. + ((FlutterRenderer.ImageReaderSurfaceProducer) producer).onTrimMemory(40); + // Trigger a resume. ((LifecycleRegistry) ProcessLifecycleOwner.get().getLifecycle()) .setCurrentState(Lifecycle.State.RESUMED);