Skip to content

Commit 1896f10

Browse files
[webview_flutter_wkwebview][webview_flutter_android] Fixes bug where the WebViews could not be released (flutter#6996)
* fix and test non disposing webview * combine boolean * version bump * more pumpAndSettles * add more pumpAndSettles * update int tests * android test * fix version bump and unchange compile files * make _currentNavigationDelegate nullable * quick fix * accidental letter * small tests fixes * missing hashtag
1 parent 5dd0f41 commit 1896f10

File tree

12 files changed

+492
-154
lines changed

12 files changed

+492
-154
lines changed

packages/webview_flutter/webview_flutter_android/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 3.2.3
2+
3+
* Fixes bug that prevented the web view from being garbage collected.
4+
* Fixes bug causing a `LateInitializationError` when a `PlatformNavigationDelegate` is not provided.
5+
16
## 3.2.2
27

38
* Updates example code for `use_build_context_synchronously` lint.

packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart

Lines changed: 145 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import 'package:flutter/material.dart';
1717
import 'package:flutter/services.dart';
1818
import 'package:flutter_test/flutter_test.dart';
1919
import 'package:integration_test/integration_test.dart';
20+
import 'package:webview_flutter_android/src/android_webview.dart' as android;
21+
import 'package:webview_flutter_android/src/android_webview_api_impls.dart';
2022
import 'package:webview_flutter_android/src/instance_manager.dart';
2123
import 'package:webview_flutter_android/src/weak_reference_utils.dart';
2224
import 'package:webview_flutter_android/webview_flutter_android.dart';
@@ -58,15 +60,13 @@ Future<void> main() async {
5860
)
5961
..loadRequest(LoadRequestParams(uri: Uri.parse(primaryUrl)));
6062

61-
await tester.pumpWidget(
62-
Builder(
63-
builder: (BuildContext context) {
64-
return PlatformWebViewWidget(
65-
PlatformWebViewWidgetCreationParams(controller: controller),
66-
).build(context);
67-
},
68-
),
69-
);
63+
await tester.pumpWidget(Builder(
64+
builder: (BuildContext context) {
65+
return PlatformWebViewWidget(
66+
PlatformWebViewWidgetCreationParams(controller: controller),
67+
).build(context);
68+
},
69+
));
7070

7171
await pageFinished.future;
7272

@@ -96,6 +96,79 @@ Future<void> main() async {
9696
expect(gcIdentifier, 0);
9797
}, timeout: const Timeout(Duration(seconds: 10)));
9898

99+
testWidgets(
100+
'WebView is released by garbage collection',
101+
(WidgetTester tester) async {
102+
final Completer<void> webViewGCCompleter = Completer<void>();
103+
104+
late final InstanceManager instanceManager;
105+
instanceManager =
106+
InstanceManager(onWeakReferenceRemoved: (int identifier) {
107+
final Copyable instance =
108+
instanceManager.getInstanceWithWeakReference(identifier)!;
109+
if (instance is android.WebView && !webViewGCCompleter.isCompleted) {
110+
webViewGCCompleter.complete();
111+
}
112+
});
113+
114+
android.WebView.api = WebViewHostApiImpl(
115+
instanceManager: instanceManager,
116+
);
117+
android.WebSettings.api = WebSettingsHostApiImpl(
118+
instanceManager: instanceManager,
119+
);
120+
android.WebChromeClient.api = WebChromeClientHostApiImpl(
121+
instanceManager: instanceManager,
122+
);
123+
124+
await tester.pumpWidget(
125+
Builder(
126+
builder: (BuildContext context) {
127+
return PlatformWebViewWidget(
128+
AndroidWebViewWidgetCreationParams(
129+
instanceManager: instanceManager,
130+
controller: PlatformWebViewController(
131+
const PlatformWebViewControllerCreationParams(),
132+
),
133+
),
134+
).build(context);
135+
},
136+
),
137+
);
138+
await tester.pumpAndSettle();
139+
140+
await tester.pumpWidget(
141+
Builder(
142+
builder: (BuildContext context) {
143+
return PlatformWebViewWidget(
144+
AndroidWebViewWidgetCreationParams(
145+
instanceManager: instanceManager,
146+
controller: PlatformWebViewController(
147+
const PlatformWebViewControllerCreationParams(),
148+
),
149+
),
150+
).build(context);
151+
},
152+
),
153+
);
154+
await tester.pumpAndSettle();
155+
156+
// Force garbage collection.
157+
await IntegrationTestWidgetsFlutterBinding.instance
158+
.watchPerformance(() async {
159+
await tester.pumpAndSettle();
160+
});
161+
162+
await tester.pumpAndSettle();
163+
await expectLater(webViewGCCompleter.future, completes);
164+
165+
android.WebView.api = WebViewHostApiImpl();
166+
android.WebSettings.api = WebSettingsHostApiImpl();
167+
android.WebChromeClient.api = WebChromeClientHostApiImpl();
168+
},
169+
timeout: const Timeout(Duration(seconds: 10)),
170+
);
171+
99172
testWidgets('runJavaScriptReturningResult', (WidgetTester tester) async {
100173
final Completer<void> pageFinished = Completer<void>();
101174

@@ -110,15 +183,13 @@ Future<void> main() async {
110183
)
111184
..loadRequest(LoadRequestParams(uri: Uri.parse(primaryUrl)));
112185

113-
await tester.pumpWidget(
114-
Builder(
115-
builder: (BuildContext context) {
116-
return PlatformWebViewWidget(
117-
PlatformWebViewWidgetCreationParams(controller: controller),
118-
).build(context);
119-
},
120-
),
121-
);
186+
await tester.pumpWidget(Builder(
187+
builder: (BuildContext context) {
188+
return PlatformWebViewWidget(
189+
PlatformWebViewWidgetCreationParams(controller: controller),
190+
).build(context);
191+
},
192+
));
122193

123194
await pageFinished.future;
124195

@@ -151,15 +222,13 @@ Future<void> main() async {
151222
),
152223
);
153224

154-
await tester.pumpWidget(
155-
Builder(
156-
builder: (BuildContext context) {
157-
return PlatformWebViewWidget(
158-
PlatformWebViewWidgetCreationParams(controller: controller),
159-
).build(context);
160-
},
161-
),
162-
);
225+
await tester.pumpWidget(Builder(
226+
builder: (BuildContext context) {
227+
return PlatformWebViewWidget(
228+
PlatformWebViewWidgetCreationParams(controller: controller),
229+
).build(context);
230+
},
231+
));
163232

164233
await pageLoads.stream.firstWhere((String url) => url == headersUrl);
165234

@@ -195,15 +264,13 @@ Future<void> main() async {
195264
'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
196265
);
197266

198-
await tester.pumpWidget(
199-
Builder(
200-
builder: (BuildContext context) {
201-
return PlatformWebViewWidget(
202-
PlatformWebViewWidgetCreationParams(controller: controller),
203-
).build(context);
204-
},
205-
),
206-
);
267+
await tester.pumpWidget(Builder(
268+
builder: (BuildContext context) {
269+
return PlatformWebViewWidget(
270+
PlatformWebViewWidgetCreationParams(controller: controller),
271+
).build(context);
272+
},
273+
));
207274

208275
await pageFinished.future;
209276

@@ -258,15 +325,13 @@ Future<void> main() async {
258325
..setUserAgent('Custom_User_Agent1')
259326
..loadRequest(LoadRequestParams(uri: Uri.parse('about:blank')));
260327

261-
await tester.pumpWidget(
262-
Builder(
263-
builder: (BuildContext context) {
264-
return PlatformWebViewWidget(
265-
PlatformWebViewWidgetCreationParams(controller: controller),
266-
).build(context);
267-
},
268-
),
269-
);
328+
await tester.pumpWidget(Builder(
329+
builder: (BuildContext context) {
330+
return PlatformWebViewWidget(
331+
PlatformWebViewWidgetCreationParams(controller: controller),
332+
).build(context);
333+
},
334+
));
270335

271336
await pageFinished.future;
272337

@@ -335,15 +400,13 @@ Future<void> main() async {
335400
),
336401
);
337402

338-
await tester.pumpWidget(
339-
Builder(
340-
builder: (BuildContext context) {
341-
return PlatformWebViewWidget(
342-
PlatformWebViewWidgetCreationParams(controller: controller),
343-
).build(context);
344-
},
345-
),
346-
);
403+
await tester.pumpWidget(Builder(
404+
builder: (BuildContext context) {
405+
return PlatformWebViewWidget(
406+
PlatformWebViewWidgetCreationParams(controller: controller),
407+
).build(context);
408+
},
409+
));
347410

348411
await pageLoaded.future;
349412

@@ -369,15 +432,13 @@ Future<void> main() async {
369432
),
370433
);
371434

372-
await tester.pumpWidget(
373-
Builder(
374-
builder: (BuildContext context) {
375-
return PlatformWebViewWidget(
376-
PlatformWebViewWidgetCreationParams(controller: controller),
377-
).build(context);
378-
},
379-
),
380-
);
435+
await tester.pumpWidget(Builder(
436+
builder: (BuildContext context) {
437+
return PlatformWebViewWidget(
438+
PlatformWebViewWidgetCreationParams(controller: controller),
439+
).build(context);
440+
},
441+
));
381442

382443
await pageLoaded.future;
383444

@@ -491,15 +552,13 @@ Future<void> main() async {
491552
),
492553
);
493554

494-
await tester.pumpWidget(
495-
Builder(
496-
builder: (BuildContext context) {
497-
return PlatformWebViewWidget(
498-
PlatformWebViewWidgetCreationParams(controller: controller),
499-
).build(context);
500-
},
501-
),
502-
);
555+
await tester.pumpWidget(Builder(
556+
builder: (BuildContext context) {
557+
return PlatformWebViewWidget(
558+
PlatformWebViewWidgetCreationParams(controller: controller),
559+
).build(context);
560+
},
561+
));
503562

504563
await pageLoaded.future;
505564

@@ -525,15 +584,13 @@ Future<void> main() async {
525584
),
526585
);
527586

528-
await tester.pumpWidget(
529-
Builder(
530-
builder: (BuildContext context) {
531-
return PlatformWebViewWidget(
532-
PlatformWebViewWidgetCreationParams(controller: controller),
533-
).build(context);
534-
},
535-
),
536-
);
587+
await tester.pumpWidget(Builder(
588+
builder: (BuildContext context) {
589+
return PlatformWebViewWidget(
590+
PlatformWebViewWidgetCreationParams(controller: controller),
591+
).build(context);
592+
},
593+
));
537594

538595
await pageLoaded.future;
539596

@@ -573,15 +630,13 @@ Future<void> main() async {
573630
),
574631
);
575632

576-
await tester.pumpWidget(
577-
Builder(
578-
builder: (BuildContext context) {
579-
return PlatformWebViewWidget(
580-
PlatformWebViewWidgetCreationParams(controller: controller),
581-
).build(context);
582-
},
583-
),
584-
);
633+
await tester.pumpWidget(Builder(
634+
builder: (BuildContext context) {
635+
return PlatformWebViewWidget(
636+
PlatformWebViewWidgetCreationParams(controller: controller),
637+
).build(context);
638+
},
639+
));
585640

586641
await pageLoaded.future;
587642

packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -96,28 +96,30 @@ class AndroidWebViewController extends PlatformWebViewController {
9696
);
9797

9898
late final android_webview.WebChromeClient _webChromeClient =
99-
withWeakReferenceTo(this,
100-
(WeakReference<AndroidWebViewController> weakReference) {
101-
return _androidWebViewParams.androidWebViewProxy
102-
.createAndroidWebChromeClient(
103-
onProgressChanged: (android_webview.WebView webView, int progress) {
104-
if (weakReference.target?._currentNavigationDelegate._onProgress !=
99+
_androidWebViewParams.androidWebViewProxy.createAndroidWebChromeClient(
100+
onProgressChanged: withWeakReferenceTo(this,
101+
(WeakReference<AndroidWebViewController> weakReference) {
102+
return (android_webview.WebView webView, int progress) {
103+
if (weakReference.target?._currentNavigationDelegate?._onProgress !=
105104
null) {
106105
weakReference
107-
.target!._currentNavigationDelegate._onProgress!(progress);
106+
.target!._currentNavigationDelegate!._onProgress!(progress);
108107
}
109-
},
110-
onShowFileChooser: (android_webview.WebView webView,
108+
};
109+
}),
110+
onShowFileChooser: withWeakReferenceTo(this,
111+
(WeakReference<AndroidWebViewController> weakReference) {
112+
return (android_webview.WebView webView,
111113
android_webview.FileChooserParams params) async {
112114
if (weakReference.target?._onShowFileSelectorCallback != null) {
113115
return weakReference.target!._onShowFileSelectorCallback!(
114116
FileSelectorParams._fromFileChooserParams(params),
115117
);
116118
}
117119
return <String>[];
118-
},
119-
);
120-
});
120+
};
121+
}),
122+
);
121123

122124
/// The native [android_webview.FlutterAssetManager] allows managing assets.
123125
late final android_webview.FlutterAssetManager _flutterAssetManager =
@@ -126,10 +128,7 @@ class AndroidWebViewController extends PlatformWebViewController {
126128
final Map<String, AndroidJavaScriptChannelParams> _javaScriptChannelParams =
127129
<String, AndroidJavaScriptChannelParams>{};
128130

129-
// The keeps a reference to the current NavigationDelegate so that the
130-
// callback methods remain reachable.
131-
// ignore: unused_field
132-
late AndroidNavigationDelegate _currentNavigationDelegate;
131+
AndroidNavigationDelegate? _currentNavigationDelegate;
133132

134133
Future<List<String>> Function(FileSelectorParams)?
135134
_onShowFileSelectorCallback;

packages/webview_flutter/webview_flutter_android/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: webview_flutter_android
22
description: A Flutter plugin that provides a WebView widget on Android.
33
repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
5-
version: 3.2.2
5+
version: 3.2.3
66

77
environment:
88
sdk: ">=2.17.0 <3.0.0"

0 commit comments

Comments
 (0)