diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 87c5731531bc8..c4b3fcac32865 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -708,7 +708,6 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/app/FlutterPluginRegist FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/AndroidKeyProcessor.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/DrawableSplashScreen.java -FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/ExclusiveAppComponent.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterActivityLaunchConfigs.java @@ -728,7 +727,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/Splas FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/TransparencyMode.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineCache.java -FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterOverlaySurface.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 52373e3c74ffa..3443dba51d3ae 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -128,7 +128,6 @@ android_java_sources = [ "io/flutter/embedding/android/AndroidKeyProcessor.java", "io/flutter/embedding/android/AndroidTouchProcessor.java", "io/flutter/embedding/android/DrawableSplashScreen.java", - "io/flutter/embedding/android/ExclusiveAppComponent.java", "io/flutter/embedding/android/FlutterActivity.java", "io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java", "io/flutter/embedding/android/FlutterActivityLaunchConfigs.java", @@ -148,7 +147,7 @@ android_java_sources = [ "io/flutter/embedding/android/TransparencyMode.java", "io/flutter/embedding/engine/FlutterEngine.java", "io/flutter/embedding/engine/FlutterEngineCache.java", - "io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java", + "io/flutter/embedding/engine/FlutterEnginePluginRegistry.java", "io/flutter/embedding/engine/FlutterJNI.java", "io/flutter/embedding/engine/FlutterOverlaySurface.java", "io/flutter/embedding/engine/FlutterShellArgs.java", @@ -430,7 +429,7 @@ action("robolectric_tests") { "test/io/flutter/embedding/android/FlutterViewTest.java", "test/io/flutter/embedding/android/RobolectricFlutterActivity.java", "test/io/flutter/embedding/engine/FlutterEngineCacheTest.java", - "test/io/flutter/embedding/engine/FlutterEngineConnectionRegistryTest.java", + "test/io/flutter/embedding/engine/FlutterEnginePluginRegistryTest.java", "test/io/flutter/embedding/engine/FlutterEngineTest.java", "test/io/flutter/embedding/engine/FlutterJNITest.java", "test/io/flutter/embedding/engine/FlutterShellArgsTest.java", diff --git a/shell/platform/android/io/flutter/embedding/android/ExclusiveAppComponent.java b/shell/platform/android/io/flutter/embedding/android/ExclusiveAppComponent.java deleted file mode 100644 index e1c356a6ea6b4..0000000000000 --- a/shell/platform/android/io/flutter/embedding/android/ExclusiveAppComponent.java +++ /dev/null @@ -1,35 +0,0 @@ -// 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.embedding.android; - -import androidx.annotation.NonNull; - -/** - * An Android App Component exclusively attached to a {@link - * io.flutter.embedding.engine.FlutterEngine}. - * - *

An exclusive App Component's {@link #detachFromFlutterEngine} is invoked when another App - * Component is becoming attached to the {@link io.flutter.embedding.engine.FlutterEngine}. - * - *

The term "App Component" refer to the 4 component types: Activity, Service, Broadcast - * Receiver, and Content Provider, as defined in - * https://developer.android.com/guide/components/fundamentals. - * - * @param The App Component behind this exclusive App Component. - */ -public interface ExclusiveAppComponent { - /** - * Called when another App Component is about to become attached to the {@link - * io.flutter.embedding.engine.FlutterEngine} this App Component is currently attached to. - * - *

This App Component's connections to the {@link io.flutter.embedding.engine.FlutterEngine} - * are still valid at the moment of this call. - */ - void detachFromFlutterEngine(); - - /** Retrieve the App Component behind this exclusive App Component. */ - @NonNull - T getAppComponent(); -} diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 66c2acd8e886b..22993f08fe58e 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -560,102 +560,56 @@ protected void onPause() { @Override protected void onStop() { super.onStop(); - if (stillAttachedForEvent("onStop")) { - delegate.onStop(); - } + delegate.onStop(); lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - if (stillAttachedForEvent("onSaveInstanceState")) { - delegate.onSaveInstanceState(outState); - } - } - - /** - * Irreversibly release this activity's control of the {@link FlutterEngine} and its - * subcomponents. - * - *

Calling will disconnect this activity's view from the Flutter renderer, disconnect this - * activity from plugins' {@link ActivityControlSurface}, and stop system channel messages from - * this activity. - * - *

After calling, this activity should be disposed immediately and not be re-used. - */ - private void release() { - delegate.onDestroyView(); - delegate.onDetach(); - delegate.release(); - delegate = null; - } - - @Override - public void detachFromFlutterEngine() { - Log.v( - TAG, - "FlutterActivity " - + this - + " connection to the engine " - + getFlutterEngine() - + " evicted by another attaching activity"); - release(); + delegate.onSaveInstanceState(outState); } @Override protected void onDestroy() { super.onDestroy(); - if (stillAttachedForEvent("onDestroy")) { - release(); - } + delegate.onDestroyView(); + delegate.onDetach(); lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (stillAttachedForEvent("onActivityResult")) { - delegate.onActivityResult(requestCode, resultCode, data); - } + delegate.onActivityResult(requestCode, resultCode, data); } @Override protected void onNewIntent(@NonNull Intent intent) { // TODO(mattcarroll): change G3 lint rule that forces us to call super super.onNewIntent(intent); - if (stillAttachedForEvent("onNewIntent")) { - delegate.onNewIntent(intent); - } + delegate.onNewIntent(intent); } @Override public void onBackPressed() { - if (stillAttachedForEvent("onBackPressed")) { - delegate.onBackPressed(); - } + delegate.onBackPressed(); } @Override public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - if (stillAttachedForEvent("onRequestPermissionsResult")) { - delegate.onRequestPermissionsResult(requestCode, permissions, grantResults); - } + delegate.onRequestPermissionsResult(requestCode, permissions, grantResults); } @Override public void onUserLeaveHint() { - if (stillAttachedForEvent("onUserLeaveHint")) { - delegate.onUserLeaveHint(); - } + delegate.onUserLeaveHint(); } @Override public void onTrimMemory(int level) { super.onTrimMemory(level); - if (stillAttachedForEvent("onTrimMemory")) { - delegate.onTrimMemory(level); - } + delegate.onTrimMemory(level); } /** @@ -954,7 +908,7 @@ public void cleanUpFlutterEngine(@NonNull FlutterEngine flutterEngine) { *

Returning false from this method does not preclude a {@link FlutterEngine} from being * attaching to a {@code FlutterActivity} - it just prevents the attachment from happening * automatically. A developer can choose to subclass {@code FlutterActivity} and then invoke - * {@link ActivityControlSurface#attachToActivity(ExclusiveAppComponent, Lifecycle)} and {@link + * {@link ActivityControlSurface#attachToActivity(Activity, Lifecycle)} and {@link * ActivityControlSurface#detachFromActivity()} at the desired times. * *

One reason that a developer might choose to manually manage the relationship between the @@ -1007,12 +961,4 @@ public boolean shouldRestoreAndSaveState() { } return true; } - - private boolean stillAttachedForEvent(String event) { - if (delegate == null) { - Log.v(TAG, "FlutterActivity " + hashCode() + " " + event + " called after release."); - return false; - } - return true; - } } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index 78585172dd683..e4cad301c21a0 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java @@ -62,7 +62,7 @@ * the same form. Do not use this class as a convenient shortcut for any other * behavior. */ -/* package */ class FlutterActivityAndFragmentDelegate implements ExclusiveAppComponent { +/* package */ final class FlutterActivityAndFragmentDelegate { private static final String TAG = "FlutterActivityAndFragmentDelegate"; private static final String FRAMEWORK_RESTORATION_BUNDLE_KEY = "framework"; private static final String PLUGINS_RESTORATION_BUNDLE_KEY = "plugins"; @@ -154,6 +154,14 @@ void onAttach(@NonNull Context context) { setupFlutterEngine(); } + // Regardless of whether or not a FlutterEngine already existed, the PlatformPlugin + // is bound to a specific Activity. Therefore, it needs to be created and configured + // every time this Fragment attaches to a new Activity. + // TODO(mattcarroll): the PlatformPlugin needs to be reimagined because it implicitly takes + // control of the entire window. This is unacceptable for non-fullscreen + // use-cases. + platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine); + if (host.shouldAttachEngineToActivity()) { // Notify any plugins that are currently attached to our FlutterEngine that they // are now attached to an Activity. @@ -164,32 +172,15 @@ void onAttach(@NonNull Context context) { // which means there shouldn't be any possibility for the Fragment Lifecycle to get out of // sync with the Activity. We use the Fragment's Lifecycle because it is possible that the // attached Activity is not a LifecycleOwner. - Log.v(TAG, "Attaching FlutterEngine to the Activity that owns this delegate."); - flutterEngine.getActivityControlSurface().attachToActivity(this, host.getLifecycle()); + Log.v(TAG, "Attaching FlutterEngine to the Activity that owns this Fragment."); + flutterEngine + .getActivityControlSurface() + .attachToActivity(host.getActivity(), host.getLifecycle()); } - // Regardless of whether or not a FlutterEngine already existed, the PlatformPlugin - // is bound to a specific Activity. Therefore, it needs to be created and configured - // every time this Fragment attaches to a new Activity. - // TODO(mattcarroll): the PlatformPlugin needs to be reimagined because it implicitly takes - // control of the entire window. This is unacceptable for non-fullscreen - // use-cases. - platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine); - host.configureFlutterEngine(flutterEngine); } - @Override - public @NonNull Activity getAppComponent() { - final Activity activity = host.getActivity(); - if (activity == null) { - throw new AssertionError( - "FlutterActivityAndFragmentDelegate's getAppComponent should only " - + "be queried after onAttach, when the host's activity should always be non-null"); - } - return activity; - } - /** * Obtains a reference to a FlutterEngine to back this delegate and its {@code host}. * @@ -489,24 +480,6 @@ void onSaveInstanceState(@Nullable Bundle bundle) { } } - @Override - public void detachFromFlutterEngine() { - if (host.shouldDestroyEngineWithHost()) { - // The host owns the engine and should never have its engine taken by another exclusive - // activity. - throw new AssertionError( - "The internal FlutterEngine created by " - + host - + " has been attached to by another activity. To persist a FlutterEngine beyond the " - + "ownership of this activity, explicitly create a FlutterEngine"); - } - - // Default, but customizable, behavior is for the host to call {@link #onDetach} - // deterministically as to not mix more events during the lifecycle of the next exclusive - // activity. - host.detachFromFlutterEngine(); - } - /** * Invoke this from {@code Activity#onDestroy()} or {@code Fragment#onDetach()}. * @@ -768,15 +741,6 @@ private void ensureAlive() { */ boolean shouldDestroyEngineWithHost(); - /** - * Callback called when the {@link FlutterEngine} has been attached to by another activity - * before this activity was destroyed. - * - *

The expected behavior is for this activity to synchronously stop using the {@link - * FlutterEngine} to avoid lifecycle crosstalk with the new activity. - */ - void detachFromFlutterEngine(); - /** Returns the Dart entrypoint that should run when a new {@link FlutterEngine} is created. */ @NonNull String getDartEntrypointFunctionName(); diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterEngineConfigurator.java b/shell/platform/android/io/flutter/embedding/android/FlutterEngineConfigurator.java index 94dc318173e79..227770d4b0bd6 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterEngineConfigurator.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterEngineConfigurator.java @@ -4,6 +4,7 @@ package io.flutter.embedding.android; +import android.app.Activity; import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; import io.flutter.embedding.engine.FlutterEngine; @@ -20,8 +21,8 @@ public interface FlutterEngineConfigurator { * *

This method is called after the given {@link FlutterEngine} has been attached to the owning * {@code FragmentActivity}. See {@link - * io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity( - * ExclusiveAppComponent, Lifecycle)}. + * io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity(Activity, + * Lifecycle)}. * *

It is possible that the owning {@code FragmentActivity} opted not to connect itself as an * {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface}. In that case, any diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index 4d2438bd95935..d64b6788e71a6 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -623,55 +623,29 @@ public void onPause() { @Override public void onStop() { super.onStop(); - if (stillAttachedForEvent("onStop")) { - delegate.onStop(); - } + delegate.onStop(); } @Override public void onDestroyView() { super.onDestroyView(); - if (stillAttachedForEvent("onDestroyView")) { - delegate.onDestroyView(); - } + delegate.onDestroyView(); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - if (stillAttachedForEvent("onSaveInstanceState")) { - delegate.onSaveInstanceState(outState); - } + delegate.onSaveInstanceState(outState); } @Override - public void detachFromFlutterEngine() { - Log.v( - TAG, - "FlutterFragment " - + this - + " connection to the engine " - + getFlutterEngine() - + " evicted by another attaching activity"); - // Redundant calls are ok. - delegate.onDestroyView(); + public void onDetach() { + super.onDetach(); delegate.onDetach(); delegate.release(); delegate = null; } - @Override - public void onDetach() { - super.onDetach(); - if (delegate != null) { - delegate.onDetach(); - delegate.release(); - delegate = null; - } else { - Log.v(TAG, "FlutterFragment " + this + " onDetach called after release."); - } - } - /** * The result of a permission request has been received. * @@ -686,9 +660,7 @@ public void onDetach() { @ActivityCallThrough public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - if (stillAttachedForEvent("onRequestPermissionsResult")) { - delegate.onRequestPermissionsResult(requestCode, permissions, grantResults); - } + delegate.onRequestPermissionsResult(requestCode, permissions, grantResults); } /** @@ -703,9 +675,7 @@ public void onRequestPermissionsResult( */ @ActivityCallThrough public void onNewIntent(@NonNull Intent intent) { - if (stillAttachedForEvent("onNewIntent")) { - delegate.onNewIntent(intent); - } + delegate.onNewIntent(intent); } /** @@ -715,9 +685,7 @@ public void onNewIntent(@NonNull Intent intent) { */ @ActivityCallThrough public void onBackPressed() { - if (stillAttachedForEvent("onBackPressed")) { - delegate.onBackPressed(); - } + delegate.onBackPressed(); } /** @@ -732,9 +700,7 @@ public void onBackPressed() { */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (stillAttachedForEvent("onActivityResult")) { - delegate.onActivityResult(requestCode, resultCode, data); - } + delegate.onActivityResult(requestCode, resultCode, data); } /** @@ -745,9 +711,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { */ @ActivityCallThrough public void onUserLeaveHint() { - if (stillAttachedForEvent("onUserLeaveHint")) { - delegate.onUserLeaveHint(); - } + delegate.onUserLeaveHint(); } /** @@ -761,9 +725,7 @@ public void onUserLeaveHint() { */ @ActivityCallThrough public void onTrimMemory(int level) { - if (stillAttachedForEvent("onTrimMemory")) { - delegate.onTrimMemory(level); - } + delegate.onTrimMemory(level); } /** @@ -774,9 +736,7 @@ public void onTrimMemory(int level) { @Override public void onLowMemory() { super.onLowMemory(); - if (stillAttachedForEvent("onLowMemory")) { - delegate.onLowMemory(); - } + delegate.onLowMemory(); } /** @@ -969,8 +929,8 @@ public PlatformPlugin providePlatformPlugin( * *

This method is called after {@link #provideFlutterEngine(Context)}, and after the given * {@link FlutterEngine} has been attached to the owning {@code FragmentActivity}. See {@link - * io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity( - * ExclusiveAppComponent, Lifecycle)}. + * io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity(Activity, + * Lifecycle)}. * *

It is possible that the owning {@code FragmentActivity} opted not to connect itself as an * {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface}. In that case, any @@ -1075,14 +1035,6 @@ public boolean shouldRestoreAndSaveState() { return true; } - private boolean stillAttachedForEvent(String event) { - if (delegate == null) { - Log.v(TAG, "FlutterFragment " + hashCode() + " " + event + " called after release."); - return false; - } - return true; - } - /** * Annotates methods in {@code FlutterFragment} that must be called by the containing {@code * Activity}. diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 48d9243cf86c2..c5c92a05d9dc3 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -967,7 +967,7 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) { public void detachFromFlutterEngine() { Log.v(TAG, "Detaching from a FlutterEngine: " + flutterEngine); if (!isAttachedToFlutterEngine()) { - Log.v(TAG, "FlutterView not attached to an engine. Not detaching."); + Log.v(TAG, "Not attached to an engine. Doing nothing."); return; } diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index 71730e4668a85..9ec50d7ef98eb 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -74,7 +74,7 @@ public class FlutterEngine { @NonNull private final FlutterJNI flutterJNI; @NonNull private final FlutterRenderer renderer; @NonNull private final DartExecutor dartExecutor; - @NonNull private final FlutterEngineConnectionRegistry pluginRegistry; + @NonNull private final FlutterEnginePluginRegistry pluginRegistry; @NonNull private final LocalizationPlugin localizationPlugin; // System channels. @@ -301,7 +301,7 @@ public FlutterEngine( this.platformViewsController.onAttachedToJNI(); this.pluginRegistry = - new FlutterEngineConnectionRegistry(context.getApplicationContext(), this, flutterLoader); + new FlutterEnginePluginRegistry(context.getApplicationContext(), this, flutterLoader); if (automaticallyRegisterPlugins) { registerPlugins(); diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java similarity index 90% rename from shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java rename to shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java index 729bc1d842595..7e9d39b30752d 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java @@ -15,7 +15,6 @@ import androidx.annotation.Nullable; import androidx.lifecycle.Lifecycle; import io.flutter.Log; -import io.flutter.embedding.android.ExclusiveAppComponent; import io.flutter.embedding.engine.loader.FlutterLoader; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.PluginRegistry; @@ -37,20 +36,13 @@ import java.util.Map; import java.util.Set; -/** - * This class is owned by the {@link FlutterEngine} and its role is to managed its connections with - * Android App Components and Flutter plugins. - * - *

It enforces the {0|1}:1 relationship between activity and engine, and propagates the app - * component connection to the plugins. - */ -/* package */ class FlutterEngineConnectionRegistry +class FlutterEnginePluginRegistry implements PluginRegistry, ActivityControlSurface, ServiceControlSurface, BroadcastReceiverControlSurface, ContentProviderControlSurface { - private static final String TAG = "FlutterEngineConnectionRegistry"; + private static final String TAG = "FlutterEnginePluginRegistry"; // PluginRegistry @NonNull @@ -65,9 +57,7 @@ private final Map, ActivityAware> activityAwarePlugins = new HashMap<>(); - // TODO(xster): remove activity after 2021/03/01 since exclusiveActivity should be the API to use. - @Deprecated @Nullable private Activity activity; - @Nullable private ExclusiveAppComponent exclusiveActivity; + @Nullable private Activity activity; @Nullable private FlutterEngineActivityPluginBinding activityPluginBinding; private boolean isWaitingForActivityReattachment = false; @@ -95,7 +85,7 @@ @Nullable private ContentProvider contentProvider; @Nullable private FlutterEngineContentProviderPluginBinding contentProviderPluginBinding; - FlutterEngineConnectionRegistry( + FlutterEnginePluginRegistry( @NonNull Context appContext, @NonNull FlutterEngine flutterEngine, @NonNull FlutterLoader flutterLoader) { @@ -113,10 +103,10 @@ public void destroy() { Log.v(TAG, "Destroying."); // Detach from any Android component that we may currently be attached to, e.g., Activity, - // Service, BroadcastReceiver, ContentProvider. This must happen before removing all plugins so - // that the plugins have an opportunity to clean up references as a result of component - // detachment. - detachFromAppComponent(); + // Service, + // BroadcastReceiver, ContentProvider. This must happen before removing all plugins so that the + // plugins have an opportunity to clean up references as a result of component detachment. + detachFromAndroidComponent(); // Remove all registered plugins. removeAll(); @@ -279,7 +269,7 @@ public void removeAll() { plugins.clear(); } - private void detachFromAppComponent() { + private void detachFromAndroidComponent() { if (isAttachedToActivity()) { detachFromActivity(); } else if (isAttachedToService()) { @@ -293,11 +283,7 @@ private void detachFromAppComponent() { // -------- Start ActivityControlSurface ------- private boolean isAttachedToActivity() { - return activity != null || exclusiveActivity != null; - } - - private Activity attachedActivity() { - return exclusiveActivity != null ? exclusiveActivity.getAppComponent() : activity; + return activity != null; } @Override @@ -308,43 +294,10 @@ public void attachToActivity(@NonNull Activity activity, @NonNull Lifecycle life + activity + "." + (isWaitingForActivityReattachment ? " This is after a config change." : "")); - if (this.exclusiveActivity != null) { - this.exclusiveActivity.detachFromFlutterEngine(); - } - // If we were already attached to an app component, detach from it. - detachFromAppComponent(); + // If we were already attached to an Android component, detach from it. + detachFromAndroidComponent(); - if (this.exclusiveActivity != null) { - throw new AssertionError("Only activity or exclusiveActivity should be set"); - } this.activity = activity; - attachToActivityInternal(activity, lifecycle); - } - - @Override - public void attachToActivity( - @NonNull ExclusiveAppComponent exclusiveActivity, @NonNull Lifecycle lifecycle) { - Log.v( - TAG, - "Attaching to an exclusive Activity: " - + exclusiveActivity.getAppComponent() - + (isAttachedToActivity() ? " evicting previous activity " + attachedActivity() : "") - + "." - + (isWaitingForActivityReattachment ? " This is after a config change." : "")); - if (this.exclusiveActivity != null) { - this.exclusiveActivity.detachFromFlutterEngine(); - } - // If we were already attached to an app component, detach from it. - detachFromAppComponent(); - - if (this.activity != null) { - throw new AssertionError("Only activity or exclusiveActivity should be set"); - } - this.exclusiveActivity = exclusiveActivity; - attachToActivityInternal(exclusiveActivity.getAppComponent(), lifecycle); - } - - private void attachToActivityInternal(@NonNull Activity activity, @NonNull Lifecycle lifecycle) { this.activityPluginBinding = new FlutterEngineActivityPluginBinding(activity, lifecycle); // Activate the PlatformViewsController. This must happen before any plugins attempt @@ -368,14 +321,18 @@ private void attachToActivityInternal(@NonNull Activity activity, @NonNull Lifec @Override public void detachFromActivityForConfigChanges() { if (isAttachedToActivity()) { - Log.v(TAG, "Detaching from an Activity for config changes: " + attachedActivity()); + Log.v(TAG, "Detaching from an Activity for config changes: " + activity); isWaitingForActivityReattachment = true; for (ActivityAware activityAware : activityAwarePlugins.values()) { activityAware.onDetachedFromActivityForConfigChanges(); } - detachFromActivityInternal(); + // Deactivate PlatformViewsController. + flutterEngine.getPlatformViewsController().detach(); + + activity = null; + activityPluginBinding = null; } else { Log.e(TAG, "Attempted to detach plugins from an Activity when no Activity was attached."); } @@ -384,26 +341,21 @@ public void detachFromActivityForConfigChanges() { @Override public void detachFromActivity() { if (isAttachedToActivity()) { - Log.v(TAG, "Detaching from an Activity: " + attachedActivity()); + Log.v(TAG, "Detaching from an Activity: " + activity); for (ActivityAware activityAware : activityAwarePlugins.values()) { activityAware.onDetachedFromActivity(); } - detachFromActivityInternal(); + // Deactivate PlatformViewsController. + flutterEngine.getPlatformViewsController().detach(); + + activity = null; + activityPluginBinding = null; } else { Log.e(TAG, "Attempted to detach plugins from an Activity when no Activity was attached."); } } - private void detachFromActivityInternal() { - // Deactivate PlatformViewsController. - flutterEngine.getPlatformViewsController().detach(); - - exclusiveActivity = null; - activity = null; - activityPluginBinding = null; - } - @Override public boolean onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResult) { @@ -491,7 +443,7 @@ public void attachToService( @NonNull Service service, @Nullable Lifecycle lifecycle, boolean isForeground) { Log.v(TAG, "Attaching to a Service: " + service); // If we were already attached to an Android component, detach from it. - detachFromAppComponent(); + detachFromAndroidComponent(); this.service = service; this.servicePluginBinding = new FlutterEngineServicePluginBinding(service, lifecycle); @@ -545,7 +497,7 @@ public void attachToBroadcastReceiver( @NonNull BroadcastReceiver broadcastReceiver, @NonNull Lifecycle lifecycle) { Log.v(TAG, "Attaching to BroadcastReceiver: " + broadcastReceiver); // If we were already attached to an Android component, detach from it. - detachFromAppComponent(); + detachFromAndroidComponent(); this.broadcastReceiver = broadcastReceiver; this.broadcastReceiverPluginBinding = @@ -587,7 +539,7 @@ public void attachToContentProvider( @NonNull ContentProvider contentProvider, @NonNull Lifecycle lifecycle) { Log.v(TAG, "Attaching to ContentProvider: " + contentProvider); // If we were already attached to an Android component, detach from it. - detachFromAppComponent(); + detachFromAndroidComponent(); this.contentProvider = contentProvider; this.contentProviderPluginBinding = diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java index 276b54d56321a..671f8311f4827 100644 --- a/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/activity/ActivityControlSurface.java @@ -10,7 +10,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.Lifecycle; -import io.flutter.embedding.android.ExclusiveAppComponent; /** * Control surface through which an {@link Activity} attaches to a {@link FlutterEngine}. @@ -20,10 +19,9 @@ * *

    *
  1. Once an {@link Activity} is created, and its associated {@link FlutterEngine} is executing - * Dart code, the {@link Activity} should invoke {@link #attachToActivity( - * ExclusiveAppComponent, Lifecycle)}. At this point the {@link FlutterEngine} is considered - * "attached" to the {@link Activity} and all {@link ActivityAware} plugins are given access - * to the {@link Activity}. + * Dart code, the {@link Activity} should invoke {@link #attachToActivity(Activity, + * Lifecycle)}. At this point the {@link FlutterEngine} is considered "attached" to the {@link + * Activity} and all {@link ActivityAware} plugins are given access to the {@link Activity}. *
  2. Just before an attached {@link Activity} is destroyed for configuration change purposes, * that {@link Activity} should invoke {@link #detachFromActivityForConfigChanges()}, giving * each {@link ActivityAware} plugin an opportunity to clean up its references before the @@ -34,10 +32,6 @@ *
  3. When an {@link Activity} is destroyed for non-configuration-change purposes, or when the * {@link Activity} is no longer interested in displaying a {@link FlutterEngine}'s content, * the {@link Activity} should invoke {@link #detachFromActivity()}. - *
  4. When a {@link Activity} is being attached while an existing {@link ExclusiveAppComponent} - * is already attached, the existing {@link ExclusiveAppComponent} is given a chance to detach - * first via {@link ExclusiveAppComponent#detachFromFlutterEngine()} before the new activity - * attaches. *
* * The attached {@link Activity} should also forward all {@link Activity} calls that this {@code @@ -54,31 +48,9 @@ public interface ActivityControlSurface { * Dart code, the {@link Activity} should invoke this method. At that point the {@link * FlutterEngine} is considered "attached" to the {@link Activity} and all {@link ActivityAware} * plugins are given access to the {@link Activity}. - * - * @deprecated Prefer using the {@link #attachToActivity(ExclusiveAppComponent, Lifecycle)} API to - * avoid situations where multiple activities are driving the FlutterEngine simultaneously. - * See https://github.com/flutter/flutter/issues/66192. */ - @Deprecated void attachToActivity(@NonNull Activity activity, @NonNull Lifecycle lifecycle); - /** - * Call this method from the {@link ExclusiveAppComponent} that is displaying the visual content - * of the {@link FlutterEngine} that is associated with this {@code ActivityControlSurface}. - * - *

Once an {@link ExclusiveAppComponent} is created, and its associated {@link FlutterEngine} - * is executing Dart code, the {@link ExclusiveAppComponent} should invoke this method. At that - * point the {@link FlutterEngine} is considered "attached" to the {@link ExclusiveAppComponent} - * and all {@link ActivityAware} plugins are given access to the {@link ExclusiveAppComponent}'s - * {@link Activity}. - * - *

This method differs from {@link #attachToActivity(Activity, Lifecycle)} in that it calls - * back the existing {@link ExclusiveAppComponent} to give it a chance to cleanly detach before a - * new {@link ExclusiveAppComponent} is attached. - */ - void attachToActivity( - @NonNull ExclusiveAppComponent exclusiveActivity, @NonNull Lifecycle lifecycle); - /** * Call this method from the {@link Activity} that is attached to this {@code * ActivityControlSurfaces}'s {@link FlutterEngine} when the {@link Activity} is about to be diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index c7de3c2c0ec6c..75d724605c69d 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -460,9 +460,7 @@ public void attach( */ @UiThread public void detach() { - if (platformViewsChannel != null) { - platformViewsChannel.setPlatformViewsHandler(null); - } + platformViewsChannel.setPlatformViewsHandler(null); platformViewsChannel = null; context = null; textureRegistry = null; diff --git a/shell/platform/android/test/io/flutter/FlutterTestSuite.java b/shell/platform/android/test/io/flutter/FlutterTestSuite.java index bdc130ef87e9a..bb5c44c6568df 100644 --- a/shell/platform/android/test/io/flutter/FlutterTestSuite.java +++ b/shell/platform/android/test/io/flutter/FlutterTestSuite.java @@ -12,7 +12,7 @@ import io.flutter.embedding.android.FlutterFragmentTest; import io.flutter.embedding.android.FlutterViewTest; import io.flutter.embedding.engine.FlutterEngineCacheTest; -import io.flutter.embedding.engine.FlutterEngineConnectionRegistryTest; +import io.flutter.embedding.engine.FlutterEnginePluginRegistryTest; import io.flutter.embedding.engine.FlutterJNITest; import io.flutter.embedding.engine.LocalizationPluginTest; import io.flutter.embedding.engine.RenderingComponentTest; @@ -52,7 +52,7 @@ FlutterActivityTest.class, FlutterAndroidComponentTest.class, FlutterEngineCacheTest.class, - FlutterEngineConnectionRegistryTest.class, + FlutterEnginePluginRegistryTest.class, FlutterEngineTest.class, FlutterFragmentActivityTest.class, FlutterFragmentTest.class, diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java index 4a621b3344107..7ef0b048e7e8f 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -357,7 +357,7 @@ public void itAttachesFlutterToTheActivityIfDesired() { // Verify that the ActivityControlSurface was told to attach to an Activity. verify(mockFlutterEngine.getActivityControlSurface(), times(1)) - .attachToActivity(any(ExclusiveAppComponent.class), any(Lifecycle.class)); + .attachToActivity(any(Activity.class), any(Lifecycle.class)); // Flutter is detached from the surrounding Activity in onDetach. delegate.onDetach(); diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java index 2fe23de49b303..67e5984fad0ec 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java @@ -6,9 +6,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -18,7 +15,6 @@ import androidx.annotation.Nullable; import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode; import io.flutter.embedding.engine.FlutterEngine; -import io.flutter.embedding.engine.FlutterEngineCache; import io.flutter.embedding.engine.FlutterJNI; import io.flutter.embedding.engine.loader.FlutterLoader; import io.flutter.plugins.GeneratedPluginRegistrant; @@ -162,40 +158,6 @@ public void itRegistersPluginsAtConfigurationTime() { assertEquals(activity.getFlutterEngine(), registeredEngines.get(0)); } - @Test - public void itCanBeDetachedFromTheEngineAndStopSendingFurtherEvents() { - FlutterActivityAndFragmentDelegate mockDelegate = - mock(FlutterActivityAndFragmentDelegate.class); - FlutterEngine mockEngine = mock(FlutterEngine.class); - FlutterEngineCache.getInstance().put("my_cached_engine", mockEngine); - - Intent intent = - FlutterActivity.withCachedEngine("my_cached_engine").build(RuntimeEnvironment.application); - ActivityController activityController = - Robolectric.buildActivity(FlutterActivity.class, intent); - FlutterActivity flutterActivity = activityController.get(); - flutterActivity.setDelegate(mockDelegate); - flutterActivity.onStart(); - flutterActivity.onResume(); - - verify(mockDelegate, times(1)).onStart(); - verify(mockDelegate, times(1)).onResume(); - - flutterActivity.onPause(); - flutterActivity.detachFromFlutterEngine(); - verify(mockDelegate, times(1)).onPause(); - verify(mockDelegate, times(1)).onDestroyView(); - verify(mockDelegate, times(1)).onDetach(); - - flutterActivity.onStop(); - flutterActivity.onDestroy(); - - verify(mockDelegate, never()).onStop(); - // 1 time same as before. - verify(mockDelegate, times(1)).onDestroyView(); - verify(mockDelegate, times(1)).onDetach(); - } - static class FlutterActivityWithProvidedEngine extends FlutterActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java index 5cede33a1b833..9255cb4077b67 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java @@ -3,19 +3,15 @@ import static org.junit.Assert.assertNotNull; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; import android.app.Activity; import android.content.Context; -import android.content.Intent; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -28,18 +24,15 @@ 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.embedding.engine.systemchannels.LifecycleChannel; import io.flutter.plugin.platform.PlatformPlugin; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; @Config(manifest = Config.NONE) @@ -61,8 +54,7 @@ public void pluginsReceiveFlutterPluginBinding() { cachedEngine.getPlugins().add(mockPlugin); // Create a fake Host, which is required by the delegate. - FakeHost fakeHost = new FakeHost(cachedEngine); - fakeHost.shouldDestroyEngineWithHost = true; + FlutterActivityAndFragmentDelegate.Host fakeHost = new FakeHost(cachedEngine); // Create the real object that we're testing. FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(fakeHost); @@ -173,87 +165,9 @@ public Object answer(InvocationOnMock invocation) throws Throwable { verify(activityAwarePlugin, times(1)).onDetachedFromActivity(); } - @Test - public void normalLifecycleStepsDoNotTriggerADetachFromFlutterEngine() { - // ---- Test setup ---- - // Place a FlutterEngine in the static cache. - FlutterLoader mockFlutterLoader = mock(FlutterLoader.class); - FlutterJNI mockFlutterJni = mock(FlutterJNI.class); - when(mockFlutterJni.isAttached()).thenReturn(true); - FlutterEngine cachedEngine = - spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni)); - FlutterEngineCache.getInstance().put("my_flutter_engine", cachedEngine); - - // Create a fake Host, which is required by the delegate. - FakeHost fakeHost = new FakeHost(cachedEngine); - - // Create the real object that we're testing. - FlutterActivityAndFragmentDelegate delegate = - spy(new FlutterActivityAndFragmentDelegate(fakeHost)); - - // --- Execute the behavior under test --- - // Push the delegate through all lifecycle methods all the way to destruction. - delegate.onAttach(RuntimeEnvironment.application); - delegate.onActivityCreated(null); - delegate.onCreateView(null, null, null); - delegate.onStart(); - delegate.onResume(); - delegate.onPause(); - delegate.onStop(); - delegate.onDestroyView(); - delegate.onDetach(); - - verify(delegate, never()).detachFromFlutterEngine(); - } - - @Test - public void twoOverlappingFlutterActivitiesDoNotCrosstalk() { - // ---- Test setup ---- - // Place a FlutterEngine in the static cache. - FlutterLoader mockFlutterLoader = mock(FlutterLoader.class); - FlutterJNI mockFlutterJni = mock(FlutterJNI.class); - when(mockFlutterJni.isAttached()).thenReturn(true); - FlutterEngine cachedEngine = - spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni)); - FlutterEngineCache.getInstance().put("my_flutter_engine", cachedEngine); - LifecycleChannel mockLifecycleChannel = mock(LifecycleChannel.class); - when(cachedEngine.getLifecycleChannel()).thenReturn(mockLifecycleChannel); - - Intent intent = - FlutterActivity.withCachedEngine("my_flutter_engine").build(RuntimeEnvironment.application); - ActivityController activityController1 = - Robolectric.buildActivity(FlutterActivity.class, intent); - activityController1.create().start().resume(); - - InOrder inOrder = inOrder(mockLifecycleChannel); - inOrder.verify(mockLifecycleChannel, times(1)).appIsResumed(); - verifyNoMoreInteractions(mockLifecycleChannel); - - activityController1.pause(); - // Create a second instance on the same engine and start running it as well. - ActivityController activityController2 = - Robolectric.buildActivity(FlutterActivity.class, intent); - activityController2.create().start().resume(); - - // From the onPause of the first activity. - inOrder.verify(mockLifecycleChannel, times(1)).appIsInactive(); - // By creating the second activity, we should automatically detach the first activity. - inOrder.verify(mockLifecycleChannel, times(1)).appIsDetached(); - // In order, the second activity then is resumed. - inOrder.verify(mockLifecycleChannel, times(1)).appIsResumed(); - verifyNoMoreInteractions(mockLifecycleChannel); - - // The first activity goes through the normal lifecycles of destruction, but since we - // detached the first activity during the second activity's creation, we should ignore the - // first activity's destruction events to avoid crosstalk. - activityController1.stop().destroy(); - verifyNoMoreInteractions(mockLifecycleChannel); - } - private static class FakeHost implements FlutterActivityAndFragmentDelegate.Host { final FlutterEngine cachedEngine; Activity activity; - boolean shouldDestroyEngineWithHost = false; Lifecycle lifecycle = mock(Lifecycle.class); private FakeHost(@NonNull FlutterEngine flutterEngine) { @@ -295,7 +209,7 @@ public String getCachedEngineId() { @Override public boolean shouldDestroyEngineWithHost() { - return shouldDestroyEngineWithHost; + return true; } @NonNull @@ -374,8 +288,5 @@ public void onFlutterUiDisplayed() {} @Override public void onFlutterUiNoLongerDisplayed() {} - - @Override - public void detachFromFlutterEngine() {} } } diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java index 9327994e8d9e6..6dea8ec6b946d 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java @@ -5,10 +5,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import org.junit.Test; import org.junit.runner.RunWith; @@ -75,34 +71,4 @@ public void itCreatesCachedEngineFragmentThatDestroysTheEngine() { assertEquals("my_cached_engine", fragment.getCachedEngineId()); assertTrue(fragment.shouldDestroyEngineWithHost()); } - - @Test - public void itCanBeDetachedFromTheEngineAndStopSendingFurtherEvents() { - FlutterActivityAndFragmentDelegate mockDelegate = - mock(FlutterActivityAndFragmentDelegate.class); - FlutterFragment fragment = - FlutterFragment.withCachedEngine("my_cached_engine") - .destroyEngineWithFragment(true) - .build(); - fragment.setDelegate(mockDelegate); - fragment.onStart(); - fragment.onResume(); - - verify(mockDelegate, times(1)).onStart(); - verify(mockDelegate, times(1)).onResume(); - - fragment.onPause(); - fragment.detachFromFlutterEngine(); - verify(mockDelegate, times(1)).onPause(); - verify(mockDelegate, times(1)).onDestroyView(); - verify(mockDelegate, times(1)).onDetach(); - - fragment.onStop(); - fragment.onDestroy(); - - verify(mockDelegate, never()).onStop(); - // 1 time same as before. - verify(mockDelegate, times(1)).onDestroyView(); - verify(mockDelegate, times(1)).onDetach(); - } } diff --git a/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineConnectionRegistryTest.java b/shell/platform/android/test/io/flutter/embedding/engine/FlutterEnginePluginRegistryTest.java similarity index 95% rename from shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineConnectionRegistryTest.java rename to shell/platform/android/test/io/flutter/embedding/engine/FlutterEnginePluginRegistryTest.java index 6037ca8c4e11e..ee653348004a1 100644 --- a/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineConnectionRegistryTest.java +++ b/shell/platform/android/test/io/flutter/embedding/engine/FlutterEnginePluginRegistryTest.java @@ -26,7 +26,7 @@ // Run with Robolectric so that Log calls don't crash. @Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) -public class FlutterEngineConnectionRegistryTest { +public class FlutterEnginePluginRegistryTest { @Test public void itDoesNotRegisterTheSamePluginTwice() { Context context = mock(Context.class); @@ -40,8 +40,8 @@ public void itDoesNotRegisterTheSamePluginTwice() { FakeFlutterPlugin fakePlugin1 = new FakeFlutterPlugin(); FakeFlutterPlugin fakePlugin2 = new FakeFlutterPlugin(); - FlutterEngineConnectionRegistry registry = - new FlutterEngineConnectionRegistry(context, flutterEngine, flutterLoader); + FlutterEnginePluginRegistry registry = + new FlutterEnginePluginRegistry(context, flutterEngine, flutterLoader); // Verify that the registry doesn't think it contains our plugin yet. assertFalse(registry.has(fakePlugin1.getClass())); @@ -80,8 +80,8 @@ public void activityResultListenerCanBeRemovedFromListener() { AtomicBoolean isFirstCall = new AtomicBoolean(true); // setup the environment to get the required internal data - FlutterEngineConnectionRegistry registry = - new FlutterEngineConnectionRegistry(context, flutterEngine, flutterLoader); + FlutterEnginePluginRegistry registry = + new FlutterEnginePluginRegistry(context, flutterEngine, flutterLoader); FakeActivityAwareFlutterPlugin fakePlugin = new FakeActivityAwareFlutterPlugin(); registry.add(fakePlugin); registry.attachToActivity(activity, lifecycle);