Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 4dd7eb7

Browse files
author
Emmanuel Garcia
authored
Don't remove views while the rasterizer is torn down (#31229)
1 parent 1313aa8 commit 4dd7eb7

File tree

2 files changed

+65
-20
lines changed

2 files changed

+65
-20
lines changed

shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ public void attachToView(@NonNull FlutterView flutterView) {
508508
*/
509509
public void detachFromView() {
510510
destroyOverlaySurfaces();
511+
removeOverlaySurfaces();
511512
this.flutterView = null;
512513
flutterViewConvertedToImageView = false;
513514

@@ -970,17 +971,26 @@ public FlutterOverlaySurface createOverlaySurface() {
970971
* Destroys the overlay surfaces and removes them from the view hierarchy.
971972
*
972973
* <p>This method is used only internally by {@code FlutterJNI}.
973-
*
974-
* <p>This member is not intended for public use, and is only visible for testing.
975974
*/
976975
public void destroyOverlaySurfaces() {
977976
for (int i = 0; i < overlayLayerViews.size(); i++) {
978-
FlutterImageView overlayView = overlayLayerViews.valueAt(i);
977+
final FlutterImageView overlayView = overlayLayerViews.valueAt(i);
979978
overlayView.detachFromRenderer();
980979
overlayView.closeImageReader();
981-
if (flutterView != null) {
982-
flutterView.removeView(overlayView);
983-
}
980+
// Don't remove overlayView from the view hierarchy since this method can
981+
// be called while the Android framework is iterating over the array of views.
982+
// See ViewGroup#dispatchDetachedFromWindow(), and
983+
// https://github.com/flutter/flutter/issues/97679.
984+
}
985+
}
986+
987+
private void removeOverlaySurfaces() {
988+
if (flutterView == null) {
989+
Log.e(TAG, "removeOverlaySurfaces called while flutter view is null");
990+
return;
991+
}
992+
for (int i = 0; i < overlayLayerViews.size(); i++) {
993+
flutterView.removeView(overlayLayerViews.valueAt(i));
984994
}
985995
overlayLayerViews.clear();
986996
}

shell/platform/android/test/io/flutter/plugin/platform/PlatformViewsControllerTest.java

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -657,17 +657,13 @@ public void detach__destroysOverlaySurfaces() {
657657

658658
platformViewsController.detach();
659659

660-
assertThrows(
661-
IllegalStateException.class,
662-
() -> {
663-
platformViewsController.onDisplayOverlaySurface(
664-
overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10);
665-
});
660+
verify(overlayImageView, times(1)).closeImageReader();
661+
verify(overlayImageView, times(1)).detachFromRenderer();
666662
}
667663

668664
@Test
669665
@Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
670-
public void detachFromView__removesOverlaySurfaces() {
666+
public void detachFromView__removesAndDestroysOverlayViews() {
671667
final PlatformViewsController platformViewsController = new PlatformViewsController();
672668

673669
final int platformViewId = 0;
@@ -684,6 +680,9 @@ public void detachFromView__removesOverlaySurfaces() {
684680
jni.attachToNative();
685681
attach(jni, platformViewsController);
686682

683+
final FlutterView flutterView = mock(FlutterView.class);
684+
platformViewsController.attachToView(flutterView);
685+
687686
final FlutterImageView overlayImageView = mock(FlutterImageView.class);
688687
when(overlayImageView.acquireLatestImage()).thenReturn(true);
689688

@@ -695,17 +694,14 @@ public void detachFromView__removesOverlaySurfaces() {
695694

696695
platformViewsController.detachFromView();
697696

698-
assertThrows(
699-
IllegalStateException.class,
700-
() -> {
701-
platformViewsController.onDisplayOverlaySurface(
702-
overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10);
703-
});
697+
verify(overlayImageView, times(1)).closeImageReader();
698+
verify(overlayImageView, times(1)).detachFromRenderer();
699+
verify(flutterView, times(1)).removeView(overlayImageView);
704700
}
705701

706702
@Test
707703
@Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
708-
public void destroyOverlaySurfaces__doesNotThrowIfControllerIsDetached() {
704+
public void destroyOverlaySurfaces__doesNotThrowIfFlutterViewIsDetached() {
709705
final PlatformViewsController platformViewsController = new PlatformViewsController();
710706

711707
final int platformViewId = 0;
@@ -722,6 +718,9 @@ public void destroyOverlaySurfaces__doesNotThrowIfControllerIsDetached() {
722718
jni.attachToNative();
723719
attach(jni, platformViewsController);
724720

721+
final FlutterView flutterView = mock(FlutterView.class);
722+
platformViewsController.attachToView(flutterView);
723+
725724
final FlutterImageView overlayImageView = mock(FlutterImageView.class);
726725
when(overlayImageView.acquireLatestImage()).thenReturn(true);
727726

@@ -735,6 +734,42 @@ public void destroyOverlaySurfaces__doesNotThrowIfControllerIsDetached() {
735734

736735
platformViewsController.destroyOverlaySurfaces();
737736
verify(overlayImageView, times(1)).closeImageReader();
737+
verify(overlayImageView, times(1)).detachFromRenderer();
738+
}
739+
740+
@Test
741+
@Config(shadows = {ShadowFlutterSurfaceView.class, ShadowFlutterJNI.class})
742+
public void destroyOverlaySurfaces__doesNotRemoveOverlayView() {
743+
final PlatformViewsController platformViewsController = new PlatformViewsController();
744+
745+
final int platformViewId = 0;
746+
assertNull(platformViewsController.getPlatformViewById(platformViewId));
747+
748+
final PlatformViewFactory viewFactory = mock(PlatformViewFactory.class);
749+
final PlatformView platformView = mock(PlatformView.class);
750+
when(platformView.getView()).thenReturn(mock(View.class));
751+
when(viewFactory.create(any(), eq(platformViewId), any())).thenReturn(platformView);
752+
753+
platformViewsController.getRegistry().registerViewFactory("testType", viewFactory);
754+
755+
final FlutterJNI jni = new FlutterJNI();
756+
jni.attachToNative();
757+
attach(jni, platformViewsController);
758+
759+
final FlutterView flutterView = mock(FlutterView.class);
760+
platformViewsController.attachToView(flutterView);
761+
762+
final FlutterImageView overlayImageView = mock(FlutterImageView.class);
763+
when(overlayImageView.acquireLatestImage()).thenReturn(true);
764+
765+
final FlutterOverlaySurface overlaySurface =
766+
platformViewsController.createOverlaySurface(overlayImageView);
767+
768+
platformViewsController.onDisplayOverlaySurface(
769+
overlaySurface.getId(), /* x=*/ 0, /* y=*/ 0, /* width=*/ 10, /* height=*/ 10);
770+
771+
platformViewsController.destroyOverlaySurfaces();
772+
verify(flutterView, never()).removeView(overlayImageView);
738773
}
739774

740775
@Test

0 commit comments

Comments
 (0)