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 @@
*
*
* - 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}.
*
- 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 @@
*
- 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()}.
- *
- 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);