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

Commit b751ff1

Browse files
[webview_flutter_android] Fixes crash when the Java InstanceManager was used after plugin was removed from engine (#6943)
* dont throw errors on all instance manager methods * tests * version bump * change to logging a warning and ignore calls in other methods * the * documentation
1 parent f36fa64 commit b751ff1

File tree

4 files changed

+104
-20
lines changed

4 files changed

+104
-20
lines changed

packages/webview_flutter/webview_flutter_android/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 3.1.3
2+
3+
* Fixes crash when the Java `InstanceManager` was used after plugin was removed from the engine.
4+
15
## 3.1.2
26

37
* Fixes bug where an `AndroidWebViewController` couldn't be reused with a new `WebViewWidget`.

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import android.os.Handler;
88
import android.os.Looper;
9+
import android.util.Log;
910
import androidx.annotation.Nullable;
1011
import java.lang.ref.ReferenceQueue;
1112
import java.lang.ref.WeakReference;
@@ -35,6 +36,8 @@ public class InstanceManager {
3536
// 0 <= n < 2^16.
3637
private static final long MIN_HOST_CREATED_IDENTIFIER = 65536;
3738
private static final long CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL = 30000;
39+
private static final String TAG = "InstanceManager";
40+
private static final String CLOSED_WARNING = "Method was called while the manager was closed.";
3841

3942
/** Interface for listening when a weak reference of an instance is removed from the manager. */
4043
public interface FinalizationListener {
@@ -79,11 +82,15 @@ private InstanceManager(FinalizationListener finalizationListener) {
7982
*
8083
* @param identifier the identifier paired to an instance.
8184
* @param <T> the expected return type.
82-
* @return the removed instance if the manager contains the given identifier, otherwise null.
85+
* @return the removed instance if the manager contains the given identifier, otherwise null if
86+
* the manager doesn't contain the value or the manager is closed.
8387
*/
8488
@Nullable
8589
public <T> T remove(long identifier) {
86-
assertManagerIsNotClosed();
90+
if (isClosed()) {
91+
Log.w(TAG, CLOSED_WARNING);
92+
return null;
93+
}
8794
return (T) strongInstances.remove(identifier);
8895
}
8996

@@ -95,11 +102,14 @@ public <T> T remove(long identifier) {
95102
*
96103
* @param instance an instance that may be stored in the manager.
97104
* @return the identifier associated with `instance` if the manager contains the value, otherwise
98-
* null.
105+
* null if the manager doesn't contain the value or the manager is closed.
99106
*/
100107
@Nullable
101108
public Long getIdentifierForStrongReference(Object instance) {
102-
assertManagerIsNotClosed();
109+
if (isClosed()) {
110+
Log.w(TAG, CLOSED_WARNING);
111+
return null;
112+
}
103113
final Long identifier = identifiers.get(instance);
104114
if (identifier != null) {
105115
strongInstances.put(identifier, instance);
@@ -114,22 +124,30 @@ public Long getIdentifierForStrongReference(Object instance) {
114124
* The Dart InstanceManager is considered the source of truth and has the capability to overwrite
115125
* stored pairs in response to hot restarts.
116126
*
127+
* <p>If the manager is closed, the addition is ignored.
128+
*
117129
* @param instance the instance to be stored.
118130
* @param identifier the identifier to be paired with instance. This value must be >= 0.
119131
*/
120132
public void addDartCreatedInstance(Object instance, long identifier) {
121-
assertManagerIsNotClosed();
133+
if (isClosed()) {
134+
Log.w(TAG, CLOSED_WARNING);
135+
return;
136+
}
122137
addInstance(instance, identifier);
123138
}
124139

125140
/**
126141
* Adds a new instance that was instantiated from the host platform.
127142
*
128143
* @param instance the instance to be stored.
129-
* @return the unique identifier stored with instance.
144+
* @return the unique identifier stored with instance. If the manager is closed, returns -1.
130145
*/
131146
public long addHostCreatedInstance(Object instance) {
132-
assertManagerIsNotClosed();
147+
if (isClosed()) {
148+
Log.w(TAG, CLOSED_WARNING);
149+
return -1;
150+
}
133151
final long identifier = nextIdentifier++;
134152
addInstance(instance, identifier);
135153
return identifier;
@@ -141,11 +159,14 @@ public long addHostCreatedInstance(Object instance) {
141159
* @param identifier the identifier paired to an instance.
142160
* @param <T> the expected return type.
143161
* @return the instance associated with `identifier` if the manager contains the value, otherwise
144-
* null.
162+
* null if the manager doesn't contain the value or the manager is closed.
145163
*/
146164
@Nullable
147165
public <T> T getInstance(long identifier) {
148-
assertManagerIsNotClosed();
166+
if (isClosed()) {
167+
Log.w(TAG, CLOSED_WARNING);
168+
return null;
169+
}
149170
final WeakReference<T> instance = (WeakReference<T>) weakInstances.get(identifier);
150171
if (instance != null) {
151172
return instance.get();
@@ -157,22 +178,38 @@ public <T> T getInstance(long identifier) {
157178
* Returns whether this manager contains the given `instance`.
158179
*
159180
* @param instance the instance whose presence in this manager is to be tested.
160-
* @return whether this manager contains the given `instance`.
181+
* @return whether this manager contains the given `instance`. If the manager is closed, returns
182+
* `false`.
161183
*/
162184
public boolean containsInstance(Object instance) {
163-
assertManagerIsNotClosed();
185+
if (isClosed()) {
186+
Log.w(TAG, CLOSED_WARNING);
187+
return false;
188+
}
164189
return identifiers.containsKey(instance);
165190
}
166191

167192
/**
168193
* Closes the manager and releases resources.
169194
*
170-
* <p>Calling a method after calling this one will throw an {@link AssertionError}. This method
171-
* excluded.
195+
* <p>Methods called after this one will be ignored and log a warning.
172196
*/
173197
public void close() {
174198
handler.removeCallbacks(this::releaseAllFinalizedInstances);
175199
isClosed = true;
200+
identifiers.clear();
201+
weakInstances.clear();
202+
strongInstances.clear();
203+
weakReferencesToIdentifiers.clear();
204+
}
205+
206+
/**
207+
* Whether the manager has released resources and is not longer usable.
208+
*
209+
* <p>See {@link #close()}.
210+
*/
211+
public boolean isClosed() {
212+
return isClosed;
176213
}
177214

178215
private void releaseAllFinalizedInstances() {
@@ -199,10 +236,4 @@ private void addInstance(Object instance, long identifier) {
199236
weakReferencesToIdentifiers.put(weakReference, identifier);
200237
strongInstances.put(identifier, instance);
201238
}
202-
203-
private void assertManagerIsNotClosed() {
204-
if (isClosed) {
205-
throw new AssertionError("Manager has already been closed.");
206-
}
207-
}
208239
}

packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/InstanceManagerTest.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package io.flutter.plugins.webviewflutter;
66

77
import static org.junit.Assert.assertEquals;
8+
import static org.junit.Assert.assertFalse;
89
import static org.junit.Assert.assertNotNull;
910
import static org.junit.Assert.assertNull;
1011
import static org.junit.Assert.assertTrue;
@@ -59,4 +60,52 @@ public void remove() {
5960

6061
instanceManager.close();
6162
}
63+
64+
@Test
65+
public void removeReturnsNullWhenClosed() {
66+
final Object object = new Object();
67+
final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
68+
instanceManager.addDartCreatedInstance(object, 0);
69+
instanceManager.close();
70+
71+
assertNull(instanceManager.remove(0));
72+
}
73+
74+
@Test
75+
public void getIdentifierForStrongReferenceReturnsNullWhenClosed() {
76+
final Object object = new Object();
77+
final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
78+
instanceManager.addDartCreatedInstance(object, 0);
79+
instanceManager.close();
80+
81+
assertNull(instanceManager.getIdentifierForStrongReference(object));
82+
}
83+
84+
@Test
85+
public void addHostCreatedInstanceReturnsNegativeOneWhenClosed() {
86+
final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
87+
instanceManager.close();
88+
89+
assertEquals(instanceManager.addHostCreatedInstance(new Object()), -1L);
90+
}
91+
92+
@Test
93+
public void getInstanceReturnsNullWhenClosed() {
94+
final Object object = new Object();
95+
final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
96+
instanceManager.addDartCreatedInstance(object, 0);
97+
instanceManager.close();
98+
99+
assertNull(instanceManager.getInstance(0));
100+
}
101+
102+
@Test
103+
public void containsInstanceReturnsFalseWhenClosed() {
104+
final Object object = new Object();
105+
final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
106+
instanceManager.addDartCreatedInstance(object, 0);
107+
instanceManager.close();
108+
109+
assertFalse(instanceManager.containsInstance(object));
110+
}
62111
}

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.1.2
5+
version: 3.1.3
66

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

0 commit comments

Comments
 (0)