Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 21ec1c6

Browse files
committed
Add auto plugin registration to FlutterFragmentActivity as well
1 parent 07e2520 commit 21ec1c6

File tree

2 files changed

+93
-2
lines changed

2 files changed

+93
-2
lines changed

shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import io.flutter.embedding.engine.FlutterShellArgs;
4242
import io.flutter.plugin.platform.PlatformPlugin;
4343
import io.flutter.view.FlutterMain;
44+
import java.lang.reflect.Method;
4445

4546
/**
4647
* A Flutter {@code Activity} that is based upon {@link FragmentActivity}.
@@ -554,13 +555,18 @@ public FlutterEngine provideFlutterEngine(@NonNull Context context) {
554555
}
555556

556557
/**
557-
* Hook for subclasses to easily configure a {@code FlutterEngine}, e.g., register plugins.
558+
* Hook for subclasses to easily configure a {@code FlutterEngine}.
558559
*
559560
* <p>This method is called after {@link #provideFlutterEngine(Context)}.
561+
*
562+
* <p>All plugins listed in the app's pubspec are registered in the base implementation of this
563+
* method. To avoid automatic plugin registration, override this method without invoking super().
564+
* To keep automatic plugin registration and further configure the flutterEngine, override this
565+
* method, invoke super(), and then configure the flutterEngine as desired.
560566
*/
561567
@Override
562568
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
563-
// No-op. Hook for subclasses.
569+
registerPlugins(flutterEngine);
564570
}
565571

566572
/**
@@ -710,4 +716,34 @@ protected RenderMode getRenderMode() {
710716
private boolean isDebuggable() {
711717
return (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
712718
}
719+
720+
/**
721+
* Registers all plugins that an app lists in its pubspec.yaml.
722+
*
723+
* <p>The Flutter tool generates a class called GeneratedPluginRegistrant, which includes the code
724+
* necessary to register every plugin in the pubspec.yaml with a given {@code FlutterEngine}. The
725+
* GeneratedPluginRegistrant must be generated per app, because each app uses different sets of
726+
* plugins. Therefore, the Android embedding cannot place a compile-time dependency on this
727+
* generated class. This method uses reflection to attempt to locate the generated file and then
728+
* use it at runtime.
729+
*
730+
* <p>This method fizzles if the GeneratedPluginRegistrant cannot be found or invoked. This
731+
* situation should never occur, but if any eventuality comes up that prevents an app from using
732+
* this behavior, that app can still write code that explicitly registers plugins.
733+
*/
734+
private static void registerPlugins(@NonNull FlutterEngine flutterEngine) {
735+
try {
736+
Class<?> generatedPluginRegistrant =
737+
Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
738+
Method registrationMethod =
739+
generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
740+
registrationMethod.invoke(null, flutterEngine);
741+
} catch (Exception e) {
742+
Log.w(
743+
TAG,
744+
"Tried to automatically register plugins with FlutterEngine ("
745+
+ flutterEngine
746+
+ ") but could not find and invoke the GeneratedPluginRegistrant.");
747+
}
748+
}
713749
}

shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,40 @@
11
package io.flutter.embedding.android;
22

33
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertTrue;
5+
import static org.mockito.Mockito.mock;
6+
import static org.mockito.Mockito.when;
47

8+
import android.content.Context;
59
import android.content.Intent;
610
import androidx.annotation.NonNull;
11+
import androidx.annotation.Nullable;
712
import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
13+
import io.flutter.embedding.engine.FlutterEngine;
14+
import io.flutter.embedding.engine.FlutterJNI;
15+
import io.flutter.embedding.engine.loader.FlutterLoader;
16+
import io.flutter.plugins.GeneratedPluginRegistrant;
17+
import java.util.List;
18+
import org.junit.After;
19+
import org.junit.Before;
820
import org.junit.Test;
921
import org.junit.runner.RunWith;
22+
import org.robolectric.Robolectric;
1023
import org.robolectric.RobolectricTestRunner;
1124
import org.robolectric.annotation.Config;
1225

1326
@Config(manifest = Config.NONE)
1427
@RunWith(RobolectricTestRunner.class)
1528
public class FlutterFragmentActivityTest {
29+
@Before
30+
public void setUp() {
31+
GeneratedPluginRegistrant.clearRegisteredEngines();
32+
}
33+
34+
@After
35+
public void tearDown() {
36+
GeneratedPluginRegistrant.clearRegisteredEngines();
37+
}
1638

1739
@Test
1840
public void createFlutterFragment__defaultRenderModeSurface() {
@@ -44,6 +66,39 @@ protected RenderMode getRenderMode() {
4466
assertEquals(activity.createFlutterFragment().getRenderMode(), RenderMode.texture);
4567
}
4668

69+
@Test
70+
public void itRegistersPluginsAtConfigurationTime() {
71+
FlutterFragmentActivity activity =
72+
Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get();
73+
assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
74+
75+
// Calling onCreate on the FlutterFragmentActivity will create a FlutterFragment and
76+
// commit it to the fragment manager. This attaches the fragment to the FlutterFragmentActivity
77+
// creating and configuring the engine.
78+
activity.onCreate(null);
79+
80+
List<FlutterEngine> registeredEngines = GeneratedPluginRegistrant.getRegisteredEngines();
81+
assertEquals(1, registeredEngines.size());
82+
assertEquals(activity.getFlutterEngine(), registeredEngines.get(0));
83+
}
84+
85+
static class FlutterFragmentActivityWithProvidedEngine extends FlutterFragmentActivity {
86+
@Override
87+
protected FlutterFragment createFlutterFragment() {
88+
return FlutterFragment.createDefault();
89+
}
90+
91+
@Nullable
92+
@Override
93+
public FlutterEngine provideFlutterEngine(@NonNull Context context) {
94+
FlutterJNI flutterJNI = mock(FlutterJNI.class);
95+
when(flutterJNI.isAttached()).thenReturn(true);
96+
97+
return new FlutterEngine(
98+
context, mock(FlutterLoader.class), flutterJNI, new String[] {}, false);
99+
}
100+
}
101+
47102
private static class FakeFlutterFragmentActivity extends FlutterFragmentActivity {
48103
@Override
49104
public Intent getIntent() {

0 commit comments

Comments
 (0)