Skip to content

Commit 5f49a95

Browse files
authored
Add auto plugin registration to FlutterFragmentActivity as well (flutter#20865)
1 parent 9398717 commit 5f49a95

File tree

6 files changed

+110
-34
lines changed

6 files changed

+110
-34
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugin
754754
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java
755755
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java
756756
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java
757+
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java
757758
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java
758759
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java
759760
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java

shell/platform/android/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ android_java_sources = [
177177
"io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java",
178178
"io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java",
179179
"io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java",
180+
"io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java",
180181
"io/flutter/embedding/engine/renderer/FlutterRenderer.java",
181182
"io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java",
182183
"io/flutter/embedding/engine/renderer/RenderSurface.java",

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

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@
4242
import io.flutter.embedding.engine.FlutterEngine;
4343
import io.flutter.embedding.engine.FlutterShellArgs;
4444
import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface;
45+
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister;
4546
import io.flutter.plugin.platform.PlatformPlugin;
4647
import io.flutter.view.FlutterMain;
47-
import java.lang.reflect.Method;
4848

4949
/**
5050
* {@code Activity} which displays a fullscreen Flutter UI.
@@ -872,7 +872,7 @@ public PlatformPlugin providePlatformPlugin(
872872
*/
873873
@Override
874874
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
875-
registerPlugins(flutterEngine);
875+
GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
876876
}
877877

878878
/**
@@ -962,34 +962,4 @@ public boolean shouldRestoreAndSaveState() {
962962
}
963963
return true;
964964
}
965-
966-
/**
967-
* Registers all plugins that an app lists in its pubspec.yaml.
968-
*
969-
* <p>The Flutter tool generates a class called GeneratedPluginRegistrant, which includes the code
970-
* necessary to register every plugin in the pubspec.yaml with a given {@code FlutterEngine}. The
971-
* GeneratedPluginRegistrant must be generated per app, because each app uses different sets of
972-
* plugins. Therefore, the Android embedding cannot place a compile-time dependency on this
973-
* generated class. This method uses reflection to attempt to locate the generated file and then
974-
* use it at runtime.
975-
*
976-
* <p>This method fizzles if the GeneratedPluginRegistrant cannot be found or invoked. This
977-
* situation should never occur, but if any eventuality comes up that prevents an app from using
978-
* this behavior, that app can still write code that explicitly registers plugins.
979-
*/
980-
private static void registerPlugins(@NonNull FlutterEngine flutterEngine) {
981-
try {
982-
Class<?> generatedPluginRegistrant =
983-
Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
984-
Method registrationMethod =
985-
generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
986-
registrationMethod.invoke(null, flutterEngine);
987-
} catch (Exception e) {
988-
Log.w(
989-
TAG,
990-
"Tried to automatically register plugins with FlutterEngine ("
991-
+ flutterEngine
992-
+ ") but could not find and invoke the GeneratedPluginRegistrant.");
993-
}
994-
}
995965
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
4040
import io.flutter.embedding.engine.FlutterEngine;
4141
import io.flutter.embedding.engine.FlutterShellArgs;
42+
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister;
4243
import io.flutter.plugin.platform.PlatformPlugin;
4344
import io.flutter.view.FlutterMain;
4445

@@ -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+
GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
564570
}
565571

566572
/**
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.embedding.engine.plugins.util;
6+
7+
import androidx.annotation.NonNull;
8+
import io.flutter.Log;
9+
import io.flutter.embedding.engine.FlutterEngine;
10+
import java.lang.reflect.Method;
11+
12+
public class GeneratedPluginRegister {
13+
private static final String TAG = "GeneratedPluginsRegister";
14+
/**
15+
* Registers all plugins that an app lists in its pubspec.yaml.
16+
*
17+
* <p>The Flutter tool generates a class called GeneratedPluginRegistrant, which includes the code
18+
* necessary to register every plugin in the pubspec.yaml with a given {@code FlutterEngine}. The
19+
* GeneratedPluginRegistrant must be generated per app, because each app uses different sets of
20+
* plugins. Therefore, the Android embedding cannot place a compile-time dependency on this
21+
* generated class. This method uses reflection to attempt to locate the generated file and then
22+
* use it at runtime.
23+
*
24+
* <p>This method fizzles if the GeneratedPluginRegistrant cannot be found or invoked. This
25+
* situation should never occur, but if any eventuality comes up that prevents an app from using
26+
* this behavior, that app can still write code that explicitly registers plugins.
27+
*/
28+
public static void registerGeneratedPlugins(@NonNull FlutterEngine flutterEngine) {
29+
try {
30+
Class<?> generatedPluginRegistrant =
31+
Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
32+
Method registrationMethod =
33+
generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
34+
registrationMethod.invoke(null, flutterEngine);
35+
} catch (Exception e) {
36+
Log.w(
37+
TAG,
38+
"Tried to automatically register plugins with FlutterEngine ("
39+
+ flutterEngine
40+
+ ") but could not find and invoke the GeneratedPluginRegistrant.");
41+
}
42+
}
43+
}

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)