mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Prevent that removeActivityResultListener() is causing an ConcurrentModificationException (#19702)
This commit is contained in:
parent
8c511aae14
commit
f21cd8aedf
@ -686,7 +686,7 @@ class FlutterEnginePluginRegistry
|
||||
boolean onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
boolean didConsumeResult = false;
|
||||
for (io.flutter.plugin.common.PluginRegistry.ActivityResultListener listener :
|
||||
onActivityResultListeners) {
|
||||
new HashSet<>(onActivityResultListeners)) {
|
||||
didConsumeResult =
|
||||
listener.onActivityResult(requestCode, resultCode, data) || didConsumeResult;
|
||||
}
|
||||
|
||||
@ -6,11 +6,18 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import io.flutter.embedding.engine.loader.FlutterLoader;
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||
import io.flutter.plugin.common.PluginRegistry;
|
||||
import io.flutter.plugin.platform.PlatformViewsController;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
@ -58,6 +65,49 @@ public class FlutterEnginePluginRegistryTest {
|
||||
assertEquals(0, fakePlugin2.detachmentCallCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void activityResultListenerCanBeRemovedFromListener() {
|
||||
Context context = mock(Context.class);
|
||||
|
||||
FlutterEngine flutterEngine = mock(FlutterEngine.class);
|
||||
PlatformViewsController platformViewsController = mock(PlatformViewsController.class);
|
||||
when(flutterEngine.getPlatformViewsController()).thenReturn(platformViewsController);
|
||||
|
||||
FlutterLoader flutterLoader = mock(FlutterLoader.class);
|
||||
Activity activity = mock(Activity.class);
|
||||
Lifecycle lifecycle = mock(Lifecycle.class);
|
||||
Intent intent = mock(Intent.class);
|
||||
AtomicBoolean isFirstCall = new AtomicBoolean(true);
|
||||
|
||||
// setup the environment to get the required internal data
|
||||
FlutterEnginePluginRegistry registry =
|
||||
new FlutterEnginePluginRegistry(context, flutterEngine, flutterLoader);
|
||||
FakeActivityAwareFlutterPlugin fakePlugin = new FakeActivityAwareFlutterPlugin();
|
||||
registry.add(fakePlugin);
|
||||
registry.attachToActivity(activity, lifecycle);
|
||||
|
||||
// The binding is now available via `fakePlugin.binding`: Create and add the listeners
|
||||
FakeActivityResultListener listener1 =
|
||||
new FakeActivityResultListener(isFirstCall, fakePlugin.binding);
|
||||
FakeActivityResultListener listener2 =
|
||||
new FakeActivityResultListener(isFirstCall, fakePlugin.binding);
|
||||
|
||||
fakePlugin.binding.addActivityResultListener(listener1);
|
||||
fakePlugin.binding.addActivityResultListener(listener2);
|
||||
|
||||
// fire the onActivityResult which should invoke both listeners
|
||||
registry.onActivityResult(0, 0, intent);
|
||||
|
||||
assertEquals(1, listener1.callCount);
|
||||
assertEquals(1, listener2.callCount);
|
||||
|
||||
// fire it again to check if the first called listener was removed
|
||||
registry.onActivityResult(0, 0, intent);
|
||||
|
||||
// The order of the listeners in the HashSet is random: So just check the sum of calls
|
||||
assertEquals(3, listener1.callCount + listener2.callCount);
|
||||
}
|
||||
|
||||
private static class FakeFlutterPlugin implements FlutterPlugin {
|
||||
public int attachmentCallCount = 0;
|
||||
public int detachmentCallCount = 0;
|
||||
@ -72,4 +122,50 @@ public class FlutterEnginePluginRegistryTest {
|
||||
detachmentCallCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FakeActivityAwareFlutterPlugin implements FlutterPlugin, ActivityAware {
|
||||
public ActivityPluginBinding binding;
|
||||
|
||||
@Override
|
||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {}
|
||||
|
||||
@Override
|
||||
public void onAttachedToActivity(final ActivityPluginBinding binding) {
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromActivityForConfigChanges() {}
|
||||
|
||||
@Override
|
||||
public void onReattachedToActivityForConfigChanges(final ActivityPluginBinding binding) {}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromActivity() {}
|
||||
}
|
||||
|
||||
private static class FakeActivityResultListener implements PluginRegistry.ActivityResultListener {
|
||||
public int callCount = 0;
|
||||
private final AtomicBoolean isFirstCall;
|
||||
private final ActivityPluginBinding binding;
|
||||
|
||||
public FakeActivityResultListener(AtomicBoolean isFirstCall, ActivityPluginBinding binding) {
|
||||
this.isFirstCall = isFirstCall;
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActivityResult(
|
||||
final int requestCode, final int resultCode, final Intent data) {
|
||||
callCount++;
|
||||
if (isFirstCall.get()) {
|
||||
isFirstCall.set(false);
|
||||
binding.removeActivityResultListener(this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user