Change timing of onSurfaceDestroyed to match onSurfaceCleanup (#161252)

Follow-up to https://github.com/flutter/flutter/pull/160937
(https://github.com/flutter/flutter/issues/160933).

This more or less mirrors what we did already for `onSurfaceCreated` to
match `onSurfaceAvailable`.

With this approach, the master branch should immediately start seeing
better behavior in terms of being able to use the `onSurfaceDestroyed`
callback effectively (before the surface has been released). For
example, for a plugin that already uses `onSurfaceDestroyed`, [i.e
`camerax`](97ce56a68e/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/PreviewHostApiImpl.java):

```java
@Override
public void onSurfaceDestroyed() {
  // Invalidate the SurfaceRequest so that CameraX knows to to make a new request
  // for a surface.
  request.invalidate();
}
```

... the request is now invalidated _before_ the surface has been
destroyed, which is what (I believe) we wanted?

---

Folks that want to publish package updates that _definitely_ use the
correct timing will have to wait for the next stable.

/cc @hasali19 would be great to get your input here.
This commit is contained in:
Matan Lurey 2025-01-07 13:15:09 -08:00 committed by GitHub
parent 13ae4cd8d8
commit 9467677fef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 27 additions and 17 deletions

View File

@ -721,10 +721,6 @@ public class FlutterRenderer implements TextureRegistry {
}
cleanup();
createNewReader = true;
if (this.callback != null) {
notifiedDestroy = true;
this.callback.onSurfaceDestroyed();
}
}
private void releaseInternal() {

View File

@ -116,6 +116,8 @@ public interface TextureRegistry {
/**
* Invoked when an Android application is resumed after {@link Callback#onSurfaceDestroyed()}.
*
* <p>When this method is overridden, {@link Callback#onSurfaceCreated()} is not called.
*
* <p>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.
@ -141,17 +143,9 @@ public interface TextureRegistry {
}
/**
* Invoked when a {@link Surface} returned by {@link SurfaceProducer#getSurface()} is invalid.
* An alias for {@link Callback#onSurfaceCleanup()} with a less accurate name.
*
* <p>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.
*
* @deprecated Override and use {@link Callback#onSurfaceCleanup()} instead. This method is
* called after the surface has already been destroyed, which is often too late to tell a
* dependency (which might have already scheduled a render) to stop.
* @see <a href="https://github.com/flutter/flutter/issues/160933">#160933</a>.
* @deprecated Override and use {@link Callback#onSurfaceCleanup()} instead.
*/
@Deprecated(since = "Flutter 3.28", forRemoval = true)
default void onSurfaceDestroyed() {}
@ -160,6 +154,8 @@ public interface TextureRegistry {
* Invoked when a {@link Surface} returned by {@link SurfaceProducer#getSurface()} is about
* to become invalid.
*
* <p>When this method is overridden, {@link Callback#onSurfaceDestroyed()} is not called.
*
* <p>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
@ -183,7 +179,9 @@ public interface TextureRegistry {
* }
* </pre>
*/
default void onSurfaceCleanup() {}
default void onSurfaceCleanup() {
onSurfaceDestroyed();
}
}
/** This method is not officially part of the public API surface and will be deprecated. */

View File

@ -767,7 +767,7 @@ public class FlutterRendererTest {
@Test
@SuppressWarnings({"deprecation", "removal"})
public void ImageReaderSurfaceProducerIsDestroyedOnTrimMemory() {
public void ImageReaderSurfaceProducerIsCleanedUpOnTrimMemory() {
FlutterRenderer flutterRenderer = engineRule.getFlutterEngine().getRenderer();
TextureRegistry.SurfaceProducer producer = flutterRenderer.createSurfaceProducer();
@ -781,7 +781,6 @@ public class FlutterRendererTest {
// Verify.
verify(callback).onSurfaceCleanup();
verify(callback).onSurfaceDestroyed();
}
private static class TestSurfaceState {
@ -821,6 +820,23 @@ public class FlutterRendererTest {
assertFalse("Should be destroyed", state.beingDestroyed.isValid());
}
@Test
@SuppressWarnings({"deprecation", "removal"})
public void ImageReaderSurfaceProducerSignalsCleanupCallsDestroy() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
TextureRegistry.SurfaceProducer.Callback callback =
new TextureRegistry.SurfaceProducer.Callback() {
@Override
public void onSurfaceDestroyed() {
latch.countDown();
}
};
// Tests that cleanup, if not provided, just calls destroyed.
callback.onSurfaceCleanup();
latch.await();
}
@Test
@SuppressWarnings({"deprecation", "removal"})
public void ImageReaderSurfaceProducerUnsubscribesWhenReleased() {