Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
// Map of unique IDs to views that render overlay layers.
private final SparseArray<PlatformOverlayView> overlayLayerViews;

// Platform views to be disposed on the next frame.
private ArrayList<Integer> platformViewsToDispose;

// The platform view wrappers that are appended to FlutterView.
//
// These platform views use a TextureLayer in the framework. This is different than
Expand Down Expand Up @@ -239,6 +242,15 @@ public void dispose(int viewId) {
Log.e(TAG, "Disposing unknown platform view with id: " + viewId);
return;
}

// The platform view is displayed using a PlatformViewLayer. Enqueue
// the platform view for deletion during the next frame.
final FlutterMutatorView parentView = platformViewParent.get(viewId);
if (parentView != null) {
platformViewsToDispose.add(viewId);
return;
}

if (platformView.getView() != null) {
final View embeddedView = platformView.getView();
final ViewGroup pvParent = (ViewGroup) embeddedView.getParent();
Expand Down Expand Up @@ -280,18 +292,6 @@ public void dispose(int viewId) {
viewWrappers.remove(viewId);
return;
}
// The platform view is displayed using a PlatformViewLayer.
final FlutterMutatorView parentView = platformViewParent.get(viewId);
if (parentView != null) {
parentView.removeAllViews();
parentView.unsetOnDescendantFocusChangeListener();

final ViewGroup mutatorViewParent = (ViewGroup) parentView.getParent();
if (mutatorViewParent != null) {
mutatorViewParent.removeView(parentView);
}
platformViewParent.remove(viewId);
}
}

@Override
Expand Down Expand Up @@ -744,6 +744,7 @@ public PlatformViewsController() {
viewWrappers = new SparseArray<>();
platformViews = new SparseArray<>();
platformViewParent = new SparseArray<>();
platformViewsToDispose = new ArrayList<>();

motionEventTracker = MotionEventTracker.getInstance();
}
Expand Down Expand Up @@ -1071,6 +1072,10 @@ private void diposeAllViews() {
// Dispose deletes the entry from platformViews and clears associated resources.
channelHandler.dispose(viewId);
}
for (Integer viewId : platformViewsToDispose) {
disposeHybridCompositionPlatformView(viewId);
}
platformViewsToDispose.clear();
}

// Invoked when the Android system is requesting we reduce memory usage.
Expand Down Expand Up @@ -1247,6 +1252,19 @@ public void onBeginFrame() {
* <p>This member is not intended for public use, and is only visible for testing.
*/
public void onEndFrame() {
ArrayList<Integer> nextPlatformViewsToDispose = new ArrayList<>();
for (Integer viewId : platformViewsToDispose) {
// Any platform views that are queued for disposal but still in the composition
// tree must survive at least one more frame, but otherwise can be deleted.
if (currentFrameUsedPlatformViewIds.contains(viewId)) {
nextPlatformViewsToDispose.add(viewId);
continue;
}

disposeHybridCompositionPlatformView(viewId);
}
platformViewsToDispose = nextPlatformViewsToDispose;

// If there are no platform views in the current frame,
// then revert the image view surface and use the previous surface.
//
Expand Down Expand Up @@ -1388,4 +1406,38 @@ private void removeOverlaySurfaces() {
public SparseArray<PlatformOverlayView> getOverlayLayerViews() {
return overlayLayerViews;
}

private void disposeHybridCompositionPlatformView(int viewId) {
final PlatformView platformView = platformViews.get(viewId);
if (platformView != null) {
if (platformView.getView() != null) {
final View embeddedView = platformView.getView();
final ViewGroup pvParent = (ViewGroup) embeddedView.getParent();
if (pvParent != null) {
// Eagerly remove the embedded view from the PlatformViewWrapper.
// Without this call, we see some crashes because removing the view
// is used as a signal to stop processing.
pvParent.removeView(embeddedView);
}
}
platformViews.remove(viewId);
try {
platformView.dispose();
} catch (RuntimeException exception) {
Log.e(TAG, "Disposing platform view threw an exception", exception);
}
}

final FlutterMutatorView parentView = platformViewParent.get(viewId);
if (parentView != null) {
parentView.removeAllViews();
parentView.unsetOnDescendantFocusChangeListener();

final ViewGroup mutatorViewParent = (ViewGroup) parentView.getParent();
if (mutatorViewParent != null) {
mutatorViewParent.removeView(parentView);
}
platformViewParent.remove(viewId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,10 @@ public void disposeAndroidView_hybridComposition() {

// Simulate dispose call from the framework.
disposePlatformView(jni, platformViewsController, platformViewId);
assertNotNull(androidView.getParent());

// pump frame to force disposal.
platformViewsController.onEndFrame();
assertNull(androidView.getParent());

// Simulate create call from the framework.
Expand Down Expand Up @@ -947,6 +951,9 @@ public void disposeNullAndroidView() {

// Simulate dispose call from the framework.
disposePlatformView(jni, platformViewsController, platformViewId);
// pump frame to force disposal.
platformViewsController.onEndFrame();

verify(platformView, times(1)).dispose();
}

Expand Down Expand Up @@ -1090,6 +1097,9 @@ public void onEndFrame_removesPlatformViewParent() {

// Simulate dispose call from the framework.
disposePlatformView(jni, platformViewsController, platformViewId);
platformViewsController.onBeginFrame();
platformViewsController.onEndFrame();

assertEquals(flutterView.getChildCount(), 1);
}

Expand Down