From a4b73c6d1a1e6d9b1120f27fb619bee23cabac19 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Tue, 24 Sep 2024 19:07:56 -0700 Subject: [PATCH 1/2] Add SurfaceProducer#onSurfaceAvailable, deprecate Created. --- .../engine/renderer/FlutterRenderer.java | 13 +++- .../io/flutter/view/TextureRegistry.java | 65 ++++++++++++++++--- .../engine/renderer/FlutterRendererTest.java | 5 +- 3 files changed, 71 insertions(+), 12 deletions(-) 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..20c872c35c0c4 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); From 85ec660291be92419574e6b77e46d8c8c2275671 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Tue, 24 Sep 2024 19:08:28 -0700 Subject: [PATCH 2/2] ++ --- .../io/flutter/embedding/engine/renderer/FlutterRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 20c872c35c0c4..680e91305a0cb 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -466,7 +466,7 @@ final class ImageReaderSurfaceProducer /** * Stores whether {@link Callback#onSurfaceDestroyed()} was previously invoked. * - *

Used to avoid signaling {@link Callback#onSurfaceAvailable()} unnecessarily.

+ *

Used to avoid signaling {@link Callback#onSurfaceAvailable()} unnecessarily. */ private boolean notifiedDestroy = false;