diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 611cdc4dba0bf..8b5fea520bf03 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -754,6 +754,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugin FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 23300cf01a4d3..3443dba51d3ae 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -177,6 +177,7 @@ android_java_sources = [ "io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java", "io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java", "io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java", + "io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java", "io/flutter/embedding/engine/renderer/FlutterRenderer.java", "io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java", "io/flutter/embedding/engine/renderer/RenderSurface.java", diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index acc208a8dc7a3..7446522a5b6c7 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -42,9 +42,9 @@ import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterShellArgs; import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface; +import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister; import io.flutter.plugin.platform.PlatformPlugin; import io.flutter.view.FlutterMain; -import java.lang.reflect.Method; /** * {@code Activity} which displays a fullscreen Flutter UI. @@ -872,7 +872,7 @@ public PlatformPlugin providePlatformPlugin( */ @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { - registerPlugins(flutterEngine); + GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine); } /** @@ -962,34 +962,4 @@ public boolean shouldRestoreAndSaveState() { } return true; } - - /** - * Registers all plugins that an app lists in its pubspec.yaml. - * - *
The Flutter tool generates a class called GeneratedPluginRegistrant, which includes the code - * necessary to register every plugin in the pubspec.yaml with a given {@code FlutterEngine}. The - * GeneratedPluginRegistrant must be generated per app, because each app uses different sets of - * plugins. Therefore, the Android embedding cannot place a compile-time dependency on this - * generated class. This method uses reflection to attempt to locate the generated file and then - * use it at runtime. - * - *
This method fizzles if the GeneratedPluginRegistrant cannot be found or invoked. This - * situation should never occur, but if any eventuality comes up that prevents an app from using - * this behavior, that app can still write code that explicitly registers plugins. - */ - private static void registerPlugins(@NonNull FlutterEngine flutterEngine) { - try { - Class> generatedPluginRegistrant = - Class.forName("io.flutter.plugins.GeneratedPluginRegistrant"); - Method registrationMethod = - generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class); - registrationMethod.invoke(null, flutterEngine); - } catch (Exception e) { - Log.w( - TAG, - "Tried to automatically register plugins with FlutterEngine (" - + flutterEngine - + ") but could not find and invoke the GeneratedPluginRegistrant."); - } - } } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index 0d0c0d203bc8b..2a1cb94241fb1 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -39,6 +39,7 @@ import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterShellArgs; +import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister; import io.flutter.plugin.platform.PlatformPlugin; import io.flutter.view.FlutterMain; @@ -554,13 +555,18 @@ public FlutterEngine provideFlutterEngine(@NonNull Context context) { } /** - * Hook for subclasses to easily configure a {@code FlutterEngine}, e.g., register plugins. + * Hook for subclasses to easily configure a {@code FlutterEngine}. * *
This method is called after {@link #provideFlutterEngine(Context)}. + * + *
All plugins listed in the app's pubspec are registered in the base implementation of this + * method. To avoid automatic plugin registration, override this method without invoking super(). + * To keep automatic plugin registration and further configure the flutterEngine, override this + * method, invoke super(), and then configure the flutterEngine as desired. */ @Override public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { - // No-op. Hook for subclasses. + GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine); } /** diff --git a/shell/platform/android/io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java b/shell/platform/android/io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java new file mode 100644 index 0000000000000..5bb0ff5a6f37b --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.engine.plugins.util; + +import androidx.annotation.NonNull; +import io.flutter.Log; +import io.flutter.embedding.engine.FlutterEngine; +import java.lang.reflect.Method; + +public class GeneratedPluginRegister { + private static final String TAG = "GeneratedPluginsRegister"; + /** + * Registers all plugins that an app lists in its pubspec.yaml. + * + *
The Flutter tool generates a class called GeneratedPluginRegistrant, which includes the code + * necessary to register every plugin in the pubspec.yaml with a given {@code FlutterEngine}. The + * GeneratedPluginRegistrant must be generated per app, because each app uses different sets of + * plugins. Therefore, the Android embedding cannot place a compile-time dependency on this + * generated class. This method uses reflection to attempt to locate the generated file and then + * use it at runtime. + * + *
This method fizzles if the GeneratedPluginRegistrant cannot be found or invoked. This
+ * situation should never occur, but if any eventuality comes up that prevents an app from using
+ * this behavior, that app can still write code that explicitly registers plugins.
+ */
+ public static void registerGeneratedPlugins(@NonNull FlutterEngine flutterEngine) {
+ try {
+ Class> generatedPluginRegistrant =
+ Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
+ Method registrationMethod =
+ generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
+ registrationMethod.invoke(null, flutterEngine);
+ } catch (Exception e) {
+ Log.w(
+ TAG,
+ "Tried to automatically register plugins with FlutterEngine ("
+ + flutterEngine
+ + ") but could not find and invoke the GeneratedPluginRegistrant.");
+ }
+ }
+}
diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java
index 88ef48cbdd8e9..cd311a835588e 100644
--- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java
+++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java
@@ -1,18 +1,40 @@
package io.flutter.embedding.android;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
+import io.flutter.embedding.engine.FlutterEngine;
+import io.flutter.embedding.engine.FlutterJNI;
+import io.flutter.embedding.engine.loader.FlutterLoader;
+import io.flutter.plugins.GeneratedPluginRegistrant;
+import java.util.List;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class FlutterFragmentActivityTest {
+ @Before
+ public void setUp() {
+ GeneratedPluginRegistrant.clearRegisteredEngines();
+ }
+
+ @After
+ public void tearDown() {
+ GeneratedPluginRegistrant.clearRegisteredEngines();
+ }
@Test
public void createFlutterFragment__defaultRenderModeSurface() {
@@ -44,6 +66,39 @@ protected RenderMode getRenderMode() {
assertEquals(activity.createFlutterFragment().getRenderMode(), RenderMode.texture);
}
+ @Test
+ public void itRegistersPluginsAtConfigurationTime() {
+ FlutterFragmentActivity activity =
+ Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get();
+ assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
+
+ // Calling onCreate on the FlutterFragmentActivity will create a FlutterFragment and
+ // commit it to the fragment manager. This attaches the fragment to the FlutterFragmentActivity
+ // creating and configuring the engine.
+ activity.onCreate(null);
+
+ List