[webview_flutter_android] Updates the Dart InstanceManager to take a listener for when an object is garbage collected (#6148)

diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
index 5706d65..dc9850e 100644
--- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 2.9.3
+
+* Updates the Dart InstanceManager to take a listener for when an object is garbage collected.
+  See https://github.com/flutter/flutter/issues/107199.
+
 ## 2.9.2
 
 * Updates the Java InstanceManager to take a listener for when an object is garbage collected.
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
index 005e281..f2e305f 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
@@ -28,6 +28,11 @@
   /// This should only be used by subclasses created by this library or to
   /// create copies.
   JavaObject.detached();
+
+  /// Global instance of [InstanceManager].
+  static final InstanceManager globalInstanceManager = InstanceManager(
+    onWeakReferenceRemoved: (_) {},
+  );
 }
 
 /// An Android View that displays web pages.
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
index b1268f4..c3e2096 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
@@ -88,28 +88,27 @@
   WebViewHostApiImpl({
     BinaryMessenger? binaryMessenger,
     InstanceManager? instanceManager,
-  }) : super(binaryMessenger: binaryMessenger) {
-    this.instanceManager = instanceManager ?? InstanceManager.instance;
-  }
+  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+        super(binaryMessenger: binaryMessenger);
 
   /// Maintains instances stored to communicate with java objects.
-  late final InstanceManager instanceManager;
+  final InstanceManager instanceManager;
 
   /// Helper method to convert instances ids to objects.
-  Future<void> createFromInstance(WebView instance) async {
-    final int? instanceId = instanceManager.tryAddInstance(instance);
-    if (instanceId != null) {
-      return create(instanceId, instance.useHybridComposition);
-    }
+  Future<void> createFromInstance(WebView instance) {
+    return create(
+      instanceManager.addDartCreatedInstance(instance),
+      instance.useHybridComposition,
+    );
   }
 
   /// Helper method to convert instances ids to objects.
   Future<void> disposeFromInstance(WebView instance) async {
-    final int? instanceId = instanceManager.getInstanceId(instance);
+    final int? instanceId = instanceManager.getIdentifier(instance);
     if (instanceId != null) {
+      instanceManager.remove(instanceId);
       await dispose(instanceId);
     }
-    instanceManager.removeInstance(instance);
   }
 
   /// Helper method to convert the instances ids to objects.
@@ -120,7 +119,7 @@
     String? encoding,
   ) {
     return loadData(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       data,
       mimeType,
       encoding,
@@ -137,7 +136,7 @@
     String? historyUrl,
   ) {
     return loadDataWithBaseUrl(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       baseUrl,
       data,
       mimeType,
@@ -152,7 +151,7 @@
     String url,
     Map<String, String> headers,
   ) {
-    return loadUrl(instanceManager.getInstanceId(instance)!, url, headers);
+    return loadUrl(instanceManager.getIdentifier(instance)!, url, headers);
   }
 
   /// Helper method to convert instances ids to objects.
@@ -161,43 +160,43 @@
     String url,
     Uint8List data,
   ) {
-    return postUrl(instanceManager.getInstanceId(instance)!, url, data);
+    return postUrl(instanceManager.getIdentifier(instance)!, url, data);
   }
 
   /// Helper method to convert instances ids to objects.
   Future<String?> getUrlFromInstance(WebView instance) {
-    return getUrl(instanceManager.getInstanceId(instance)!);
+    return getUrl(instanceManager.getIdentifier(instance)!);
   }
 
   /// Helper method to convert instances ids to objects.
   Future<bool> canGoBackFromInstance(WebView instance) {
-    return canGoBack(instanceManager.getInstanceId(instance)!);
+    return canGoBack(instanceManager.getIdentifier(instance)!);
   }
 
   /// Helper method to convert instances ids to objects.
   Future<bool> canGoForwardFromInstance(WebView instance) {
-    return canGoForward(instanceManager.getInstanceId(instance)!);
+    return canGoForward(instanceManager.getIdentifier(instance)!);
   }
 
   /// Helper method to convert instances ids to objects.
   Future<void> goBackFromInstance(WebView instance) {
-    return goBack(instanceManager.getInstanceId(instance)!);
+    return goBack(instanceManager.getIdentifier(instance)!);
   }
 
   /// Helper method to convert instances ids to objects.
   Future<void> goForwardFromInstance(WebView instance) {
-    return goForward(instanceManager.getInstanceId(instance)!);
+    return goForward(instanceManager.getIdentifier(instance)!);
   }
 
   /// Helper method to convert instances ids to objects.
   Future<void> reloadFromInstance(WebView instance) {
-    return reload(instanceManager.getInstanceId(instance)!);
+    return reload(instanceManager.getIdentifier(instance)!);
   }
 
   /// Helper method to convert instances ids to objects.
   Future<void> clearCacheFromInstance(WebView instance, bool includeDiskFiles) {
     return clearCache(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       includeDiskFiles,
     );
   }
@@ -208,34 +207,34 @@
     String javascriptString,
   ) {
     return evaluateJavascript(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       javascriptString,
     );
   }
 
   /// Helper method to convert instances ids to objects.
   Future<String?> getTitleFromInstance(WebView instance) {
-    return getTitle(instanceManager.getInstanceId(instance)!);
+    return getTitle(instanceManager.getIdentifier(instance)!);
   }
 
   /// Helper method to convert instances ids to objects.
   Future<void> scrollToFromInstance(WebView instance, int x, int y) {
-    return scrollTo(instanceManager.getInstanceId(instance)!, x, y);
+    return scrollTo(instanceManager.getIdentifier(instance)!, x, y);
   }
 
   /// Helper method to convert instances ids to objects.
   Future<void> scrollByFromInstance(WebView instance, int x, int y) {
-    return scrollBy(instanceManager.getInstanceId(instance)!, x, y);
+    return scrollBy(instanceManager.getIdentifier(instance)!, x, y);
   }
 
   /// Helper method to convert instances ids to objects.
   Future<int> getScrollXFromInstance(WebView instance) {
-    return getScrollX(instanceManager.getInstanceId(instance)!);
+    return getScrollX(instanceManager.getIdentifier(instance)!);
   }
 
   /// Helper method to convert instances ids to objects.
   Future<int> getScrollYFromInstance(WebView instance) {
-    return getScrollY(instanceManager.getInstanceId(instance)!);
+    return getScrollY(instanceManager.getIdentifier(instance)!);
   }
 
   /// Helper method to convert instances ids to objects.
@@ -244,8 +243,8 @@
     WebViewClient webViewClient,
   ) {
     return setWebViewClient(
-      instanceManager.getInstanceId(instance)!,
-      instanceManager.getInstanceId(webViewClient)!,
+      instanceManager.getIdentifier(instance)!,
+      instanceManager.getIdentifier(webViewClient)!,
     );
   }
 
@@ -255,8 +254,8 @@
     JavaScriptChannel javaScriptChannel,
   ) {
     return addJavaScriptChannel(
-      instanceManager.getInstanceId(instance)!,
-      instanceManager.getInstanceId(javaScriptChannel)!,
+      instanceManager.getIdentifier(instance)!,
+      instanceManager.getIdentifier(javaScriptChannel)!,
     );
   }
 
@@ -266,8 +265,8 @@
     JavaScriptChannel javaScriptChannel,
   ) {
     return removeJavaScriptChannel(
-      instanceManager.getInstanceId(instance)!,
-      instanceManager.getInstanceId(javaScriptChannel)!,
+      instanceManager.getIdentifier(instance)!,
+      instanceManager.getIdentifier(javaScriptChannel)!,
     );
   }
 
@@ -277,8 +276,8 @@
     DownloadListener? listener,
   ) {
     return setDownloadListener(
-      instanceManager.getInstanceId(instance)!,
-      listener != null ? instanceManager.getInstanceId(listener) : null,
+      instanceManager.getIdentifier(instance)!,
+      listener != null ? instanceManager.getIdentifier(listener) : null,
     );
   }
 
@@ -288,14 +287,14 @@
     WebChromeClient? client,
   ) {
     return setWebChromeClient(
-      instanceManager.getInstanceId(instance)!,
-      client != null ? instanceManager.getInstanceId(client) : null,
+      instanceManager.getIdentifier(instance)!,
+      client != null ? instanceManager.getIdentifier(client) : null,
     );
   }
 
   /// Helper method to convert instances ids to objects.
   Future<void> setBackgroundColorFromInstance(WebView instance, int color) {
-    return setBackgroundColor(instanceManager.getInstanceId(instance)!, color);
+    return setBackgroundColor(instanceManager.getIdentifier(instance)!, color);
   }
 }
 
@@ -305,28 +304,25 @@
   WebSettingsHostApiImpl({
     BinaryMessenger? binaryMessenger,
     InstanceManager? instanceManager,
-  }) : super(binaryMessenger: binaryMessenger) {
-    this.instanceManager = instanceManager ?? InstanceManager.instance;
-  }
+  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+        super(binaryMessenger: binaryMessenger);
 
   /// Maintains instances stored to communicate with java objects.
-  late final InstanceManager instanceManager;
+  final InstanceManager instanceManager;
 
   /// Helper method to convert instances ids to objects.
-  Future<void> createFromInstance(WebSettings instance, WebView webView) async {
-    final int? instanceId = instanceManager.tryAddInstance(instance);
-    if (instanceId != null) {
-      return create(
-        instanceId,
-        instanceManager.getInstanceId(webView)!,
-      );
-    }
+  Future<void> createFromInstance(WebSettings instance, WebView webView) {
+    return create(
+      instanceManager.addDartCreatedInstance(instance),
+      instanceManager.getIdentifier(webView)!,
+    );
   }
 
   /// Helper method to convert instances ids to objects.
   Future<void> disposeFromInstance(WebSettings instance) async {
-    final int? instanceId = instanceManager.removeInstance(instance);
+    final int? instanceId = instanceManager.getIdentifier(instance);
     if (instanceId != null) {
+      instanceManager.remove(instanceId);
       return dispose(instanceId);
     }
   }
@@ -336,7 +332,7 @@
     WebSettings instance,
     bool flag,
   ) {
-    return setDomStorageEnabled(instanceManager.getInstanceId(instance)!, flag);
+    return setDomStorageEnabled(instanceManager.getIdentifier(instance)!, flag);
   }
 
   /// Helper method to convert instances ids to objects.
@@ -345,7 +341,7 @@
     bool flag,
   ) {
     return setJavaScriptCanOpenWindowsAutomatically(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       flag,
     );
   }
@@ -356,7 +352,7 @@
     bool support,
   ) {
     return setSupportMultipleWindows(
-        instanceManager.getInstanceId(instance)!, support);
+        instanceManager.getIdentifier(instance)!, support);
   }
 
   /// Helper method to convert instances ids to objects.
@@ -365,7 +361,7 @@
     bool flag,
   ) {
     return setJavaScriptEnabled(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       flag,
     );
   }
@@ -376,7 +372,7 @@
     String? userAgentString,
   ) {
     return setUserAgentString(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       userAgentString,
     );
   }
@@ -387,7 +383,7 @@
     bool require,
   ) {
     return setMediaPlaybackRequiresUserGesture(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       require,
     );
   }
@@ -397,7 +393,7 @@
     WebSettings instance,
     bool support,
   ) {
-    return setSupportZoom(instanceManager.getInstanceId(instance)!, support);
+    return setSupportZoom(instanceManager.getIdentifier(instance)!, support);
   }
 
   /// Helper method to convert instances ids to objects.
@@ -406,7 +402,7 @@
     bool overview,
   ) {
     return setLoadWithOverviewMode(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       overview,
     );
   }
@@ -416,7 +412,7 @@
     WebSettings instance,
     bool use,
   ) {
-    return setUseWideViewPort(instanceManager.getInstanceId(instance)!, use);
+    return setUseWideViewPort(instanceManager.getIdentifier(instance)!, use);
   }
 
   /// Helper method to convert instances ids to objects.
@@ -425,7 +421,7 @@
     bool enabled,
   ) {
     return setDisplayZoomControls(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       enabled,
     );
   }
@@ -436,7 +432,7 @@
     bool enabled,
   ) {
     return setBuiltInZoomControls(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       enabled,
     );
   }
@@ -447,7 +443,7 @@
     bool enabled,
   ) {
     return setAllowFileAccess(
-      instanceManager.getInstanceId(instance)!,
+      instanceManager.getIdentifier(instance)!,
       enabled,
     );
   }
@@ -459,18 +455,20 @@
   JavaScriptChannelHostApiImpl({
     BinaryMessenger? binaryMessenger,
     InstanceManager? instanceManager,
-  }) : super(binaryMessenger: binaryMessenger) {
-    this.instanceManager = instanceManager ?? InstanceManager.instance;
-  }
+  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+        super(binaryMessenger: binaryMessenger);
 
   /// Maintains instances stored to communicate with java objects.
-  late final InstanceManager instanceManager;
+  final InstanceManager instanceManager;
 
   /// Helper method to convert instances ids to objects.
   Future<void> createFromInstance(JavaScriptChannel instance) async {
-    final int? instanceId = instanceManager.tryAddInstance(instance);
-    if (instanceId != null) {
-      return create(instanceId, instance.channelName);
+    if (instanceManager.getIdentifier(instance) == null) {
+      final int identifier = instanceManager.addDartCreatedInstance(instance);
+      await create(
+        identifier,
+        instance.channelName,
+      );
     }
   }
 }
@@ -478,22 +476,21 @@
 /// Flutter api implementation for [JavaScriptChannel].
 class JavaScriptChannelFlutterApiImpl extends JavaScriptChannelFlutterApi {
   /// Constructs a [JavaScriptChannelFlutterApiImpl].
-  JavaScriptChannelFlutterApiImpl({InstanceManager? instanceManager}) {
-    this.instanceManager = instanceManager ?? InstanceManager.instance;
-  }
+  JavaScriptChannelFlutterApiImpl({InstanceManager? instanceManager})
+      : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
 
   /// Maintains instances stored to communicate with java objects.
-  late final InstanceManager instanceManager;
+  final InstanceManager instanceManager;
 
   @override
   void dispose(int instanceId) {
-    instanceManager.removeInstance(instanceId);
+    instanceManager.remove(instanceId);
   }
 
   @override
   void postMessage(int instanceId, String message) {
-    final JavaScriptChannel? instance =
-        instanceManager.getInstance(instanceId) as JavaScriptChannel?;
+    final JavaScriptChannel? instance = instanceManager
+        .getInstanceWithWeakReference(instanceId) as JavaScriptChannel?;
     assert(
       instance != null,
       'InstanceManager does not contain an JavaScriptChannel with instanceId: $instanceId',
@@ -508,18 +505,17 @@
   WebViewClientHostApiImpl({
     BinaryMessenger? binaryMessenger,
     InstanceManager? instanceManager,
-  }) : super(binaryMessenger: binaryMessenger) {
-    this.instanceManager = instanceManager ?? InstanceManager.instance;
-  }
+  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+        super(binaryMessenger: binaryMessenger);
 
   /// Maintains instances stored to communicate with java objects.
-  late final InstanceManager instanceManager;
+  final InstanceManager instanceManager;
 
   /// Helper method to convert instances ids to objects.
   Future<void> createFromInstance(WebViewClient instance) async {
-    final int? instanceId = instanceManager.tryAddInstance(instance);
-    if (instanceId != null) {
-      return create(instanceId, instance.shouldOverrideUrlLoading);
+    if (instanceManager.getIdentifier(instance) == null) {
+      final int identifier = instanceManager.addDartCreatedInstance(instance);
+      return create(identifier, instance.shouldOverrideUrlLoading);
     }
   }
 }
@@ -527,24 +523,23 @@
 /// Flutter api implementation for [WebViewClient].
 class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi {
   /// Constructs a [WebViewClientFlutterApiImpl].
-  WebViewClientFlutterApiImpl({InstanceManager? instanceManager}) {
-    this.instanceManager = instanceManager ?? InstanceManager.instance;
-  }
+  WebViewClientFlutterApiImpl({InstanceManager? instanceManager})
+      : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
 
   /// Maintains instances stored to communicate with java objects.
-  late final InstanceManager instanceManager;
+  final InstanceManager instanceManager;
 
   @override
   void dispose(int instanceId) {
-    instanceManager.removeInstance(instanceId);
+    instanceManager.remove(instanceId);
   }
 
   @override
   void onPageFinished(int instanceId, int webViewInstanceId, String url) {
-    final WebViewClient? instance =
-        instanceManager.getInstance(instanceId) as WebViewClient?;
-    final WebView? webViewInstance =
-        instanceManager.getInstance(webViewInstanceId) as WebView?;
+    final WebViewClient? instance = instanceManager
+        .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+    final WebView? webViewInstance = instanceManager
+        .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
     assert(
       instance != null,
       'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -558,10 +553,10 @@
 
   @override
   void onPageStarted(int instanceId, int webViewInstanceId, String url) {
-    final WebViewClient? instance =
-        instanceManager.getInstance(instanceId) as WebViewClient?;
-    final WebView? webViewInstance =
-        instanceManager.getInstance(webViewInstanceId) as WebView?;
+    final WebViewClient? instance = instanceManager
+        .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+    final WebView? webViewInstance = instanceManager
+        .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
     assert(
       instance != null,
       'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -581,10 +576,10 @@
     String description,
     String failingUrl,
   ) {
-    final WebViewClient? instance =
-        instanceManager.getInstance(instanceId) as WebViewClient?;
-    final WebView? webViewInstance =
-        instanceManager.getInstance(webViewInstanceId) as WebView?;
+    final WebViewClient? instance = instanceManager
+        .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+    final WebView? webViewInstance = instanceManager
+        .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
     assert(
       instance != null,
       'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -609,10 +604,10 @@
     WebResourceRequestData request,
     WebResourceErrorData error,
   ) {
-    final WebViewClient? instance =
-        instanceManager.getInstance(instanceId) as WebViewClient?;
-    final WebView? webViewInstance =
-        instanceManager.getInstance(webViewInstanceId) as WebView?;
+    final WebViewClient? instance = instanceManager
+        .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+    final WebView? webViewInstance = instanceManager
+        .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
     assert(
       instance != null,
       'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -634,10 +629,10 @@
     int webViewInstanceId,
     WebResourceRequestData request,
   ) {
-    final WebViewClient? instance =
-        instanceManager.getInstance(instanceId) as WebViewClient?;
-    final WebView? webViewInstance =
-        instanceManager.getInstance(webViewInstanceId) as WebView?;
+    final WebViewClient? instance = instanceManager
+        .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+    final WebView? webViewInstance = instanceManager
+        .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
     assert(
       instance != null,
       'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -655,10 +650,10 @@
     int webViewInstanceId,
     String url,
   ) {
-    final WebViewClient? instance =
-        instanceManager.getInstance(instanceId) as WebViewClient?;
-    final WebView? webViewInstance =
-        instanceManager.getInstance(webViewInstanceId) as WebView?;
+    final WebViewClient? instance = instanceManager
+        .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+    final WebView? webViewInstance = instanceManager
+        .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
     assert(
       instance != null,
       'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -677,18 +672,17 @@
   DownloadListenerHostApiImpl({
     BinaryMessenger? binaryMessenger,
     InstanceManager? instanceManager,
-  }) : super(binaryMessenger: binaryMessenger) {
-    this.instanceManager = instanceManager ?? InstanceManager.instance;
-  }
+  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+        super(binaryMessenger: binaryMessenger);
 
   /// Maintains instances stored to communicate with java objects.
-  late final InstanceManager instanceManager;
+  final InstanceManager instanceManager;
 
   /// Helper method to convert instances ids to objects.
   Future<void> createFromInstance(DownloadListener instance) async {
-    final int? instanceId = instanceManager.tryAddInstance(instance);
-    if (instanceId != null) {
-      return create(instanceId);
+    if (instanceManager.getIdentifier(instance) == null) {
+      final int identifier = instanceManager.addDartCreatedInstance(instance);
+      return create(identifier);
     }
   }
 }
@@ -696,16 +690,15 @@
 /// Flutter api implementation for [DownloadListener].
 class DownloadListenerFlutterApiImpl extends DownloadListenerFlutterApi {
   /// Constructs a [DownloadListenerFlutterApiImpl].
-  DownloadListenerFlutterApiImpl({InstanceManager? instanceManager}) {
-    this.instanceManager = instanceManager ?? InstanceManager.instance;
-  }
+  DownloadListenerFlutterApiImpl({InstanceManager? instanceManager})
+      : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
 
   /// Maintains instances stored to communicate with java objects.
-  late final InstanceManager instanceManager;
+  final InstanceManager instanceManager;
 
   @override
   void dispose(int instanceId) {
-    instanceManager.removeInstance(instanceId);
+    instanceManager.remove(instanceId);
   }
 
   @override
@@ -717,8 +710,8 @@
     String mimetype,
     int contentLength,
   ) {
-    final DownloadListener? instance =
-        instanceManager.getInstance(instanceId) as DownloadListener?;
+    final DownloadListener? instance = instanceManager
+        .getInstanceWithWeakReference(instanceId) as DownloadListener?;
     assert(
       instance != null,
       'InstanceManager does not contain an DownloadListener with instanceId: $instanceId',
@@ -739,21 +732,23 @@
   WebChromeClientHostApiImpl({
     BinaryMessenger? binaryMessenger,
     InstanceManager? instanceManager,
-  }) : super(binaryMessenger: binaryMessenger) {
-    this.instanceManager = instanceManager ?? InstanceManager.instance;
-  }
+  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+        super(binaryMessenger: binaryMessenger);
 
   /// Maintains instances stored to communicate with java objects.
-  late final InstanceManager instanceManager;
+  final InstanceManager instanceManager;
 
   /// Helper method to convert instances ids to objects.
   Future<void> createFromInstance(
     WebChromeClient instance,
     WebViewClient webViewClient,
   ) async {
-    final int? instanceId = instanceManager.tryAddInstance(instance);
-    if (instanceId != null) {
-      return create(instanceId, instanceManager.getInstanceId(webViewClient)!);
+    if (instanceManager.getIdentifier(instance) == null) {
+      final int identifier = instanceManager.addDartCreatedInstance(instance);
+      return create(
+        identifier,
+        instanceManager.getIdentifier(webViewClient)!,
+      );
     }
   }
 }
@@ -761,24 +756,23 @@
 /// Flutter api implementation for [DownloadListener].
 class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
   /// Constructs a [DownloadListenerFlutterApiImpl].
-  WebChromeClientFlutterApiImpl({InstanceManager? instanceManager}) {
-    this.instanceManager = instanceManager ?? InstanceManager.instance;
-  }
+  WebChromeClientFlutterApiImpl({InstanceManager? instanceManager})
+      : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
 
   /// Maintains instances stored to communicate with java objects.
-  late final InstanceManager instanceManager;
+  final InstanceManager instanceManager;
 
   @override
   void dispose(int instanceId) {
-    instanceManager.removeInstance(instanceId);
+    instanceManager.remove(instanceId);
   }
 
   @override
   void onProgressChanged(int instanceId, int webViewInstanceId, int progress) {
-    final WebChromeClient? instance =
-        instanceManager.getInstance(instanceId) as WebChromeClient?;
-    final WebView? webViewInstance =
-        instanceManager.getInstance(webViewInstanceId) as WebView?;
+    final WebChromeClient? instance = instanceManager
+        .getInstanceWithWeakReference(instanceId) as WebChromeClient?;
+    final WebView? webViewInstance = instanceManager
+        .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
     assert(
       instance != null,
       'InstanceManager does not contain an WebChromeClient with instanceId: $instanceId',
@@ -797,23 +791,22 @@
   WebStorageHostApiImpl({
     BinaryMessenger? binaryMessenger,
     InstanceManager? instanceManager,
-  }) : super(binaryMessenger: binaryMessenger) {
-    this.instanceManager = instanceManager ?? InstanceManager.instance;
-  }
+  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+        super(binaryMessenger: binaryMessenger);
 
   /// Maintains instances stored to communicate with java objects.
-  late final InstanceManager instanceManager;
+  final InstanceManager instanceManager;
 
   /// Helper method to convert instances ids to objects.
   Future<void> createFromInstance(WebStorage instance) async {
-    final int? instanceId = instanceManager.tryAddInstance(instance);
-    if (instanceId != null) {
-      return create(instanceId);
+    if (instanceManager.getIdentifier(instance) == null) {
+      final int identifier = instanceManager.addDartCreatedInstance(instance);
+      return create(identifier);
     }
   }
 
   /// Helper method to convert instances ids to objects.
   Future<void> deleteAllDataFromInstance(WebStorage instance) {
-    return deleteAllData(instanceManager.getInstanceId(instance)!);
+    return deleteAllData(instanceManager.getIdentifier(instance)!);
   }
 }
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart b/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart
index 531fffa..5892823 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart
@@ -23,51 +23,179 @@
   Copyable copy();
 }
 
-/// Maintains instances stored to communicate with java objects.
+/// Maintains instances used to communicate with the native objects they
+/// represent.
+///
+/// Added instances are stored as weak references and their copies are stored
+/// as strong references to maintain access to their variables and callback
+/// methods. Both are stored with the same identifier.
+///
+/// When a weak referenced instance becomes inaccessible,
+/// [onWeakReferenceRemoved] is called with its associated identifier.
+///
+/// If an instance is retrieved and has the possibility to be used,
+/// (e.g. calling [getInstanceWithWeakReference]) a copy of the strong reference
+/// is added as a weak reference with the same identifier. This prevents a
+/// scenario where the weak referenced instance was released and then later
+/// returned by the host platform.
 class InstanceManager {
-  final Map<int, Object> _instanceIdsToInstances = <int, Object>{};
-  final Map<Object, int> _instancesToInstanceIds = <Object, int>{};
+  /// Constructs an [InstanceManager].
+  InstanceManager({required void Function(int) onWeakReferenceRemoved}) {
+    this.onWeakReferenceRemoved = (int identifier) {
+      _weakInstances.remove(identifier);
+      onWeakReferenceRemoved(identifier);
+    };
+    _finalizer = Finalizer<int>(this.onWeakReferenceRemoved);
+  }
 
-  int _nextInstanceId = 0;
+  // Identifiers are locked to a specific range to avoid collisions with objects
+  // created simultaneously by the host platform.
+  // Host uses identifiers >= 2^16 and Dart is expected to use values n where,
+  // 0 <= n < 2^16.
+  static const int _maxDartCreatedIdentifier = 65536;
 
-  /// Global instance of [InstanceManager].
-  static final InstanceManager instance = InstanceManager();
+  // Expando is used because it doesn't prevent its keys from becoming
+  // inaccessible. This allows the manager to efficiently retrieve an identifier
+  // of an instance without holding a strong reference to that instance.
+  //
+  // It also doesn't use `==` to search for identifiers, which would lead to an
+  // infinite loop when comparing an object to its copy. (i.e. which was caused
+  // by calling instanceManager.getIdentifier() inside of `==` while this was a
+  // HashMap).
+  final Expando<int> _identifiers = Expando<int>();
+  final Map<int, WeakReference<Copyable>> _weakInstances =
+      <int, WeakReference<Copyable>>{};
+  final Map<int, Copyable> _strongInstances = <int, Copyable>{};
+  late final Finalizer<int> _finalizer;
+  int _nextIdentifier = 0;
 
-  /// Attempt to add a new instance.
+  /// Called when a weak referenced instance is removed by [removeWeakReference]
+  /// or becomes inaccessible.
+  late final void Function(int) onWeakReferenceRemoved;
+
+  /// Adds a new instance that was instantiated by Dart.
   ///
-  /// Returns new if [instance] has already been added. Otherwise, it is added
-  /// with a new instance id.
-  int? tryAddInstance(Object instance) {
-    if (_instancesToInstanceIds.containsKey(instance)) {
+  /// In other words, Dart wants to add a new instance that will represent
+  /// an object that will be instantiated on the host platform.
+  ///
+  /// Throws assertion error if the instance has already been added.
+  ///
+  /// Returns the randomly generated id of the [instance] added.
+  int addDartCreatedInstance(Copyable instance) {
+    assert(getIdentifier(instance) == null);
+
+    final int identifier = _nextUniqueIdentifier();
+    _addInstanceWithIdentifier(instance, identifier);
+    return identifier;
+  }
+
+  /// Removes the instance, if present, and call [onWeakReferenceRemoved] with
+  /// its identifier.
+  ///
+  /// Returns the identifier associated with the removed instance. Otherwise,
+  /// `null` if the instance was not found in this manager.
+  ///
+  /// This does not remove the the strong referenced instance associated with
+  /// [instance]. This can be done with [remove].
+  int? removeWeakReference(Copyable instance) {
+    final int? identifier = getIdentifier(instance);
+    if (identifier == null) {
       return null;
     }
 
-    final int instanceId = _nextInstanceId++;
-    _instancesToInstanceIds[instance] = instanceId;
-    _instanceIdsToInstances[instanceId] = instance;
-    return instanceId;
+    _identifiers[instance] = null;
+    _finalizer.detach(instance);
+    onWeakReferenceRemoved(identifier);
+
+    return identifier;
   }
 
-  /// Remove the instance from the manager.
+  /// Removes [identifier] and its associated strongly referenced instance, if
+  /// present, from the manager.
   ///
-  /// Returns null if the instance is removed. Otherwise, return the instanceId
-  /// of the removed instance.
-  int? removeInstance(Object instance) {
-    final int? instanceId = _instancesToInstanceIds[instance];
-    if (instanceId != null) {
-      _instancesToInstanceIds.remove(instance);
-      _instanceIdsToInstances.remove(instanceId);
+  /// Returns the strong referenced instance associated with [identifier] before
+  /// it was removed. Returns `null` if [identifier] was not associated with
+  /// any strong reference.
+  ///
+  /// This does not remove the the weak referenced instance associtated with
+  /// [identifier]. This can be done with [removeWeakReference].
+  T? remove<T extends Copyable>(int identifier) {
+    return _strongInstances.remove(identifier) as T?;
+  }
+
+  /// Retrieves the instance associated with identifier.
+  ///
+  /// The value returned is chosen from the following order:
+  ///
+  /// 1. A weakly referenced instance associated with identifier.
+  /// 2. If the only instance associated with identifier is a strongly
+  /// referenced instance, a copy of the instance is added as a weak reference
+  /// with the same identifier. Returning the newly created copy.
+  /// 3. If no instance is associated with identifier, returns null.
+  ///
+  /// This method also expects the host `InstanceManager` to have a strong
+  /// reference to the instance the identifier is associated with.
+  T? getInstanceWithWeakReference<T extends Copyable>(int identifier) {
+    final Copyable? weakInstance = _weakInstances[identifier]?.target;
+
+    if (weakInstance == null) {
+      final Copyable? strongInstance = _strongInstances[identifier];
+      if (strongInstance != null) {
+        final Copyable copy = strongInstance.copy();
+        _identifiers[copy] = identifier;
+        _weakInstances[identifier] = WeakReference<Copyable>(copy);
+        _finalizer.attach(copy, identifier, detach: copy);
+        return copy as T;
+      }
+      return strongInstance as T?;
     }
-    return instanceId;
+
+    return weakInstance as T;
   }
 
-  /// Retrieve the Object paired with instanceId.
-  Object? getInstance(int instanceId) {
-    return _instanceIdsToInstances[instanceId];
+  /// Retrieves the identifier associated with instance.
+  int? getIdentifier(Copyable instance) {
+    return _identifiers[instance];
   }
 
-  /// Retrieve the instanceId paired with instance.
-  int? getInstanceId(Object instance) {
-    return _instancesToInstanceIds[instance];
+  /// Adds a new instance that was instantiated by the host platform.
+  ///
+  /// In other words, the host platform wants to add a new instance that
+  /// represents an object on the host platform. Stored with [identifier].
+  ///
+  /// Throws assertion error if the instance or its identifier has already been
+  /// added.
+  ///
+  /// Returns unique identifier of the [instance] added.
+  void addHostCreatedInstance(Copyable instance, int identifier) {
+    assert(!containsIdentifier(identifier));
+    assert(getIdentifier(instance) == null);
+    assert(identifier >= 0);
+    _addInstanceWithIdentifier(instance, identifier);
+  }
+
+  void _addInstanceWithIdentifier(Copyable instance, int identifier) {
+    _identifiers[instance] = identifier;
+    _weakInstances[identifier] = WeakReference<Copyable>(instance);
+    _finalizer.attach(instance, identifier, detach: instance);
+
+    final Copyable copy = instance.copy();
+    _identifiers[copy] = identifier;
+    _strongInstances[identifier] = copy;
+  }
+
+  /// Whether this manager contains the given [identifier].
+  bool containsIdentifier(int identifier) {
+    return _weakInstances.containsKey(identifier) ||
+        _strongInstances.containsKey(identifier);
+  }
+
+  int _nextUniqueIdentifier() {
+    late int identifier;
+    do {
+      identifier = _nextIdentifier;
+      _nextIdentifier = (_nextIdentifier + 1) % _maxDartCreatedIdentifier;
+    } while (containsIdentifier(identifier));
+    return identifier;
   }
 }
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart
index 1f0eb7b..4774024 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart
@@ -11,7 +11,6 @@
 import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
 
 import 'src/android_webview.dart';
-import 'src/instance_manager.dart';
 import 'webview_android_widget.dart';
 
 /// Builds an Android webview.
@@ -55,8 +54,8 @@
             gestureRecognizers: gestureRecognizers,
             layoutDirection:
                 Directionality.maybeOf(context) ?? TextDirection.rtl,
-            creationParams:
-                InstanceManager.instance.getInstanceId(controller.webView),
+            creationParams: JavaObject.globalInstanceManager
+                .getIdentifier(controller.webView),
             creationParamsCodec: const StandardMessageCodec(),
           ),
         );
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart
index 61ec110..e89fb7d 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart
@@ -10,7 +10,6 @@
 import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
 
 import 'src/android_webview.dart';
-import 'src/instance_manager.dart';
 import 'webview_android.dart';
 import 'webview_android_widget.dart';
 
@@ -74,8 +73,8 @@
               // directionality.
               layoutDirection:
                   Directionality.maybeOf(context) ?? TextDirection.ltr,
-              webViewIdentifier:
-                  InstanceManager.instance.getInstanceId(controller.webView)!,
+              webViewIdentifier: JavaObject.globalInstanceManager
+                  .getIdentifier(controller.webView)!,
             )
               ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
               ..addOnPlatformViewCreatedListener((int id) {
diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
index f765341..a60c4f9 100644
--- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
@@ -2,7 +2,7 @@
 description: A Flutter plugin that provides a WebView widget on Android.
 repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
-version: 2.9.2
+version: 2.9.3
 
 environment:
   sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
index cb2ea0f..9d479e6 100644
--- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
@@ -45,11 +45,11 @@
         mockPlatformHostApi = MockTestWebViewHostApi();
         TestWebViewHostApi.setup(mockPlatformHostApi);
 
-        instanceManager = InstanceManager();
+        instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
         WebView.api = WebViewHostApiImpl(instanceManager: instanceManager);
 
         webView = WebView();
-        webViewInstanceId = instanceManager.getInstanceId(webView)!;
+        webViewInstanceId = instanceManager.getIdentifier(webView)!;
       });
 
       test('create', () {
@@ -206,11 +206,12 @@
         );
 
         final WebViewClient mockWebViewClient = MockWebViewClient();
+        when(mockWebViewClient.copy()).thenReturn(MockWebViewClient());
         when(mockWebViewClient.shouldOverrideUrlLoading).thenReturn(false);
         webView.setWebViewClient(mockWebViewClient);
 
         final int webViewClientInstanceId =
-            instanceManager.getInstanceId(mockWebViewClient)!;
+            instanceManager.getIdentifier(mockWebViewClient)!;
         verify(mockPlatformHostApi.setWebViewClient(
           webViewInstanceId,
           webViewClientInstanceId,
@@ -224,12 +225,13 @@
         );
 
         final JavaScriptChannel mockJavaScriptChannel = MockJavaScriptChannel();
+        when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel());
         when(mockJavaScriptChannel.channelName).thenReturn('aChannel');
 
         webView.addJavaScriptChannel(mockJavaScriptChannel);
 
         final int javaScriptChannelInstanceId =
-            instanceManager.getInstanceId(mockJavaScriptChannel)!;
+            instanceManager.getIdentifier(mockJavaScriptChannel)!;
         verify(mockPlatformHostApi.addJavaScriptChannel(
           webViewInstanceId,
           javaScriptChannelInstanceId,
@@ -243,6 +245,7 @@
         );
 
         final JavaScriptChannel mockJavaScriptChannel = MockJavaScriptChannel();
+        when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel());
         when(mockJavaScriptChannel.channelName).thenReturn('aChannel');
 
         expect(
@@ -254,7 +257,7 @@
         webView.removeJavaScriptChannel(mockJavaScriptChannel);
 
         final int javaScriptChannelInstanceId =
-            instanceManager.getInstanceId(mockJavaScriptChannel)!;
+            instanceManager.getIdentifier(mockJavaScriptChannel)!;
         verify(mockPlatformHostApi.removeJavaScriptChannel(
           webViewInstanceId,
           javaScriptChannelInstanceId,
@@ -268,10 +271,11 @@
         );
 
         final DownloadListener mockDownloadListener = MockDownloadListener();
+        when(mockDownloadListener.copy()).thenReturn(MockDownloadListener());
         webView.setDownloadListener(mockDownloadListener);
 
         final int downloadListenerInstanceId =
-            instanceManager.getInstanceId(mockDownloadListener)!;
+            instanceManager.getIdentifier(mockDownloadListener)!;
         verify(mockPlatformHostApi.setDownloadListener(
           webViewInstanceId,
           downloadListenerInstanceId,
@@ -285,6 +289,7 @@
           instanceManager: instanceManager,
         );
         final WebViewClient mockWebViewClient = MockWebViewClient();
+        when(mockWebViewClient.copy()).thenReturn(MockWebViewClient());
         when(mockWebViewClient.shouldOverrideUrlLoading).thenReturn(false);
         webView.setWebViewClient(mockWebViewClient);
 
@@ -294,10 +299,11 @@
         );
 
         final WebChromeClient mockWebChromeClient = MockWebChromeClient();
+        when(mockWebChromeClient.copy()).thenReturn(MockWebChromeClient());
         webView.setWebChromeClient(mockWebChromeClient);
 
         final int webChromeClientInstanceId =
-            instanceManager.getInstanceId(mockWebChromeClient)!;
+            instanceManager.getIdentifier(mockWebChromeClient)!;
         verify(mockPlatformHostApi.setWebChromeClient(
           webViewInstanceId,
           webChromeClientInstanceId,
@@ -312,7 +318,7 @@
         WebSettings.api =
             WebSettingsHostApiImpl(instanceManager: instanceManager);
         final int webSettingsInstanceId =
-            instanceManager.getInstanceId(webView.settings)!;
+            instanceManager.getIdentifier(webView.settings)!;
 
         webView.release();
         verify(mockWebSettingsPlatformHostApi.dispose(webSettingsInstanceId));
@@ -333,7 +339,7 @@
       late int webSettingsInstanceId;
 
       setUp(() {
-        instanceManager = InstanceManager();
+        instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
 
         TestWebViewHostApi.setup(MockTestWebViewHostApi());
         WebView.api = WebViewHostApiImpl(instanceManager: instanceManager);
@@ -346,7 +352,7 @@
         );
 
         webSettings = WebSettings(WebView());
-        webSettingsInstanceId = instanceManager.getInstanceId(webSettings)!;
+        webSettingsInstanceId = instanceManager.getIdentifier(webSettings)!;
       });
 
       test('create', () {
@@ -463,14 +469,16 @@
       late int mockJavaScriptChannelInstanceId;
 
       setUp(() {
-        instanceManager = InstanceManager();
+        instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
         flutterApi = JavaScriptChannelFlutterApiImpl(
           instanceManager: instanceManager,
         );
 
         mockJavaScriptChannel = MockJavaScriptChannel();
+        when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel());
+
         mockJavaScriptChannelInstanceId =
-            instanceManager.tryAddInstance(mockJavaScriptChannel)!;
+            instanceManager.addDartCreatedInstance(mockJavaScriptChannel);
       });
 
       test('postMessage', () {
@@ -501,17 +509,20 @@
       late int mockWebViewInstanceId;
 
       setUp(() {
-        instanceManager = InstanceManager();
+        instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
         flutterApi = WebViewClientFlutterApiImpl(
           instanceManager: instanceManager,
         );
 
         mockWebViewClient = MockWebViewClient();
+        when(mockWebViewClient.copy()).thenReturn(MockWebViewClient());
         mockWebViewClientInstanceId =
-            instanceManager.tryAddInstance(mockWebViewClient)!;
+            instanceManager.addDartCreatedInstance(mockWebViewClient);
 
         mockWebView = MockWebView();
-        mockWebViewInstanceId = instanceManager.tryAddInstance(mockWebView)!;
+        when(mockWebView.copy()).thenReturn(MockWebView());
+        mockWebViewInstanceId =
+            instanceManager.addDartCreatedInstance(mockWebView);
       });
 
       test('onPageStarted', () {
@@ -621,14 +632,15 @@
       late int mockDownloadListenerInstanceId;
 
       setUp(() {
-        instanceManager = InstanceManager();
+        instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
         flutterApi = DownloadListenerFlutterApiImpl(
           instanceManager: instanceManager,
         );
 
         mockDownloadListener = MockDownloadListener();
+        when(mockDownloadListener.copy()).thenReturn(MockDownloadListener());
         mockDownloadListenerInstanceId =
-            instanceManager.tryAddInstance(mockDownloadListener)!;
+            instanceManager.addDartCreatedInstance(mockDownloadListener);
       });
 
       test('onPageStarted', () {
@@ -666,17 +678,21 @@
       late int mockWebViewInstanceId;
 
       setUp(() {
-        instanceManager = InstanceManager();
+        instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
         flutterApi = WebChromeClientFlutterApiImpl(
           instanceManager: instanceManager,
         );
 
         mockWebChromeClient = MockWebChromeClient();
+        when(mockWebChromeClient.copy()).thenReturn(MockWebChromeClient());
+
         mockWebChromeClientInstanceId =
-            instanceManager.tryAddInstance(mockWebChromeClient)!;
+            instanceManager.addDartCreatedInstance(mockWebChromeClient);
 
         mockWebView = MockWebView();
-        mockWebViewInstanceId = instanceManager.tryAddInstance(mockWebView)!;
+        when(mockWebView.copy()).thenReturn(MockWebView());
+        mockWebViewInstanceId =
+            instanceManager.addDartCreatedInstance(mockWebView);
       });
 
       test('onPageStarted', () {
@@ -722,7 +738,7 @@
 
       webStorage = WebStorage();
       webStorageInstanceId =
-          WebStorage.api.instanceManager.getInstanceId(webStorage)!;
+          WebStorage.api.instanceManager.getIdentifier(webStorage)!;
     });
 
     test('create', () {
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
index 72d1d41..db877e5 100644
--- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
@@ -59,7 +59,6 @@
 /// A class which mocks [DownloadListener].
 ///
 /// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
 class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener {
   MockDownloadListener() {
     _i1.throwOnMissingStub(this);
@@ -81,7 +80,6 @@
 /// A class which mocks [JavaScriptChannel].
 ///
 /// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
 class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel {
   MockJavaScriptChannel() {
     _i1.throwOnMissingStub(this);
@@ -417,7 +415,6 @@
 /// A class which mocks [WebChromeClient].
 ///
 /// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
 class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient {
   MockWebChromeClient() {
     _i1.throwOnMissingStub(this);
@@ -436,7 +433,6 @@
 /// A class which mocks [WebView].
 ///
 /// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
 class MockWebView extends _i1.Mock implements _i2.WebView {
   MockWebView() {
     _i1.throwOnMissingStub(this);
@@ -590,7 +586,6 @@
 /// A class which mocks [WebViewClient].
 ///
 /// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
 class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient {
   MockWebViewClient() {
     _i1.throwOnMissingStub(this);
diff --git a/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart b/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart
index 3aeb005..dd3dcca 100644
--- a/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart
@@ -7,29 +7,137 @@
 
 void main() {
   group('InstanceManager', () {
-    late InstanceManager testInstanceManager;
+    test('addHostCreatedInstance', () {
+      final CopyableObject object = CopyableObject();
 
-    setUp(() {
-      testInstanceManager = InstanceManager();
+      final InstanceManager instanceManager =
+          InstanceManager(onWeakReferenceRemoved: (_) {});
+
+      instanceManager.addHostCreatedInstance(object, 0);
+
+      expect(instanceManager.getIdentifier(object), 0);
+      expect(
+        instanceManager.getInstanceWithWeakReference(0),
+        object,
+      );
     });
 
-    test('tryAddInstance', () {
-      final Object object = Object();
+    test('addHostCreatedInstance prevents already used objects and ids', () {
+      final CopyableObject object = CopyableObject();
 
-      expect(testInstanceManager.tryAddInstance(object), 0);
-      expect(testInstanceManager.getInstanceId(object), 0);
-      expect(testInstanceManager.getInstance(0), object);
-      expect(testInstanceManager.tryAddInstance(object), null);
+      final InstanceManager instanceManager =
+          InstanceManager(onWeakReferenceRemoved: (_) {});
+
+      instanceManager.addHostCreatedInstance(object, 0);
+
+      expect(
+        () => instanceManager.addHostCreatedInstance(object, 0),
+        throwsAssertionError,
+      );
+
+      expect(
+        () => instanceManager.addHostCreatedInstance(CopyableObject(), 0),
+        throwsAssertionError,
+      );
     });
 
-    test('removeInstance', () {
-      final Object object = Object();
-      testInstanceManager.tryAddInstance(object);
+    test('addFlutterCreatedInstance', () {
+      final CopyableObject object = CopyableObject();
 
-      expect(testInstanceManager.removeInstance(object), 0);
-      expect(testInstanceManager.getInstanceId(object), null);
-      expect(testInstanceManager.getInstance(0), null);
-      expect(testInstanceManager.removeInstance(object), null);
+      final InstanceManager instanceManager =
+          InstanceManager(onWeakReferenceRemoved: (_) {});
+
+      instanceManager.addDartCreatedInstance(object);
+
+      final int? instanceId = instanceManager.getIdentifier(object);
+      expect(instanceId, isNotNull);
+      expect(
+        instanceManager.getInstanceWithWeakReference(instanceId!),
+        object,
+      );
+    });
+
+    test('removeWeakReference', () {
+      final CopyableObject object = CopyableObject();
+
+      int? weakInstanceId;
+      final InstanceManager instanceManager =
+          InstanceManager(onWeakReferenceRemoved: (int instanceId) {
+        weakInstanceId = instanceId;
+      });
+
+      instanceManager.addHostCreatedInstance(object, 0);
+
+      expect(instanceManager.removeWeakReference(object), 0);
+      expect(
+        instanceManager.getInstanceWithWeakReference(0),
+        isA<CopyableObject>(),
+      );
+      expect(weakInstanceId, 0);
+    });
+
+    test('removeWeakReference removes only weak reference', () {
+      final CopyableObject object = CopyableObject();
+
+      final InstanceManager instanceManager =
+          InstanceManager(onWeakReferenceRemoved: (_) {});
+
+      instanceManager.addHostCreatedInstance(object, 0);
+
+      expect(instanceManager.removeWeakReference(object), 0);
+      final CopyableObject copy = instanceManager.getInstanceWithWeakReference(
+        0,
+      )!;
+      expect(identical(object, copy), isFalse);
+    });
+
+    test('removeStrongReference', () {
+      final CopyableObject object = CopyableObject();
+
+      final InstanceManager instanceManager =
+          InstanceManager(onWeakReferenceRemoved: (_) {});
+
+      instanceManager.addHostCreatedInstance(object, 0);
+      instanceManager.removeWeakReference(object);
+      expect(instanceManager.remove(0), isA<CopyableObject>());
+      expect(instanceManager.containsIdentifier(0), isFalse);
+    });
+
+    test('removeStrongReference removes only strong reference', () {
+      final CopyableObject object = CopyableObject();
+
+      final InstanceManager instanceManager =
+          InstanceManager(onWeakReferenceRemoved: (_) {});
+
+      instanceManager.addHostCreatedInstance(object, 0);
+      expect(instanceManager.remove(0), isA<CopyableObject>());
+      expect(
+        instanceManager.getInstanceWithWeakReference(0),
+        object,
+      );
+    });
+
+    test('getInstance can add a new weak reference', () {
+      final CopyableObject object = CopyableObject();
+
+      final InstanceManager instanceManager =
+          InstanceManager(onWeakReferenceRemoved: (_) {});
+
+      instanceManager.addHostCreatedInstance(object, 0);
+      instanceManager.removeWeakReference(object);
+
+      final CopyableObject newWeakCopy =
+          instanceManager.getInstanceWithWeakReference(
+        0,
+      )!;
+      expect(identical(object, newWeakCopy), isFalse);
     });
   });
 }
+
+class CopyableObject with Copyable {
+  @override
+  Copyable copy() {
+    return CopyableObject();
+  }
+}
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
index 2432b35..2fa75af 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
@@ -828,7 +828,7 @@
       // of WebView itstelf.
       mockPlatformHostApi = MockTestWebViewHostApi();
       TestWebViewHostApi.setup(mockPlatformHostApi);
-      instanceManager = InstanceManager();
+      instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
       android_webview.WebView.api =
           WebViewHostApiImpl(instanceManager: instanceManager);
     });
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
index f9e8838..04c2d77 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
@@ -64,7 +64,6 @@
 /// A class which mocks [WebSettings].
 ///
 /// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
 class MockWebSettings extends _i1.Mock implements _i2.WebSettings {
   MockWebSettings() {
     _i1.throwOnMissingStub(this);
@@ -140,7 +139,6 @@
 /// A class which mocks [WebStorage].
 ///
 /// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
 class MockWebStorage extends _i1.Mock implements _i2.WebStorage {
   MockWebStorage() {
     _i1.throwOnMissingStub(this);
@@ -159,7 +157,6 @@
 /// A class which mocks [WebView].
 ///
 /// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
 class MockWebView extends _i1.Mock implements _i2.WebView {
   MockWebView() {
     _i1.throwOnMissingStub(this);