Ensure that unregisterTexture is called when forget to call SurfaceTextureEntry.release (flutter/engine#28304)

This commit is contained in:
ColdPaleLight 2021-08-31 10:11:01 +08:00 committed by GitHub
parent 4ac725f85f
commit 198f96cd09
2 changed files with 112 additions and 0 deletions

View File

@ -39,6 +39,7 @@ public class FlutterRenderer implements TextureRegistry {
@NonNull private final AtomicLong nextTextureId = new AtomicLong(0L);
@Nullable private Surface surface;
private boolean isDisplayingFlutterUi = false;
private Handler handler = new Handler();
@NonNull
private final FlutterUiDisplayListener flutterUiDisplayListener =
@ -168,6 +169,38 @@ public class FlutterRenderer implements TextureRegistry {
unregisterTexture(id);
released = true;
}
@Override
protected void finalize() throws Throwable {
try {
if (released) {
return;
}
handler.post(new SurfaceTextureFinalizerRunnable(id, flutterJNI));
} finally {
super.finalize();
}
}
}
static final class SurfaceTextureFinalizerRunnable implements Runnable {
private final long id;
private final FlutterJNI flutterJNI;
SurfaceTextureFinalizerRunnable(long id, @NonNull FlutterJNI flutterJNI) {
this.id = id;
this.flutterJNI = flutterJNI;
}
@Override
public void run() {
if (!flutterJNI.isAttached()) {
return;
}
Log.v(TAG, "Releasing a SurfaceTexture (" + id + ").");
flutterJNI.unregisterTexture(id);
}
}
// ------ END TextureRegistry IMPLEMENTATION ----

View File

@ -4,9 +4,14 @@ import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.os.Looper;
import android.view.Surface;
import io.flutter.embedding.engine.FlutterJNI;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -116,4 +121,78 @@ public class FlutterRendererTest {
// Verify behavior under test.
verify(fakeFlutterJNI, times(0)).markTextureFrameAvailable(eq(entry.id()));
}
@Test
public void itUnregistersTextureWhenSurfaceTextureFinalized() {
// Setup the test.
FlutterJNI fakeFlutterJNI = mock(FlutterJNI.class);
when(fakeFlutterJNI.isAttached()).thenReturn(true);
FlutterRenderer flutterRenderer = new FlutterRenderer(fakeFlutterJNI);
fakeFlutterJNI.detachFromNativeAndReleaseResources();
FlutterRenderer.SurfaceTextureRegistryEntry entry =
(FlutterRenderer.SurfaceTextureRegistryEntry) flutterRenderer.createSurfaceTexture();
long id = entry.id();
flutterRenderer.startRenderingToSurface(fakeSurface);
// Execute the behavior under test.
runFinalization(entry);
shadowOf(Looper.getMainLooper()).idle();
flutterRenderer.stopRenderingToSurface();
// Verify behavior under test.
verify(fakeFlutterJNI, times(1)).unregisterTexture(eq(id));
}
@Test
public void itStopsUnregisteringTextureWhenDetached() {
// Setup the test.
FlutterJNI fakeFlutterJNI = mock(FlutterJNI.class);
when(fakeFlutterJNI.isAttached()).thenReturn(false);
FlutterRenderer flutterRenderer = new FlutterRenderer(fakeFlutterJNI);
fakeFlutterJNI.detachFromNativeAndReleaseResources();
FlutterRenderer.SurfaceTextureRegistryEntry entry =
(FlutterRenderer.SurfaceTextureRegistryEntry) flutterRenderer.createSurfaceTexture();
long id = entry.id();
flutterRenderer.startRenderingToSurface(fakeSurface);
flutterRenderer.stopRenderingToSurface();
// Execute the behavior under test.
runFinalization(entry);
shadowOf(Looper.getMainLooper()).idle();
// Verify behavior under test.
verify(fakeFlutterJNI, times(0)).unregisterTexture(eq(id));
}
void runFinalization(FlutterRenderer.SurfaceTextureRegistryEntry entry) {
CountDownLatch latch = new CountDownLatch(1);
Thread fakeFinalizer =
new Thread(
new Runnable() {
public void run() {
try {
entry.finalize();
latch.countDown();
} catch (Throwable e) {
// do nothing
}
}
});
fakeFinalizer.start();
try {
latch.await(5L, TimeUnit.SECONDS);
} catch (Throwable e) {
// do nothing
}
}
}