diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md
index cfe8bbc7221..a7f96245153 100644
--- a/packages/camera/camera_android_camerax/CHANGELOG.md
+++ b/packages/camera/camera_android_camerax/CHANGELOG.md
@@ -17,3 +17,4 @@
* Fixes instance manager hot restart behavior and fixes Java casting issue.
* Implements image capture.
* Fixes cast of CameraInfo to fix integration test failure.
+* Updates internal Java InstanceManager to only stop finalization callbacks when stopped.
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java
index 08ecc847aa1..d0176a1812c 100644
--- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java
@@ -31,7 +31,7 @@ public CameraAndroidCameraxPlugin() {}
void setUp(BinaryMessenger binaryMessenger, Context context, TextureRegistry textureRegistry) {
// Set up instance manager.
instanceManager =
- InstanceManager.open(
+ InstanceManager.create(
identifier -> {
new GeneratedCameraXLibrary.JavaObjectFlutterApi(binaryMessenger)
.dispose(identifier, reply -> {});
@@ -66,7 +66,7 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBindin
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
if (instanceManager != null) {
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
}
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java
index 4e7fa3e9e16..43004a6aab7 100644
--- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java
@@ -7,6 +7,7 @@
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
@@ -30,9 +31,6 @@
*/
@SuppressWarnings("unchecked")
public class InstanceManager {
- /// Constant returned from #addHostCreatedInstance() if the manager is closed.
- public static final int INSTANCE_CLOSED = -1;
-
// Identifiers are locked to a specific range to avoid collisions with objects
// created simultaneously from Dart.
// Host uses identifiers >= 2^16 and Dart is expected to use values n where,
@@ -40,7 +38,6 @@ public class InstanceManager {
private static final long MIN_HOST_CREATED_IDENTIFIER = 65536;
private static final long CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL = 30000;
private static final String TAG = "InstanceManager";
- private static final String CLOSED_WARNING = "Method was called while the manager was closed.";
/** Interface for listening when a weak reference of an instance is removed from the manager. */
public interface FinalizationListener {
@@ -59,17 +56,18 @@ public interface FinalizationListener {
private final FinalizationListener finalizationListener;
private long nextIdentifier = MIN_HOST_CREATED_IDENTIFIER;
- private boolean isClosed = false;
+ private boolean hasFinalizationListenerStopped = false;
/**
* Instantiate a new manager.
*
- *
When the manager is no longer needed, {@link #close()} must be called.
+ *
When the manager is no longer needed, {@link #stopFinalizationListener()} must be called.
*
* @param finalizationListener the listener for garbage collected weak references.
* @return a new `InstanceManager`.
*/
- public static InstanceManager open(FinalizationListener finalizationListener) {
+ @NonNull
+ public static InstanceManager create(FinalizationListener finalizationListener) {
return new InstanceManager(finalizationListener);
}
@@ -85,15 +83,12 @@ private InstanceManager(FinalizationListener finalizationListener) {
*
* @param identifier the identifier paired to an instance.
* @param the expected return type.
- * @return the removed instance if the manager contains the given identifier, otherwise null if
- * the manager doesn't contain the value or the manager is closed.
+ * @return the removed instance if the manager contains the given identifier, otherwise `null` if
+ * the manager doesn't contain the value.
*/
@Nullable
public T remove(long identifier) {
- if (isClosed()) {
- Log.w(TAG, CLOSED_WARNING);
- return null;
- }
+ logWarningIfFinalizationListenerHasStopped();
return (T) strongInstances.remove(identifier);
}
@@ -111,14 +106,12 @@ public T remove(long identifier) {
*
* @param instance an instance that may be stored in the manager.
* @return the identifier associated with `instance` if the manager contains the value, otherwise
- * null if the manager doesn't contain the value or the manager is closed.
+ * `null` if the manager doesn't contain the value.
*/
@Nullable
public Long getIdentifierForStrongReference(Object instance) {
- if (isClosed()) {
- Log.w(TAG, CLOSED_WARNING);
- return null;
- }
+ logWarningIfFinalizationListenerHasStopped();
+
final Long identifier = identifiers.get(instance);
if (identifier != null) {
strongInstances.put(identifier, instance);
@@ -133,17 +126,12 @@ public Long getIdentifierForStrongReference(Object instance) {
* allows two objects that are equivalent (e.g. the `equals` method returns true and their
* hashcodes are equal) to both be added.
*
- * If the manager is closed, the addition is ignored and a warning is logged.
- *
* @param instance the instance to be stored.
* @param identifier the identifier to be paired with instance. This value must be >= 0 and
* unique.
*/
public void addDartCreatedInstance(Object instance, long identifier) {
- if (isClosed()) {
- Log.w(TAG, CLOSED_WARNING);
- return;
- }
+ logWarningIfFinalizationListenerHasStopped();
addInstance(instance, identifier);
}
@@ -151,14 +139,12 @@ public void addDartCreatedInstance(Object instance, long identifier) {
* Adds a new instance that was instantiated from the host platform.
*
* @param instance the instance to be stored. This must be unique to all other added instances.
- * @return the unique identifier (>= 0) stored with instance. If the manager is closed, returns
- * -1.
+ * @return the unique identifier (>= 0) stored with instance.
*/
public long addHostCreatedInstance(Object instance) {
- if (isClosed()) {
- Log.w(TAG, CLOSED_WARNING);
- return INSTANCE_CLOSED;
- } else if (containsInstance(instance)) {
+ logWarningIfFinalizationListenerHasStopped();
+
+ if (containsInstance(instance)) {
throw new IllegalArgumentException(
String.format("Instance of `%s` has already been added.", instance.getClass()));
}
@@ -173,14 +159,12 @@ public long addHostCreatedInstance(Object instance) {
* @param identifier the identifier associated with an instance.
* @param the expected return type.
* @return the instance associated with `identifier` if the manager contains the value, otherwise
- * null if the manager doesn't contain the value or the manager is closed.
+ * `null` if the manager doesn't contain the value.
*/
@Nullable
public T getInstance(long identifier) {
- if (isClosed()) {
- Log.w(TAG, CLOSED_WARNING);
- return null;
- }
+ logWarningIfFinalizationListenerHasStopped();
+
final WeakReference instance = (WeakReference) weakInstances.get(identifier);
if (instance != null) {
return instance.get();
@@ -192,26 +176,23 @@ public T getInstance(long identifier) {
* Returns whether this manager contains the given `instance`.
*
* @param instance the instance whose presence in this manager is to be tested.
- * @return whether this manager contains the given `instance`. If the manager is closed, returns
- * `false`.
+ * @return whether this manager contains the given `instance`.
*/
public boolean containsInstance(Object instance) {
- if (isClosed()) {
- Log.w(TAG, CLOSED_WARNING);
- return false;
- }
+ logWarningIfFinalizationListenerHasStopped();
return identifiers.containsKey(instance);
}
/**
- * Closes the manager and releases resources.
+ * Stop the periodic run of the {@link FinalizationListener} for instances that have been garbage
+ * collected.
*
- * Methods called after this one will be ignored and log a warning.
+ *
The InstanceManager can continue to be used, but the {@link FinalizationListener} will no
+ * longer be called and methods will log a warning.
*/
- public void close() {
+ public void stopFinalizationListener() {
handler.removeCallbacks(this::releaseAllFinalizedInstances);
- isClosed = true;
- clear();
+ hasFinalizationListenerStopped = true;
}
/**
@@ -227,15 +208,20 @@ public void clear() {
}
/**
- * Whether the manager has released resources and is no longer usable.
+ * Whether the {@link FinalizationListener} is still being called for instances that are garbage
+ * collected.
*
- *
See {@link #close()}.
+ *
See {@link #stopFinalizationListener()}.
*/
- public boolean isClosed() {
- return isClosed;
+ public boolean hasFinalizationListenerStopped() {
+ return hasFinalizationListenerStopped;
}
private void releaseAllFinalizedInstances() {
+ if (hasFinalizationListenerStopped()) {
+ return;
+ }
+
WeakReference reference;
while ((reference = (WeakReference) referenceQueue.poll()) != null) {
final Long identifier = weakReferencesToIdentifiers.remove(reference);
@@ -263,4 +249,10 @@ private void addInstance(Object instance, long identifier) {
weakReferencesToIdentifiers.put(weakReference, identifier);
strongInstances.put(identifier, instance);
}
+
+ private void logWarningIfFinalizationListenerHasStopped() {
+ if (hasFinalizationListenerStopped()) {
+ Log.w(TAG, "The manager was used after calls to the FinalizationListener have been stopped.");
+ }
+ }
}
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java
index 663d0e2f26d..0cd85848e71 100644
--- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java
@@ -32,12 +32,12 @@ public class CameraInfoTest {
@Before
public void setUp() {
- testInstanceManager = InstanceManager.open(identifier -> {});
+ testInstanceManager = InstanceManager.create(identifier -> {});
}
@After
public void tearDown() {
- testInstanceManager.close();
+ testInstanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java
index 2b27e08b579..81ae31f7475 100644
--- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java
@@ -36,12 +36,12 @@ public class CameraSelectorTest {
@Before
public void setUp() {
- testInstanceManager = InstanceManager.open(identifier -> {});
+ testInstanceManager = InstanceManager.create(identifier -> {});
}
@After
public void tearDown() {
- testInstanceManager.close();
+ testInstanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java
index e2135b3945b..af4fb2ce8e6 100644
--- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java
@@ -30,12 +30,12 @@ public class CameraTest {
@Before
public void setUp() {
- testInstanceManager = InstanceManager.open(identifier -> {});
+ testInstanceManager = InstanceManager.create(identifier -> {});
}
@After
public void tearDown() {
- testInstanceManager.close();
+ testInstanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageCaptureTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageCaptureTest.java
index c65bc3d3922..d1881b39a43 100644
--- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageCaptureTest.java
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageCaptureTest.java
@@ -48,14 +48,14 @@ public class ImageCaptureTest {
@Before
public void setUp() throws Exception {
- testInstanceManager = spy(InstanceManager.open(identifier -> {}));
+ testInstanceManager = spy(InstanceManager.create(identifier -> {}));
context = mock(Context.class);
mockedStaticFile = mockStatic(File.class);
}
@After
public void tearDown() {
- testInstanceManager.close();
+ testInstanceManager.stopFinalizationListener();
mockedStaticFile.close();
}
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java
index 34d8add9fc9..7dd5e62acab 100644
--- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java
@@ -15,7 +15,7 @@
public class InstanceManagerTest {
@Test
public void addDartCreatedInstance() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final Object object = new Object();
instanceManager.addDartCreatedInstance(object, 0);
@@ -24,12 +24,12 @@ public void addDartCreatedInstance() {
assertEquals((Long) 0L, instanceManager.getIdentifierForStrongReference(object));
assertTrue(instanceManager.containsInstance(object));
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
public void addHostCreatedInstance() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final Object object = new Object();
long identifier = instanceManager.addHostCreatedInstance(object);
@@ -38,12 +38,12 @@ public void addHostCreatedInstance() {
assertEquals(object, instanceManager.getInstance(identifier));
assertTrue(instanceManager.containsInstance(object));
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
public void removeReturnsRemovedObjectAndClearsIdentifier() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
Object object = new Object();
instanceManager.addDartCreatedInstance(object, 0);
@@ -58,60 +58,12 @@ public void removeReturnsRemovedObjectAndClearsIdentifier() {
assertNull(instanceManager.getInstance(0));
- instanceManager.close();
- }
-
- @Test
- public void removeReturnsNullWhenClosed() {
- final Object object = new Object();
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
- instanceManager.addDartCreatedInstance(object, 0);
- instanceManager.close();
-
- assertNull(instanceManager.remove(0));
- }
-
- @Test
- public void getIdentifierForStrongReferenceReturnsNullWhenClosed() {
- final Object object = new Object();
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
- instanceManager.addDartCreatedInstance(object, 0);
- instanceManager.close();
-
- assertNull(instanceManager.getIdentifierForStrongReference(object));
- }
-
- @Test
- public void addHostCreatedInstanceReturnsNegativeOneWhenClosed() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
- instanceManager.close();
-
- assertEquals(instanceManager.addHostCreatedInstance(new Object()), -1L);
- }
-
- @Test
- public void getInstanceReturnsNullWhenClosed() {
- final Object object = new Object();
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
- instanceManager.addDartCreatedInstance(object, 0);
- instanceManager.close();
-
- assertNull(instanceManager.getInstance(0));
- }
-
- @Test
- public void containsInstanceReturnsFalseWhenClosed() {
- final Object object = new Object();
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
- instanceManager.addDartCreatedInstance(object, 0);
- instanceManager.close();
-
- assertFalse(instanceManager.containsInstance(object));
+ instanceManager.stopFinalizationListener();
}
@Test
public void clear() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final Object instance = new Object();
@@ -121,12 +73,12 @@ public void clear() {
instanceManager.clear();
assertFalse(instanceManager.containsInstance(instance));
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
public void canAddSameObjectWithAddDartCreatedInstance() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final Object instance = new Object();
@@ -138,37 +90,51 @@ public void canAddSameObjectWithAddDartCreatedInstance() {
assertEquals(instanceManager.getInstance(0), instance);
assertEquals(instanceManager.getInstance(1), instance);
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test(expected = IllegalArgumentException.class)
public void cannotAddSameObjectsWithAddHostCreatedInstance() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final Object instance = new Object();
instanceManager.addHostCreatedInstance(instance);
instanceManager.addHostCreatedInstance(instance);
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test(expected = IllegalArgumentException.class)
public void cannotUseIdentifierLessThanZero() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
instanceManager.addDartCreatedInstance(new Object(), -1);
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test(expected = IllegalArgumentException.class)
public void identifiersMustBeUnique() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
instanceManager.addDartCreatedInstance(new Object(), 0);
instanceManager.addDartCreatedInstance(new Object(), 0);
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
+ }
+
+ @Test
+ public void managerIsUsableWhileListenerHasStopped() {
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
+ instanceManager.stopFinalizationListener();
+
+ final Object instance = new Object();
+ final long identifier = 0;
+
+ instanceManager.addDartCreatedInstance(instance, identifier);
+ assertEquals(instanceManager.getInstance(identifier), instance);
+ assertEquals(instanceManager.getIdentifierForStrongReference(instance), (Long) identifier);
+ assertTrue(instanceManager.containsInstance(instance));
}
}
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java
index cce3341aaa8..d05bf5bc46b 100644
--- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java
@@ -11,7 +11,7 @@
public class JavaObjectHostApiTest {
@Test
public void dispose() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final JavaObjectHostApiImpl hostApi = new JavaObjectHostApiImpl(instanceManager);
@@ -27,6 +27,6 @@ public void dispose() {
assertNull(instanceManager.getInstance(0));
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
}
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java
index b76a4c91842..abf99257368 100644
--- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/PreviewTest.java
@@ -49,12 +49,12 @@ public class PreviewTest {
@Before
public void setUp() {
- testInstanceManager = spy(InstanceManager.open(identifier -> {}));
+ testInstanceManager = spy(InstanceManager.create(identifier -> {}));
}
@After
public void tearDown() {
- testInstanceManager.close();
+ testInstanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java
index bfe375854c4..f7987143b98 100644
--- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java
@@ -52,13 +52,13 @@ public class ProcessCameraProviderTest {
@Before
public void setUp() {
- testInstanceManager = InstanceManager.open(identifier -> {});
+ testInstanceManager = InstanceManager.create(identifier -> {});
context = ApplicationProvider.getApplicationContext();
}
@After
public void tearDown() {
- testInstanceManager.close();
+ testInstanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/camera/camera_android_camerax/example/android/app/build.gradle b/packages/camera/camera_android_camerax/example/android/app/build.gradle
index 2d59a59a420..e2001f9ab77 100644
--- a/packages/camera/camera_android_camerax/example/android/app/build.gradle
+++ b/packages/camera/camera_android_camerax/example/android/app/build.gradle
@@ -62,5 +62,6 @@ flutter {
dependencies {
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.2.0'
- api 'androidx.test:core:1.2.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ api 'androidx.test:core:1.4.0'
}
diff --git a/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/cameraxexample/InstanceManagerTest.java b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/cameraxexample/InstanceManagerTest.java
new file mode 100644
index 00000000000..5e4454de80d
--- /dev/null
+++ b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/cameraxexample/InstanceManagerTest.java
@@ -0,0 +1,43 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.cameraexample;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import io.flutter.plugins.camerax.InstanceManager;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class InstanceManagerTest {
+ @Test
+ public void managerDoesNotTriggerFinalizationListenerWhenStopped() throws InterruptedException {
+ final boolean[] callbackTriggered = {false};
+ final InstanceManager instanceManager =
+ InstanceManager.create(identifier -> callbackTriggered[0] = true);
+ instanceManager.stopFinalizationListener();
+
+ Object object = new Object();
+ instanceManager.addDartCreatedInstance(object, 0);
+
+ assertEquals(object, instanceManager.remove(0));
+
+ // To allow for object to be garbage collected.
+ //noinspection UnusedAssignment
+ object = null;
+
+ Runtime.getRuntime().gc();
+
+ // Wait for the interval after finalized callbacks are made for garbage collected objects.
+ // See InstanceManager.CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL.
+ Thread.sleep(30000);
+
+ assertNull(instanceManager.getInstance(0));
+ assertFalse(callbackTriggered[0]);
+ }
+}
diff --git a/packages/camera/camera_android_camerax/example/android/build.gradle b/packages/camera/camera_android_camerax/example/android/build.gradle
index 7c909c6116c..45cb407e8d0 100644
--- a/packages/camera/camera_android_camerax/example/android/build.gradle
+++ b/packages/camera/camera_android_camerax/example/android/build.gradle
@@ -26,7 +26,7 @@ subprojects {
project.evaluationDependsOn(':app')
}
-task clean(type: Delete) {
+tasks.register("clean", Delete) {
delete rootProject.buildDir
}
diff --git a/packages/camera/camera_android_camerax/example/pubspec.yaml b/packages/camera/camera_android_camerax/example/pubspec.yaml
index 1a055e7913c..8e6c02572b9 100644
--- a/packages/camera/camera_android_camerax/example/pubspec.yaml
+++ b/packages/camera/camera_android_camerax/example/pubspec.yaml
@@ -20,6 +20,7 @@ dependencies:
video_player: ^2.4.10
dev_dependencies:
+ espresso: ^0.2.0
flutter_test:
sdk: flutter
integration_test:
diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
index 1e1796de73b..f63588727f8 100644
--- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 3.5.2
+
+* Updates internal Java InstanceManager to only stop finalization callbacks when stopped.
+
## 3.5.1
* Updates pigeon dev dependency to `9.2.4`.
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java
index 5fa19d21ce2..38f4044c51e 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java
@@ -31,17 +31,13 @@
*/
@SuppressWarnings("unchecked")
public class InstanceManager {
- /// Constant returned from #addHostCreatedInstance() if the manager is closed.
- public static final int INSTANCE_CLOSED = -1;
-
// Identifiers are locked to a specific range to avoid collisions with objects
// created simultaneously from Dart.
// Host uses identifiers >= 2^16 and Dart is expected to use values n where,
// 0 <= n < 2^16.
private static final long MIN_HOST_CREATED_IDENTIFIER = 65536;
- private static final long CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL = 30000;
+ private static final long CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL = 3000;
private static final String TAG = "InstanceManager";
- private static final String CLOSED_WARNING = "Method was called while the manager was closed.";
/** Interface for listening when a weak reference of an instance is removed from the manager. */
public interface FinalizationListener {
@@ -60,18 +56,18 @@ public interface FinalizationListener {
private final FinalizationListener finalizationListener;
private long nextIdentifier = MIN_HOST_CREATED_IDENTIFIER;
- private boolean isClosed = false;
+ private boolean hasFinalizationListenerStopped = false;
/**
* Instantiate a new manager.
*
- * When the manager is no longer needed, {@link #close()} must be called.
+ *
When the manager is no longer needed, {@link #stopFinalizationListener()} must be called.
*
* @param finalizationListener the listener for garbage collected weak references.
* @return a new `InstanceManager`.
*/
@NonNull
- public static InstanceManager open(@NonNull FinalizationListener finalizationListener) {
+ public static InstanceManager create(@NonNull FinalizationListener finalizationListener) {
return new InstanceManager(finalizationListener);
}
@@ -87,14 +83,12 @@ private InstanceManager(FinalizationListener finalizationListener) {
*
* @param identifier the identifier paired to an instance.
* @param the expected return type.
- * @return the removed instance if the manager contains the given identifier, otherwise null if
- * the manager doesn't contain the value or the manager is closed.
+ * @return the removed instance if the manager contains the given identifier, otherwise `null` if
+ * the manager doesn't contain the value.
*/
@Nullable
public T remove(long identifier) {
- if (assertNotClosed()) {
- return null;
- }
+ logWarningIfFinalizationListenerHasStopped();
return (T) strongInstances.remove(identifier);
}
@@ -112,13 +106,12 @@ public T remove(long identifier) {
*
* @param instance an instance that may be stored in the manager.
* @return the identifier associated with `instance` if the manager contains the value, otherwise
- * null if the manager doesn't contain the value or the manager is closed.
+ * `null` if the manager doesn't contain the value.
*/
@Nullable
public Long getIdentifierForStrongReference(@Nullable Object instance) {
- if (assertNotClosed()) {
- return null;
- }
+ logWarningIfFinalizationListenerHasStopped();
+
final Long identifier = identifiers.get(instance);
if (identifier != null) {
strongInstances.put(identifier, instance);
@@ -133,16 +126,12 @@ public Long getIdentifierForStrongReference(@Nullable Object instance) {
* allows two objects that are equivalent (e.g. the `equals` method returns true and their
* hashcodes are equal) to both be added.
*
- * If the manager is closed, the addition is ignored and a warning is logged.
- *
* @param instance the instance to be stored.
* @param identifier the identifier to be paired with instance. This value must be >= 0 and
* unique.
*/
public void addDartCreatedInstance(@NonNull Object instance, long identifier) {
- if (assertNotClosed()) {
- return;
- }
+ logWarningIfFinalizationListenerHasStopped();
addInstance(instance, identifier);
}
@@ -150,13 +139,10 @@ public void addDartCreatedInstance(@NonNull Object instance, long identifier) {
* Adds a new instance that was instantiated from the host platform.
*
* @param instance the instance to be stored. This must be unique to all other added instances.
- * @return the unique identifier stored with instance. If the manager is closed, returns -1.
- * Otherwise, returns a value >= 0.
+ * @return the unique identifier (>= 0) stored with instance.
*/
public long addHostCreatedInstance(@NonNull Object instance) {
- if (assertNotClosed()) {
- return INSTANCE_CLOSED;
- }
+ logWarningIfFinalizationListenerHasStopped();
if (containsInstance(instance)) {
throw new IllegalArgumentException(
@@ -173,13 +159,11 @@ public long addHostCreatedInstance(@NonNull Object instance) {
* @param identifier the identifier associated with an instance.
* @param the expected return type.
* @return the instance associated with `identifier` if the manager contains the value, otherwise
- * null if the manager doesn't contain the value or the manager is closed.
+ * `null` if the manager doesn't contain the value.
*/
@Nullable
public T getInstance(long identifier) {
- if (assertNotClosed()) {
- return null;
- }
+ logWarningIfFinalizationListenerHasStopped();
final WeakReference instance = (WeakReference) weakInstances.get(identifier);
if (instance != null) {
@@ -192,25 +176,23 @@ public T getInstance(long identifier) {
* Returns whether this manager contains the given `instance`.
*
* @param instance the instance whose presence in this manager is to be tested.
- * @return whether this manager contains the given `instance`. If the manager is closed, returns
- * `false`.
+ * @return whether this manager contains the given `instance`.
*/
public boolean containsInstance(@Nullable Object instance) {
- if (assertNotClosed()) {
- return false;
- }
+ logWarningIfFinalizationListenerHasStopped();
return identifiers.containsKey(instance);
}
/**
- * Closes the manager and releases resources.
+ * Stop the periodic run of the {@link FinalizationListener} for instances that have been garbage
+ * collected.
*
- * Methods called after this one will be ignored and log a warning.
+ *
The InstanceManager can continue to be used, but the {@link FinalizationListener} will no
+ * longer be called and methods will log a warning.
*/
- public void close() {
+ public void stopFinalizationListener() {
handler.removeCallbacks(this::releaseAllFinalizedInstances);
- isClosed = true;
- clear();
+ hasFinalizationListenerStopped = true;
}
/**
@@ -226,15 +208,20 @@ public void clear() {
}
/**
- * Whether the manager has released resources and is no longer usable.
+ * Whether the {@link FinalizationListener} is still being called for instances that are garbage
+ * collected.
*
- *
See {@link #close()}.
+ *
See {@link #stopFinalizationListener()}.
*/
- public boolean isClosed() {
- return isClosed;
+ public boolean hasFinalizationListenerStopped() {
+ return hasFinalizationListenerStopped;
}
private void releaseAllFinalizedInstances() {
+ if (hasFinalizationListenerStopped()) {
+ return;
+ }
+
WeakReference reference;
while ((reference = (WeakReference) referenceQueue.poll()) != null) {
final Long identifier = weakReferencesToIdentifiers.remove(reference);
@@ -263,11 +250,9 @@ private void addInstance(Object instance, long identifier) {
strongInstances.put(identifier, instance);
}
- private boolean assertNotClosed() {
- if (isClosed()) {
- Log.w(TAG, CLOSED_WARNING);
- return true;
+ private void logWarningIfFinalizationListenerHasStopped() {
+ if (hasFinalizationListenerStopped()) {
+ Log.w(TAG, "The manager was used after calls to the FinalizationListener have been stopped.");
}
- return false;
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java
index d1c239c3636..1434e57c080 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java
@@ -78,7 +78,7 @@ private void setUp(
Context context,
FlutterAssetManager flutterAssetManager) {
instanceManager =
- InstanceManager.open(
+ InstanceManager.create(
identifier ->
new GeneratedAndroidWebView.JavaObjectFlutterApi(binaryMessenger)
.dispose(identifier, reply -> {}));
@@ -145,7 +145,7 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
if (instanceManager != null) {
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
instanceManager = null;
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java
index caffbb9a95e..ae0029648eb 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java
@@ -29,7 +29,7 @@ public class DownloadListenerTest {
@Before
public void setUp() {
- instanceManager = InstanceManager.open(identifier -> {});
+ instanceManager = InstanceManager.create(identifier -> {});
final DownloadListenerCreator downloadListenerCreator =
new DownloadListenerCreator() {
@@ -48,7 +48,7 @@ public DownloadListenerImpl createDownloadListener(
@After
public void tearDown() {
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FileChooserParamsTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FileChooserParamsTest.java
index 3172ea4330c..0fb21cc474c 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FileChooserParamsTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/FileChooserParamsTest.java
@@ -35,12 +35,12 @@ public class FileChooserParamsTest {
@Before
public void setUp() {
- instanceManager = InstanceManager.open(identifier -> {});
+ instanceManager = InstanceManager.create(identifier -> {});
}
@After
public void tearDown() {
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/InstanceManagerTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/InstanceManagerTest.java
index 2bda2d12c75..2f2ea7769dd 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/InstanceManagerTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/InstanceManagerTest.java
@@ -15,7 +15,7 @@
public class InstanceManagerTest {
@Test
public void addDartCreatedInstance() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final Object object = new Object();
instanceManager.addDartCreatedInstance(object, 0);
@@ -24,12 +24,12 @@ public void addDartCreatedInstance() {
assertEquals((Long) 0L, instanceManager.getIdentifierForStrongReference(object));
assertTrue(instanceManager.containsInstance(object));
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
public void addHostCreatedInstance() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final Object object = new Object();
long identifier = instanceManager.addHostCreatedInstance(object);
@@ -38,12 +38,12 @@ public void addHostCreatedInstance() {
assertEquals(object, instanceManager.getInstance(identifier));
assertTrue(instanceManager.containsInstance(object));
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
public void remove() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
Object object = new Object();
instanceManager.addDartCreatedInstance(object, 0);
@@ -58,60 +58,12 @@ public void remove() {
assertNull(instanceManager.getInstance(0));
- instanceManager.close();
- }
-
- @Test
- public void removeReturnsNullWhenClosed() {
- final Object object = new Object();
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
- instanceManager.addDartCreatedInstance(object, 0);
- instanceManager.close();
-
- assertNull(instanceManager.remove(0));
- }
-
- @Test
- public void getIdentifierForStrongReferenceReturnsNullWhenClosed() {
- final Object object = new Object();
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
- instanceManager.addDartCreatedInstance(object, 0);
- instanceManager.close();
-
- assertNull(instanceManager.getIdentifierForStrongReference(object));
- }
-
- @Test
- public void addHostCreatedInstanceReturnsNegativeOneWhenClosed() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
- instanceManager.close();
-
- assertEquals(instanceManager.addHostCreatedInstance(new Object()), -1L);
- }
-
- @Test
- public void getInstanceReturnsNullWhenClosed() {
- final Object object = new Object();
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
- instanceManager.addDartCreatedInstance(object, 0);
- instanceManager.close();
-
- assertNull(instanceManager.getInstance(0));
- }
-
- @Test
- public void containsInstanceReturnsFalseWhenClosed() {
- final Object object = new Object();
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
- instanceManager.addDartCreatedInstance(object, 0);
- instanceManager.close();
-
- assertFalse(instanceManager.containsInstance(object));
+ instanceManager.stopFinalizationListener();
}
@Test
public void clear() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final Object instance = new Object();
@@ -121,12 +73,12 @@ public void clear() {
instanceManager.clear();
assertFalse(instanceManager.containsInstance(instance));
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
public void canAddSameObjectWithAddDartCreatedInstance() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final Object instance = new Object();
@@ -138,37 +90,51 @@ public void canAddSameObjectWithAddDartCreatedInstance() {
assertEquals(instanceManager.getInstance(0), instance);
assertEquals(instanceManager.getInstance(1), instance);
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test(expected = IllegalArgumentException.class)
public void cannotAddSameObjectsWithAddHostCreatedInstance() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final Object instance = new Object();
instanceManager.addHostCreatedInstance(instance);
instanceManager.addHostCreatedInstance(instance);
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test(expected = IllegalArgumentException.class)
public void cannotUseIdentifierLessThanZero() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
instanceManager.addDartCreatedInstance(new Object(), -1);
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test(expected = IllegalArgumentException.class)
public void identifiersMustBeUnique() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
instanceManager.addDartCreatedInstance(new Object(), 0);
instanceManager.addDartCreatedInstance(new Object(), 0);
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
+ }
+
+ @Test
+ public void managerIsUsableWhileListenerHasStopped() {
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
+ instanceManager.stopFinalizationListener();
+
+ final Object instance = new Object();
+ final long identifier = 0;
+
+ instanceManager.addDartCreatedInstance(instance, identifier);
+ assertEquals(instanceManager.getInstance(identifier), instance);
+ assertEquals(instanceManager.getIdentifierForStrongReference(instance), (Long) identifier);
+ assertTrue(instanceManager.containsInstance(instance));
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiTest.java
index 8ac349e7641..88c759d1082 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiTest.java
@@ -11,7 +11,7 @@
public class JavaObjectHostApiTest {
@Test
public void dispose() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final JavaObjectHostApiImpl hostApi = new JavaObjectHostApiImpl(instanceManager);
@@ -27,6 +27,6 @@ public void dispose() {
assertNull(instanceManager.getInstance(0));
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java
index 391c6c833e5..0c9647a8cb9 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java
@@ -30,7 +30,7 @@ public class JavaScriptChannelTest {
@Before
public void setUp() {
- instanceManager = InstanceManager.open(identifier -> {});
+ instanceManager = InstanceManager.create(identifier -> {});
final JavaScriptChannelCreator javaScriptChannelCreator =
new JavaScriptChannelCreator() {
@@ -57,7 +57,7 @@ public JavaScriptChannel createJavaScriptChannel(
@After
public void tearDown() {
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java
index f7a174091a9..ccd26f0cdc3 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java
@@ -46,7 +46,7 @@ public class WebChromeClientTest {
@Before
public void setUp() {
- instanceManager = InstanceManager.open(identifier -> {});
+ instanceManager = InstanceManager.create(identifier -> {});
final WebChromeClientCreator webChromeClientCreator =
new WebChromeClientCreator() {
@@ -66,7 +66,7 @@ public WebChromeClientImpl createWebChromeClient(
@After
public void tearDown() {
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebSettingsTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebSettingsTest.java
index 5c7c7a6f470..4a299766988 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebSettingsTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebSettingsTest.java
@@ -32,7 +32,7 @@ public class WebSettingsTest {
@Before
public void setUp() {
- testInstanceManager = InstanceManager.open(identifier -> {});
+ testInstanceManager = InstanceManager.create(identifier -> {});
when(mockWebSettingsCreator.createWebSettings(any())).thenReturn(mockWebSettings);
testHostApiImpl = new WebSettingsHostApiImpl(testInstanceManager, mockWebSettingsCreator);
@@ -43,7 +43,7 @@ public void setUp() {
@After
public void tearDown() {
- testInstanceManager.close();
+ testInstanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java
index b4f38f1702d..5756ce7a174 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java
@@ -28,7 +28,7 @@ public class WebStorageHostApiImplTest {
@Before
public void setUp() {
- testInstanceManager = InstanceManager.open(identifier -> {});
+ testInstanceManager = InstanceManager.create(identifier -> {});
when(mockWebStorageCreator.createWebStorage()).thenReturn(mockWebStorage);
testHostApiImpl = new WebStorageHostApiImpl(testInstanceManager, mockWebStorageCreator);
@@ -37,7 +37,7 @@ public void setUp() {
@After
public void tearDown() {
- testInstanceManager.close();
+ testInstanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java
index 230e81441a3..8e6b58149d0 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java
@@ -42,7 +42,7 @@ public class WebViewClientTest {
@Before
public void setUp() {
- instanceManager = InstanceManager.open(identifier -> {});
+ instanceManager = InstanceManager.create(identifier -> {});
final WebViewClientCreator webViewClientCreator =
new WebViewClientCreator() {
@@ -62,7 +62,7 @@ public WebViewClient createWebViewClient(
@After
public void tearDown() {
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java
index e0e641ac35a..00887275577 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java
@@ -54,7 +54,7 @@ public class WebViewTest {
@Before
public void setUp() {
- testInstanceManager = InstanceManager.open(identifier -> {});
+ testInstanceManager = InstanceManager.create(identifier -> {});
when(mockWebViewProxy.createWebView(mockContext, mockBinaryMessenger, testInstanceManager))
.thenReturn(mockWebView);
@@ -66,7 +66,7 @@ public void setUp() {
@After
public void tearDown() {
- testInstanceManager.close();
+ testInstanceManager.stopFinalizationListener();
}
@Test
@@ -325,7 +325,7 @@ public void destroy() {
@Test
public void flutterApiCreate() {
- final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
+ final InstanceManager instanceManager = InstanceManager.create(identifier -> {});
final WebViewFlutterApiImpl flutterApiImpl =
new WebViewFlutterApiImpl(mockBinaryMessenger, instanceManager);
@@ -339,7 +339,7 @@ public void flutterApiCreate() {
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(mockWebView));
verify(mockFlutterApi).create(eq(instanceIdentifier), any());
- instanceManager.close();
+ instanceManager.stopFinalizationListener();
}
@Test
diff --git a/packages/webview_flutter/webview_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/webviewflutterexample/InstanceManagerTest.java b/packages/webview_flutter/webview_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/webviewflutterexample/InstanceManagerTest.java
new file mode 100644
index 00000000000..54cff982d4b
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/webviewflutterexample/InstanceManagerTest.java
@@ -0,0 +1,43 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.webviewflutterexample;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import io.flutter.plugins.webviewflutter.InstanceManager;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class InstanceManagerTest {
+ @Test
+ public void managerDoesNotTriggerFinalizationListenerWhenStopped() throws InterruptedException {
+ final boolean[] callbackTriggered = {false};
+ final InstanceManager instanceManager =
+ InstanceManager.create(identifier -> callbackTriggered[0] = true);
+ instanceManager.stopFinalizationListener();
+
+ Object object = new Object();
+ instanceManager.addDartCreatedInstance(object, 0);
+
+ assertEquals(object, instanceManager.remove(0));
+
+ // To allow for object to be garbage collected.
+ //noinspection UnusedAssignment
+ object = null;
+
+ Runtime.getRuntime().gc();
+
+ // Wait for the interval after finalized callbacks are made for garbage collected objects.
+ // See InstanceManager.CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL.
+ Thread.sleep(30000);
+
+ assertNull(instanceManager.getInstance(0));
+ assertFalse(callbackTriggered[0]);
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
index 7fca5618dd2..bd22b7673ee 100644
--- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
@@ -2,7 +2,7 @@ name: webview_flutter_android
description: A Flutter plugin that provides a WebView widget on Android.
repository: https://github.com/flutter/packages/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: 3.5.1
+version: 3.5.2
environment:
sdk: ">=2.18.0 <4.0.0"