Automatically register plugins in FlutterEngine. (#43855) (#13455)

This commit is contained in:
Matt Carroll 2019-10-31 14:10:45 -07:00 committed by GitHub
parent d33b91837e
commit c882439cb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 78 additions and 6 deletions

View File

@ -420,6 +420,7 @@ action("robolectric_tests") {
"test/io/flutter/embedding/android/FlutterFragmentTest.java",
"test/io/flutter/embedding/android/FlutterViewTest.java",
"test/io/flutter/embedding/engine/FlutterEngineCacheTest.java",
"test/io/flutter/embedding/engine/FlutterEngineTest.java",
"test/io/flutter/embedding/engine/FlutterJNITest.java",
"test/io/flutter/embedding/engine/RenderingComponentTest.java",
"test/io/flutter/embedding/engine/dart/DartExecutorTest.java",

View File

@ -8,6 +8,8 @@ import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
@ -150,31 +152,35 @@ public class FlutterEngine {
* If the Dart VM has already started, the given arguments will have no effect.
*/
public FlutterEngine(@NonNull Context context, @Nullable String[] dartVmArgs) {
this(context, FlutterLoader.getInstance(), new FlutterJNI(), dartVmArgs);
this(context, FlutterLoader.getInstance(), new FlutterJNI(), dartVmArgs, true);
}
/**
* Same as {@link #FlutterEngine(Context, FlutterLoader, FlutterJNI, String[])} but with no Dart
* VM flags.
* <p>
* {@code flutterJNI} should be a new instance that has never been attached to an engine before.
*/
public FlutterEngine(
@NonNull Context context,
@NonNull FlutterLoader flutterLoader,
@NonNull FlutterJNI flutterJNI
) {
this(context, flutterLoader, flutterJNI, null);
this(context, flutterLoader, flutterJNI, null, true);
}
/**
* Constructs a new {@code FlutterEngine}. See {@link #FlutterEngine(Context)}.
*
* {@code flutterJNI} should be a new instance that has never been attached to an engine before.
* Same as {@link #FlutterEngine(Context, FlutterLoader, FlutterJNI)}, plus Dart VM flags in
* {@code dartVmArgs}, and control over whether plugins are automatically registered with this
* {@code FlutterEngine} in {@code automaticallyRegisterPlugins}. If plugins are automatically
* registered, then they are registered during the execution of this constructor.
*/
public FlutterEngine(
@NonNull Context context,
@NonNull FlutterLoader flutterLoader,
@NonNull FlutterJNI flutterJNI,
@Nullable String[] dartVmArgs
@Nullable String[] dartVmArgs,
boolean automaticallyRegisterPlugins
) {
this.flutterJNI = flutterJNI;
flutterLoader.startInitialization(context);
@ -205,6 +211,10 @@ public class FlutterEngine {
context.getApplicationContext(),
this
);
if (automaticallyRegisterPlugins) {
registerPlugins();
}
}
private void attachToJni() {
@ -222,6 +232,31 @@ public class FlutterEngine {
return flutterJNI.isAttached();
}
/**
* 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.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.

View File

@ -21,6 +21,7 @@ import io.flutter.plugin.common.StandardMessageCodecTest;
import io.flutter.plugin.editing.TextInputPluginTest;
import io.flutter.plugin.platform.SingleViewPresentationTest;
import io.flutter.util.PreconditionsTest;
import test.io.flutter.embedding.engine.FlutterEngineTest;
import test.io.flutter.embedding.engine.dart.DartExecutorTest;
@RunWith(Suite.class)
@ -30,6 +31,7 @@ import test.io.flutter.embedding.engine.dart.DartExecutorTest;
FlutterActivityTest.class,
FlutterAndroidComponentTest.class,
FlutterEngineCacheTest.class,
FlutterEngineTest.class,
FlutterFragmentTest.class,
FlutterJNITest.class,
FlutterRendererTest.class,

View File

@ -0,0 +1,34 @@
package test.io.flutter.embedding.engine;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.loader.FlutterLoader;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@Config(manifest=Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class FlutterEngineTest {
@Test
public void itDoesNotCrashIfGeneratedPluginRegistrantIsUnavailable() {
FlutterJNI flutterJNI = mock(FlutterJNI.class);
when(flutterJNI.isAttached()).thenReturn(true);
FlutterEngine flutterEngine = new FlutterEngine(
RuntimeEnvironment.application,
mock(FlutterLoader.class),
flutterJNI,
new String[] {},
true
);
// The fact that the above constructor executed without error means that
// it dealt with a non-existent GeneratedPluginRegistrant.
}
}