Guard against usage after async callbacks in RenderAndroidView, unregister listener (#108496)

This commit is contained in:
Dan Field 2022-07-28 11:36:04 -07:00 committed by GitHub
parent b1881487be
commit 0cc7db54ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 3 deletions

View file

@ -109,6 +109,7 @@ class RenderAndroidView extends PlatformViewRenderBox {
/// Sets a new Android view controller.
@override
set controller(AndroidViewController controller) {
assert(!_isDisposed);
assert(_viewController != null);
assert(controller != null);
if (_viewController == controller) {
@ -140,6 +141,7 @@ class RenderAndroidView extends PlatformViewRenderBox {
}
void _onPlatformViewCreated(int id) {
assert(!_isDisposed);
markNeedsSemanticsUpdate();
}
@ -179,8 +181,14 @@ class RenderAndroidView extends PlatformViewRenderBox {
targetSize = size;
if (_viewController.isCreated) {
_currentTextureSize = await _viewController.setSize(targetSize);
if (_isDisposed) {
return;
}
} else {
await _viewController.create(size: targetSize);
if (_isDisposed) {
return;
}
_currentTextureSize = targetSize;
}
// We've resized the platform view to targetSize, but it is possible that
@ -248,6 +256,7 @@ class RenderAndroidView extends PlatformViewRenderBox {
void dispose() {
_isDisposed = true;
_clipRectLayer.layer = null;
_viewController.removeOnPlatformViewCreatedListener(_onPlatformViewCreated);
super.dispose();
}

View file

@ -221,6 +221,45 @@ void main() {
expect(renderBox.debugLayer!.firstChild, isA<TextureLayer>());
});
});
test('markNeedsPaint does not get called on a disposed RO', () async {
FakeAsync().run((FakeAsync async) {
final AndroidViewController viewController =
PlatformViewsService.initAndroidView(id: 0, viewType: 'webview', layoutDirection: TextDirection.rtl);
final RenderAndroidView renderBox = RenderAndroidView(
viewController: viewController,
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{},
);
final Completer<void> viewCreation = Completer<void>();
const MethodChannel channel = MethodChannel('flutter/platform_views');
binding.defaultBinaryMessenger.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
assert(methodCall.method == 'create', 'Unexpected method call');
await viewCreation.future;
return /*textureId=*/ 0;
});
layout(renderBox);
pumpFrame(phase: EnginePhase.paint);
expect(renderBox.debugLayer, isNotNull);
expect(renderBox.debugLayer!.hasChildren, isFalse);
expect(viewController.isCreated, isFalse);
expect(renderBox.debugNeedsPaint, isFalse);
renderBox.dispose();
viewCreation.complete();
async.flushMicrotasks();
expect(viewController.isCreated, isTrue);
expect(renderBox.debugNeedsPaint, isFalse);
expect(renderBox.debugLayer, isNull);
pumpFrame(phase: EnginePhase.paint);
expect(renderBox.debugLayer, isNull);
});
});
}
ui.PointerData _pointerData(

View file

@ -101,9 +101,7 @@ class FakeAndroidViewController implements AndroidViewController {
}
@override
void removeOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) {
throw UnimplementedError();
}
void removeOnPlatformViewCreatedListener(PlatformViewCreatedCallback listener) {}
@override
Future<void> sendMotionEvent(AndroidMotionEvent event) {