Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import io.flutter.embedding.engine.plugins.broadcastreceiver.BroadcastReceiverControlSurface;
import io.flutter.embedding.engine.plugins.contentprovider.ContentProviderControlSurface;
import io.flutter.embedding.engine.plugins.service.ServiceControlSurface;
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister;
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.RenderSurface;
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
Expand All @@ -37,6 +36,7 @@
import io.flutter.embedding.engine.systemchannels.TextInputChannel;
import io.flutter.plugin.localization.LocalizationPlugin;
import io.flutter.plugin.platform.PlatformViewsController;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

Expand Down Expand Up @@ -342,7 +342,7 @@ public FlutterEngine(
// Only automatically register plugins if both constructor parameter and
// loaded AndroidManifest config turn this feature on.
if (automaticallyRegisterPlugins && flutterLoader.automaticallyRegisterPlugins()) {
GeneratedPluginRegister.registerGeneratedPlugins(this);
registerPlugins();
}
}

Expand Down Expand Up @@ -391,6 +391,36 @@ private boolean isAttachedToJni() {
newFlutterJNI); // FlutterJNI.
}

/**
* Registers all plugins that an app lists in its pubspec.yaml.
*
* <p>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.
*
* <p>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 void registerPlugins() {
try {
Class<?> generatedPluginRegistrant =
Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
Method registrationMethod =
generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
registrationMethod.invoke(null, this);
} catch (Exception e) {
Log.w(
TAG,
"Tried to automatically register plugins with FlutterEngine ("
+ this
+ ") but could not find and invoke the GeneratedPluginRegistrant.");
}
}

/**
* Cleans up all components within this {@code FlutterEngine} and destroys the associated Dart
* Isolate. All state held by the Dart Isolate, such as the Flutter Elements tree, is lost.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,11 @@ public static void registerGeneratedPlugins(@NonNull FlutterEngine flutterEngine
generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
registrationMethod.invoke(null, flutterEngine);
} catch (Exception e) {
Log.e(
Log.w(
TAG,
"Tried to automatically register plugins with FlutterEngine ("
+ flutterEngine
+ ") but could not find and invoke the GeneratedPluginRegistrant.");
Log.e(
TAG,
// getCause here because the first layer of the exception would be from reflect.
"Received exception while registering: " + e.getCause());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;

@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
Expand Down Expand Up @@ -64,9 +63,6 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
@After
public void tearDown() {
GeneratedPluginRegistrant.clearRegisteredEngines();
// Make sure to not forget to remove the mock exception in the generated plugin registration
// mock, or everything subsequent will break.
GeneratedPluginRegistrant.pluginRegistrationException = null;
}

@Test
Expand All @@ -82,38 +78,6 @@ public void itAutomaticallyRegistersPluginsByDefault() {
assertEquals(flutterEngine, registeredEngines.get(0));
}

// Helps show the root cause of MissingPluginException type errors like
// https://github.com/flutter/flutter/issues/78625.
@Test
public void itCatchesAndDisplaysRegistrationExceptions() {
assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
GeneratedPluginRegistrant.pluginRegistrationException =
new RuntimeException("I'm a bug in the plugin");
FlutterLoader mockFlutterLoader = mock(FlutterLoader.class);
when(mockFlutterLoader.automaticallyRegisterPlugins()).thenReturn(true);
FlutterEngine flutterEngine =
new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, flutterJNI);

List<FlutterEngine> registeredEngines = GeneratedPluginRegistrant.getRegisteredEngines();
// When it crashes, it doesn't end up registering anything.
assertEquals(0, registeredEngines.size());

// Check the logs actually says registration failed, so a subsequent MissingPluginException
// isn't mysterious.
assertTrue(
ShadowLog.getLogsForTag("GeneratedPluginsRegister")
.get(0)
.msg
.contains("Tried to automatically register plugins"));
assertTrue(
ShadowLog.getLogsForTag("GeneratedPluginsRegister")
.get(1)
.msg
.contains("I'm a bug in the plugin"));

GeneratedPluginRegistrant.pluginRegistrationException = null;
}

@Test
public void itDoesNotAutomaticallyRegistersPluginsWhenFlutterLoaderDisablesIt() {
assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
Expand Down Expand Up @@ -141,6 +105,18 @@ public void itDoesNotAutomaticallyRegistersPluginsWhenFlutterEngineDisablesIt()
assertTrue(registeredEngines.isEmpty());
}

@Test
public void itCanBeConfiguredToNotAutomaticallyRegisterPlugins() {
new FlutterEngine(
RuntimeEnvironment.application,
mock(FlutterLoader.class),
flutterJNI,
/*dartVmArgs=*/ new String[] {},
/*automaticallyRegisterPlugins=*/ false);

assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
}

@Test
public void itNotifiesPlatformViewsControllerWhenDevHotRestart() {
// Setup test.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
@VisibleForTesting
public class GeneratedPluginRegistrant {
private static final List<FlutterEngine> registeredEngines = new ArrayList<>();
public static RuntimeException pluginRegistrationException;

/**
* The one and only method currently generated by the tool.
Expand All @@ -22,9 +21,6 @@ public class GeneratedPluginRegistrant {
* all registered engines instead.
*/
public static void registerWith(FlutterEngine engine) {
if (pluginRegistrationException != null) {
throw pluginRegistrationException;
}
registeredEngines.add(engine);
}

Expand Down