mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Deduplicate plugin registration logic and make error logs visible - take 2 (flutter/engine#25395)
This commit is contained in:
parent
662fdf023c
commit
a04eb5db2b
@ -7,6 +7,7 @@ package io.flutter;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import io.flutter.embedding.engine.FlutterJNI;
|
||||
import io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager;
|
||||
import io.flutter.embedding.engine.loader.FlutterLoader;
|
||||
|
||||
@ -65,13 +66,17 @@ public final class FlutterInjector {
|
||||
}
|
||||
|
||||
private FlutterInjector(
|
||||
@NonNull FlutterLoader flutterLoader, DeferredComponentManager deferredComponentManager) {
|
||||
@NonNull FlutterLoader flutterLoader,
|
||||
@Nullable DeferredComponentManager deferredComponentManager,
|
||||
@NonNull FlutterJNI.Factory flutterJniFactory) {
|
||||
this.flutterLoader = flutterLoader;
|
||||
this.deferredComponentManager = deferredComponentManager;
|
||||
this.flutterJniFactory = flutterJniFactory;
|
||||
}
|
||||
|
||||
private FlutterLoader flutterLoader;
|
||||
private DeferredComponentManager deferredComponentManager;
|
||||
private FlutterJNI.Factory flutterJniFactory;
|
||||
|
||||
/** Returns the {@link FlutterLoader} instance to use for the Flutter Android engine embedding. */
|
||||
@NonNull
|
||||
@ -88,6 +93,11 @@ public final class FlutterInjector {
|
||||
return deferredComponentManager;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public FlutterJNI.Factory getFlutterJNIFactory() {
|
||||
return flutterJniFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder used to supply a custom FlutterInjector instance to {@link
|
||||
* FlutterInjector#setInstance(FlutterInjector)}.
|
||||
@ -97,6 +107,7 @@ public final class FlutterInjector {
|
||||
public static final class Builder {
|
||||
private FlutterLoader flutterLoader;
|
||||
private DeferredComponentManager deferredComponentManager;
|
||||
private FlutterJNI.Factory flutterJniFactory;
|
||||
/**
|
||||
* Sets a {@link FlutterLoader} override.
|
||||
*
|
||||
@ -113,9 +124,18 @@ public final class FlutterInjector {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFlutterJNIFactory(@NonNull FlutterJNI.Factory factory) {
|
||||
this.flutterJniFactory = factory;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void fillDefaults() {
|
||||
if (flutterJniFactory == null) {
|
||||
flutterJniFactory = new FlutterJNI.Factory();
|
||||
}
|
||||
|
||||
if (flutterLoader == null) {
|
||||
flutterLoader = new FlutterLoader();
|
||||
flutterLoader = new FlutterLoader(flutterJniFactory.provideFlutterJNI());
|
||||
}
|
||||
// DeferredComponentManager's intended default is null.
|
||||
}
|
||||
@ -127,7 +147,7 @@ public final class FlutterInjector {
|
||||
public FlutterInjector build() {
|
||||
fillDefaults();
|
||||
|
||||
return new FlutterInjector(flutterLoader, deferredComponentManager);
|
||||
return new FlutterInjector(flutterLoader, deferredComponentManager, flutterJniFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -919,12 +919,21 @@ public class FlutterActivity extends Activity
|
||||
* <p>This method is called after {@link #provideFlutterEngine(Context)}.
|
||||
*
|
||||
* <p>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.
|
||||
* method unless the FlutterEngine for this activity was externally created. To avoid the
|
||||
* automatic plugin registration for implicitly created FlutterEngines, 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) {
|
||||
if (delegate.isFlutterEngineFromHost()) {
|
||||
// If the FlutterEngine was explicitly built and injected into this FlutterActivity, the
|
||||
// builder should explicitly decide whether to automatically register plugins via the
|
||||
// FlutterEngine's construction parameter or via the AndroidManifest metadata.
|
||||
return;
|
||||
}
|
||||
|
||||
GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
|
||||
}
|
||||
|
||||
|
||||
@ -910,6 +910,14 @@ public class FlutterFragment extends Fragment
|
||||
return getArguments().getString(ARG_CACHED_ENGINE_ID, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true a {@code FlutterEngine} was explicitly created and injected into the {@code
|
||||
* FlutterFragment} rather than one that was created implicitly in the {@code FlutterFragment}.
|
||||
*/
|
||||
/* package */ boolean isFlutterEngineInjected() {
|
||||
return delegate.isFlutterEngineFromHost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if the {@link FlutterEngine} within this {@code FlutterFragment} should outlive
|
||||
* the {@code FlutterFragment}, itself.
|
||||
|
||||
@ -580,12 +580,21 @@ public class FlutterFragmentActivity extends FragmentActivity
|
||||
* <p>This method is called after {@link #provideFlutterEngine(Context)}.
|
||||
*
|
||||
* <p>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.
|
||||
* method unless the FlutterEngine for this activity was externally created. To avoid the
|
||||
* automatic plugin registration for implicitly created FlutterEngines, 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) {
|
||||
if (flutterFragment.isFlutterEngineInjected()) {
|
||||
// If the FlutterEngine was explicitly built and injected into this FlutterActivity, the
|
||||
// builder should explicitly decide whether to automatically register plugins via the
|
||||
// FlutterEngine's construction parameter or via the AndroidManifest metadata.
|
||||
return;
|
||||
}
|
||||
|
||||
GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface;
|
||||
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;
|
||||
@ -36,7 +37,6 @@ import io.flutter.embedding.engine.systemchannels.SystemChannel;
|
||||
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;
|
||||
|
||||
@ -157,7 +157,7 @@ public class FlutterEngine {
|
||||
* <p>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 */ null, new FlutterJNI(), dartVmArgs, true);
|
||||
this(context, /* flutterLoader */ null, /* flutterJNI */ null, dartVmArgs, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -173,7 +173,7 @@ public class FlutterEngine {
|
||||
this(
|
||||
context,
|
||||
/* flutterLoader */ null,
|
||||
new FlutterJNI(),
|
||||
/* flutterJNI */ null,
|
||||
dartVmArgs,
|
||||
automaticallyRegisterPlugins);
|
||||
}
|
||||
@ -204,7 +204,7 @@ public class FlutterEngine {
|
||||
this(
|
||||
context,
|
||||
/* flutterLoader */ null,
|
||||
new FlutterJNI(),
|
||||
/* flutterJNI */ null,
|
||||
new PlatformViewsController(),
|
||||
dartVmArgs,
|
||||
automaticallyRegisterPlugins,
|
||||
@ -282,6 +282,14 @@ public class FlutterEngine {
|
||||
} catch (NameNotFoundException e) {
|
||||
assetManager = context.getAssets();
|
||||
}
|
||||
|
||||
FlutterInjector injector = FlutterInjector.instance();
|
||||
|
||||
if (flutterJNI == null) {
|
||||
flutterJNI = injector.getFlutterJNIFactory().provideFlutterJNI();
|
||||
}
|
||||
this.flutterJNI = flutterJNI;
|
||||
|
||||
this.dartExecutor = new DartExecutor(flutterJNI, assetManager);
|
||||
this.dartExecutor.onAttachedToJNI();
|
||||
|
||||
@ -307,9 +315,8 @@ public class FlutterEngine {
|
||||
|
||||
this.localizationPlugin = new LocalizationPlugin(context, localizationChannel);
|
||||
|
||||
this.flutterJNI = flutterJNI;
|
||||
if (flutterLoader == null) {
|
||||
flutterLoader = FlutterInjector.instance().flutterLoader();
|
||||
flutterLoader = injector.flutterLoader();
|
||||
}
|
||||
|
||||
if (!flutterJNI.isAttached()) {
|
||||
@ -320,7 +327,7 @@ public class FlutterEngine {
|
||||
flutterJNI.addEngineLifecycleListener(engineLifecycleListener);
|
||||
flutterJNI.setPlatformViewsController(platformViewsController);
|
||||
flutterJNI.setLocalizationPlugin(localizationPlugin);
|
||||
flutterJNI.setDeferredComponentManager(FlutterInjector.instance().deferredComponentManager());
|
||||
flutterJNI.setDeferredComponentManager(injector.deferredComponentManager());
|
||||
|
||||
// It should typically be a fresh, unattached JNI. But on a spawned engine, the JNI instance
|
||||
// is already attached to a native shell. In that case, the Java FlutterEngine is created around
|
||||
@ -342,7 +349,7 @@ public class FlutterEngine {
|
||||
// Only automatically register plugins if both constructor parameter and
|
||||
// loaded AndroidManifest config turn this feature on.
|
||||
if (automaticallyRegisterPlugins && flutterLoader.automaticallyRegisterPlugins()) {
|
||||
registerPlugins();
|
||||
GeneratedPluginRegister.registerGeneratedPlugins(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -391,36 +398,6 @@ public class FlutterEngine {
|
||||
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.
|
||||
|
||||
@ -282,6 +282,7 @@ public class FlutterJNI {
|
||||
|
||||
@NonNull private final Looper mainLooper; // cached to avoid synchronization on repeat access.
|
||||
|
||||
// Prefer using the FlutterJNI.Factory so it's easier to test.
|
||||
public FlutterJNI() {
|
||||
// We cache the main looper so that we can ensure calls are made on the main thread
|
||||
// without consistently paying the synchronization cost of getMainLooper().
|
||||
@ -1258,4 +1259,15 @@ public class FlutterJNI {
|
||||
public interface AsyncWaitForVsyncDelegate {
|
||||
void asyncWaitForVsync(final long cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory for creating {@code FlutterJNI} instances. Useful for FlutterJNI injections during
|
||||
* tests.
|
||||
*/
|
||||
public static class Factory {
|
||||
/** @return a {@link FlutterJNI} instance. */
|
||||
public FlutterJNI provideFlutterJNI() {
|
||||
return new FlutterJNI();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ import android.view.WindowManager;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import io.flutter.BuildConfig;
|
||||
import io.flutter.FlutterInjector;
|
||||
import io.flutter.Log;
|
||||
import io.flutter.embedding.engine.FlutterJNI;
|
||||
import io.flutter.util.PathUtils;
|
||||
@ -70,7 +71,7 @@ public class FlutterLoader {
|
||||
|
||||
/** Creates a {@code FlutterLoader} that uses a default constructed {@link FlutterJNI}. */
|
||||
public FlutterLoader() {
|
||||
this(new FlutterJNI());
|
||||
this(FlutterInjector.instance().getFlutterJNIFactory().provideFlutterJNI());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -33,11 +33,12 @@ public class GeneratedPluginRegister {
|
||||
generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
|
||||
registrationMethod.invoke(null, flutterEngine);
|
||||
} catch (Exception e) {
|
||||
Log.w(
|
||||
Log.e(
|
||||
TAG,
|
||||
"Tried to automatically register plugins with FlutterEngine ("
|
||||
+ flutterEngine
|
||||
+ ") but could not find and invoke the GeneratedPluginRegistrant.");
|
||||
+ ") but could not find or invoke the GeneratedPluginRegistrant.");
|
||||
Log.e(TAG, "Received exception while registering", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import io.flutter.FlutterInjector;
|
||||
import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
|
||||
import io.flutter.embedding.engine.FlutterEngine;
|
||||
import io.flutter.embedding.engine.FlutterEngineCache;
|
||||
@ -48,11 +49,18 @@ public class FlutterActivityTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
GeneratedPluginRegistrant.clearRegisteredEngines();
|
||||
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
|
||||
when(mockFlutterJNI.isAttached()).thenReturn(true);
|
||||
FlutterJNI.Factory mockFlutterJNIFactory = mock(FlutterJNI.Factory.class);
|
||||
when(mockFlutterJNIFactory.provideFlutterJNI()).thenReturn(mockFlutterJNI);
|
||||
FlutterInjector.setInstance(
|
||||
new FlutterInjector.Builder().setFlutterJNIFactory(mockFlutterJNIFactory).build());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
GeneratedPluginRegistrant.clearRegisteredEngines();
|
||||
FlutterInjector.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -211,12 +219,14 @@ public class FlutterActivityTest {
|
||||
|
||||
@Test
|
||||
public void itRegistersPluginsAtConfigurationTime() {
|
||||
FlutterActivity activity =
|
||||
Robolectric.buildActivity(FlutterActivityWithProvidedEngine.class).get();
|
||||
activity.onCreate(null);
|
||||
Intent intent = FlutterActivity.createDefaultIntent(RuntimeEnvironment.application);
|
||||
ActivityController<FlutterActivity> activityController =
|
||||
Robolectric.buildActivity(FlutterActivity.class, intent);
|
||||
FlutterActivity activity = activityController.get();
|
||||
|
||||
assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
|
||||
activity.configureFlutterEngine(activity.getFlutterEngine());
|
||||
// This calls onAttach on FlutterActivityAndFragmentDelegate and subsequently
|
||||
// configureFlutterEngine which registers the plugins.
|
||||
activity.onCreate(null);
|
||||
|
||||
List<FlutterEngine> registeredEngines = GeneratedPluginRegistrant.getRegisteredEngines();
|
||||
assertEquals(1, registeredEngines.size());
|
||||
@ -282,6 +292,23 @@ public class FlutterActivityTest {
|
||||
"Expected FakeFlutterPlugin onCreateCalled to be true", fakeFlutterPlugin.onCreateCalled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void itDoesNotRegisterPluginsTwiceWhenUsingACachedEngine() {
|
||||
Intent intent =
|
||||
new Intent(RuntimeEnvironment.application, FlutterActivityWithProvidedEngine.class);
|
||||
ActivityController<FlutterActivityWithProvidedEngine> activityController =
|
||||
Robolectric.buildActivity(FlutterActivityWithProvidedEngine.class, intent);
|
||||
activityController.create();
|
||||
FlutterActivityWithProvidedEngine flutterActivity = activityController.get();
|
||||
flutterActivity.configureFlutterEngine(flutterActivity.getFlutterEngine());
|
||||
|
||||
List<FlutterEngine> registeredEngines = GeneratedPluginRegistrant.getRegisteredEngines();
|
||||
// This might cause the plugins to be registered twice, once by the FlutterEngine constructor,
|
||||
// and once by the default FlutterActivity.configureFlutterEngine implementation.
|
||||
// Test that it doesn't happen.
|
||||
assertEquals(1, registeredEngines.size());
|
||||
}
|
||||
|
||||
static class FlutterActivityWithProvidedEngine extends FlutterActivity {
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
@ -293,10 +320,11 @@ public class FlutterActivityTest {
|
||||
@Override
|
||||
public FlutterEngine provideFlutterEngine(@NonNull Context context) {
|
||||
FlutterJNI flutterJNI = mock(FlutterJNI.class);
|
||||
FlutterLoader flutterLoader = mock(FlutterLoader.class);
|
||||
when(flutterJNI.isAttached()).thenReturn(true);
|
||||
when(flutterLoader.automaticallyRegisterPlugins()).thenReturn(true);
|
||||
|
||||
return new FlutterEngine(
|
||||
context, mock(FlutterLoader.class), flutterJNI, new String[] {}, false);
|
||||
return new FlutterEngine(context, flutterLoader, flutterJNI, new String[] {}, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import io.flutter.FlutterInjector;
|
||||
import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
|
||||
import io.flutter.embedding.engine.FlutterEngine;
|
||||
import io.flutter.embedding.engine.FlutterJNI;
|
||||
@ -35,12 +36,20 @@ import org.robolectric.annotation.Config;
|
||||
public class FlutterFragmentActivityTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
FlutterInjector.reset();
|
||||
GeneratedPluginRegistrant.clearRegisteredEngines();
|
||||
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
|
||||
when(mockFlutterJNI.isAttached()).thenReturn(true);
|
||||
FlutterJNI.Factory mockFlutterJNIFactory = mock(FlutterJNI.Factory.class);
|
||||
when(mockFlutterJNIFactory.provideFlutterJNI()).thenReturn(mockFlutterJNI);
|
||||
FlutterInjector.setInstance(
|
||||
new FlutterInjector.Builder().setFlutterJNIFactory(mockFlutterJNIFactory).build());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
GeneratedPluginRegistrant.clearRegisteredEngines();
|
||||
FlutterInjector.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -76,7 +85,7 @@ public class FlutterFragmentActivityTest {
|
||||
@Test
|
||||
public void itRegistersPluginsAtConfigurationTime() {
|
||||
FlutterFragmentActivity activity =
|
||||
Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get();
|
||||
Robolectric.buildActivity(FlutterFragmentActivity.class).get();
|
||||
assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
|
||||
|
||||
// Calling onCreate on the FlutterFragmentActivity will create a FlutterFragment and
|
||||
@ -89,6 +98,20 @@ public class FlutterFragmentActivityTest {
|
||||
assertEquals(activity.getFlutterEngine(), registeredEngines.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void itDoesNotRegisterPluginsTwiceWhenUsingACachedEngine() {
|
||||
FlutterFragmentActivity activity =
|
||||
Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get();
|
||||
activity.onCreate(null);
|
||||
activity.configureFlutterEngine(activity.getFlutterEngine());
|
||||
|
||||
List<FlutterEngine> registeredEngines = GeneratedPluginRegistrant.getRegisteredEngines();
|
||||
// This might cause the plugins to be registered twice, once by the FlutterEngine constructor,
|
||||
// and once by the default FlutterFragmentActivity.configureFlutterEngine implementation.
|
||||
// Test that it doesn't happen.
|
||||
assertEquals(1, registeredEngines.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void itReturnsValueFromMetaDataWhenCallsShouldHandleDeepLinkingCase1()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
@ -157,10 +180,11 @@ public class FlutterFragmentActivityTest {
|
||||
@Override
|
||||
public FlutterEngine provideFlutterEngine(@NonNull Context context) {
|
||||
FlutterJNI flutterJNI = mock(FlutterJNI.class);
|
||||
FlutterLoader flutterLoader = mock(FlutterLoader.class);
|
||||
when(flutterJNI.isAttached()).thenReturn(true);
|
||||
when(flutterLoader.automaticallyRegisterPlugins()).thenReturn(true);
|
||||
|
||||
return new FlutterEngine(
|
||||
context, mock(FlutterLoader.class), flutterJNI, new String[] {}, false);
|
||||
return new FlutterEngine(context, flutterLoader, flutterJNI, new String[] {}, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ import org.mockito.stubbing.Answer;
|
||||
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)
|
||||
@ -63,6 +64,9 @@ public class FlutterEngineTest {
|
||||
@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
|
||||
@ -78,6 +82,36 @@ public class FlutterEngineTest {
|
||||
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"));
|
||||
assertEquals(
|
||||
GeneratedPluginRegistrant.pluginRegistrationException,
|
||||
ShadowLog.getLogsForTag("GeneratedPluginsRegister").get(1).throwable.getCause());
|
||||
|
||||
GeneratedPluginRegistrant.pluginRegistrationException = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void itDoesNotAutomaticallyRegistersPluginsWhenFlutterLoaderDisablesIt() {
|
||||
assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
|
||||
@ -105,18 +139,6 @@ public class FlutterEngineTest {
|
||||
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.
|
||||
@ -220,6 +242,7 @@ public class FlutterEngineTest {
|
||||
|
||||
verify(mockFlutterLoader, times(1)).startInitialization(any());
|
||||
verify(mockFlutterLoader, times(1)).ensureInitializationComplete(any(), any());
|
||||
FlutterInjector.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -13,6 +13,7 @@ import java.util.List;
|
||||
@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.
|
||||
@ -21,6 +22,9 @@ public class GeneratedPluginRegistrant {
|
||||
* all registered engines instead.
|
||||
*/
|
||||
public static void registerWith(FlutterEngine engine) {
|
||||
if (pluginRegistrationException != null) {
|
||||
throw pluginRegistrationException;
|
||||
}
|
||||
registeredEngines.add(engine);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user