Skip to content

Commit

Permalink
Started using FlutterEngineGroups by default on Android (flutter#37822)
Browse files Browse the repository at this point in the history
* Started using FlutterEngineGroups by default on Android and making
them accessible to plugins.

* added docstring

* format
  • Loading branch information
gaaclarke committed Dec 15, 2022
1 parent 353d694 commit 47a358c
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 16 deletions.
Expand Up @@ -149,7 +149,8 @@
*
* <pre>{@code
* // Create and pre-warm a FlutterEngine.
* FlutterEngine flutterEngine = new FlutterEngine(context);
* FlutterEngineGroup group = new FlutterEngineGroup(context);
* FlutterEngine flutterEngine = group.createAndRunDefaultEngine(context);
* flutterEngine.getDartExecutor().executeDartEntrypoint(DartEntrypoint.createDefault());
*
* // Cache the pre-warmed FlutterEngine in the FlutterEngineCache.
Expand Down
Expand Up @@ -90,6 +90,7 @@ public interface DelegateFactory {
private boolean isFirstFrameRendered;
private boolean isAttached;
private Integer previousVisibility;
@Nullable private FlutterEngineGroup engineGroup;

@NonNull
private final FlutterUiDisplayListener flutterUiDisplayListener =
Expand All @@ -109,8 +110,15 @@ public void onFlutterUiNoLongerDisplayed() {
};

FlutterActivityAndFragmentDelegate(@NonNull Host host) {
this(host, null);
}

FlutterActivityAndFragmentDelegate(@NonNull Host host, @Nullable FlutterEngineGroup engineGroup) {
this.host = host;
this.isFirstFrameRendered = false;
if (engineGroup != null) {
this.engineGroup = engineGroup;
}
}

/**
Expand Down Expand Up @@ -298,12 +306,16 @@ void onAttach(@NonNull Context context) {
TAG,
"No preferred FlutterEngine was provided. Creating a new FlutterEngine for"
+ " this FlutterFragment.");

FlutterEngineGroup group =
engineGroup == null
? new FlutterEngineGroup(host.getContext(), host.getFlutterShellArgs().toArray())
: engineGroup;
flutterEngine =
new FlutterEngine(
host.getContext(),
host.getFlutterShellArgs().toArray(),
/*automaticallyRegisterPlugins=*/ false,
/*willProvideRestorationData=*/ host.shouldRestoreAndSaveState());
group.createAndRunEngine(
new FlutterEngineGroup.Options(host.getContext())
.setAutomaticallyRegisterPlugins(false)
.setWaitForRestorationData(host.shouldRestoreAndSaveState()));
isFlutterEngineFromHost = false;
}

Expand Down
Expand Up @@ -79,7 +79,8 @@
*
* <pre>{@code
* // Create and pre-warm a FlutterEngine.
* FlutterEngine flutterEngine = new FlutterEngine(context);
* FlutterEngineGroup group = new FlutterEngineGroup(context);
* FlutterEngine flutterEngine = group.createAndRunDefaultEngine(context);
* flutterEngine
* .getDartExecutor()
* .executeDartEntrypoint(DartEntrypoint.createDefault());
Expand Down
Expand Up @@ -9,6 +9,7 @@
import android.content.res.AssetManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import io.flutter.FlutterInjector;
import io.flutter.Log;
import io.flutter.embedding.engine.dart.DartExecutor;
Expand Down Expand Up @@ -279,6 +280,27 @@ public FlutterEngine(
@Nullable String[] dartVmArgs,
boolean automaticallyRegisterPlugins,
boolean waitForRestorationData) {
this(
context,
flutterLoader,
flutterJNI,
platformViewsController,
dartVmArgs,
automaticallyRegisterPlugins,
waitForRestorationData,
null);
}

@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public FlutterEngine(
@NonNull Context context,
@Nullable FlutterLoader flutterLoader,
@NonNull FlutterJNI flutterJNI,
@NonNull PlatformViewsController platformViewsController,
@Nullable String[] dartVmArgs,
boolean automaticallyRegisterPlugins,
boolean waitForRestorationData,
@Nullable FlutterEngineGroup group) {
AssetManager assetManager;
try {
assetManager = context.createPackageContext(context.getPackageName(), 0).getAssets();
Expand Down Expand Up @@ -347,7 +369,8 @@ public FlutterEngine(
this.platformViewsController.onAttachedToJNI();

this.pluginRegistry =
new FlutterEngineConnectionRegistry(context.getApplicationContext(), this, flutterLoader);
new FlutterEngineConnectionRegistry(
context.getApplicationContext(), this, flutterLoader, group);

localizationPlugin.sendLocalesToFlutter(context.getResources().getConfiguration());

Expand Down
Expand Up @@ -97,7 +97,8 @@
FlutterEngineConnectionRegistry(
@NonNull Context appContext,
@NonNull FlutterEngine flutterEngine,
@NonNull FlutterLoader flutterLoader) {
@NonNull FlutterLoader flutterLoader,
@Nullable FlutterEngineGroup group) {
this.flutterEngine = flutterEngine;
pluginBinding =
new FlutterPlugin.FlutterPluginBinding(
Expand All @@ -106,7 +107,8 @@
flutterEngine.getDartExecutor(),
flutterEngine.getRenderer(),
flutterEngine.getPlatformViewsController().getRegistry(),
new DefaultFlutterAssets(flutterLoader));
new DefaultFlutterAssets(flutterLoader),
group);
}

public void destroy() {
Expand Down
Expand Up @@ -210,7 +210,8 @@ public void onEngineWillDestroy() {
platformViewsController, // PlatformViewsController.
null, // String[]. The Dart VM has already started, this arguments will have no effect.
automaticallyRegisterPlugins, // boolean.
waitForRestorationData); // boolean.
waitForRestorationData, // boolean.
this);
}

/** Options that control how a FlutterEngine should be created. */
Expand Down
Expand Up @@ -6,8 +6,10 @@

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterEngineGroup;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.platform.PlatformViewRegistry;
import io.flutter.view.TextureRegistry;
Expand Down Expand Up @@ -107,20 +109,23 @@ class FlutterPluginBinding {
private final TextureRegistry textureRegistry;
private final PlatformViewRegistry platformViewRegistry;
private final FlutterAssets flutterAssets;
private final FlutterEngineGroup group;

public FlutterPluginBinding(
@NonNull Context applicationContext,
@NonNull FlutterEngine flutterEngine,
@NonNull BinaryMessenger binaryMessenger,
@NonNull TextureRegistry textureRegistry,
@NonNull PlatformViewRegistry platformViewRegistry,
@NonNull FlutterAssets flutterAssets) {
@NonNull FlutterAssets flutterAssets,
@Nullable FlutterEngineGroup group) {
this.applicationContext = applicationContext;
this.flutterEngine = flutterEngine;
this.binaryMessenger = binaryMessenger;
this.textureRegistry = textureRegistry;
this.platformViewRegistry = platformViewRegistry;
this.flutterAssets = flutterAssets;
this.group = group;
}

@NonNull
Expand Down Expand Up @@ -157,6 +162,21 @@ public PlatformViewRegistry getPlatformViewRegistry() {
public FlutterAssets getFlutterAssets() {
return flutterAssets;
}

/**
* Accessor for the {@link FlutterEngineGroup} used to create the {@link FlutterEngine} for the
* app.
*
* <p>This is useful in the rare case that a plugin has to spawn its own engine (for example,
* running an engine the background). The result is nullable since old versions of Flutter and
* custom setups may not have used a {@link FlutterEngineGroup}. Failing to use this when it is
* available will result in suboptimal performance and odd behaviors related to Dart isolate
* groups.
*/
@Nullable
public FlutterEngineGroup getEngineGroup() {
return group;
}
}

/** Provides Flutter plugins with access to Flutter asset information. */
Expand Down
Expand Up @@ -25,7 +25,8 @@
*
* <pre>
* // Create the FlutterEngine that will back the Flutter UI.
* FlutterEngine flutterEngine = new FlutterEngine(context);
* FlutterEngineGroup group = new FlutterEngineGroup(context);
* FlutterEngine flutterEngine = group.createAndRunDefaultEngine(context);
*
* // Create a ShimPluginRegistry and wrap the FlutterEngine with the shim.
* ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine, platformViewsController);
Expand Down
Expand Up @@ -1159,6 +1159,22 @@ public void itDoesNotDelayTheFirstDrawWhenRequestedAndWithAProvidedSplashScreen(
assertNull(delegate.activePreDrawListener);
}

@Test
public void usesFlutterEngineGroup() {
FlutterEngineGroup mockEngineGroup = mock(FlutterEngineGroup.class);
when(mockEngineGroup.createAndRunEngine(any(FlutterEngineGroup.Options.class)))
.thenReturn(mockFlutterEngine);
FlutterActivityAndFragmentDelegate.Host host =
mock(FlutterActivityAndFragmentDelegate.Host.class);
when(mockHost.getContext()).thenReturn(ctx);

FlutterActivityAndFragmentDelegate delegate =
new FlutterActivityAndFragmentDelegate(mockHost, mockEngineGroup);
delegate.onAttach(ctx);
FlutterEngine engineUnderTest = delegate.getFlutterEngine();
assertEquals(engineUnderTest, mockFlutterEngine);
}

/**
* Creates a mock {@link io.flutter.embedding.engine.FlutterEngine}.
*
Expand Down
Expand Up @@ -40,7 +40,7 @@ public void itDoesNotRegisterTheSamePluginTwice() {
FakeFlutterPlugin fakePlugin2 = new FakeFlutterPlugin();

FlutterEngineConnectionRegistry registry =
new FlutterEngineConnectionRegistry(context, flutterEngine, flutterLoader);
new FlutterEngineConnectionRegistry(context, flutterEngine, flutterLoader, null);

// Verify that the registry doesn't think it contains our plugin yet.
assertFalse(registry.has(fakePlugin1.getClass()));
Expand Down Expand Up @@ -86,7 +86,7 @@ public void activityResultListenerCanBeRemovedFromListener() {

// Set up the environment to get the required internal data
FlutterEngineConnectionRegistry registry =
new FlutterEngineConnectionRegistry(context, flutterEngine, flutterLoader);
new FlutterEngineConnectionRegistry(context, flutterEngine, flutterLoader, null);
FakeActivityAwareFlutterPlugin fakePlugin = new FakeActivityAwareFlutterPlugin();
registry.add(fakePlugin);
registry.attachToActivity(appComponent, lifecycle);
Expand Down Expand Up @@ -129,7 +129,7 @@ public void softwareRendering() {

// Test attachToActivity with an Activity that has no Intent.
FlutterEngineConnectionRegistry registry =
new FlutterEngineConnectionRegistry(context, flutterEngine, flutterLoader);
new FlutterEngineConnectionRegistry(context, flutterEngine, flutterLoader, null);
registry.attachToActivity(appComponent, mock(Lifecycle.class));
verify(platformViewsController).setSoftwareRendering(false);

Expand Down
Expand Up @@ -25,8 +25,11 @@
import io.flutter.FlutterInjector;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener;
import io.flutter.embedding.engine.FlutterEngineGroup;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.PluginRegistry;
import io.flutter.plugin.platform.PlatformViewsController;
import io.flutter.plugins.GeneratedPluginRegistrant;
import java.util.List;
Expand Down Expand Up @@ -323,4 +326,34 @@ public void itComesWithARunningDartExecutorIfJNIIsAlreadyAttached() throws NameN

assertTrue(engineUnderTest.getDartExecutor().isExecutingDart());
}

@Test
public void passesEngineGroupToPlugins() throws NameNotFoundException {
Context packageContext = mock(Context.class);

when(mockContext.createPackageContext(any(), anyInt())).thenReturn(packageContext);
when(flutterJNI.isAttached()).thenReturn(true);

FlutterEngineGroup mockGroup = mock(FlutterEngineGroup.class);

FlutterEngine engineUnderTest =
new FlutterEngine(
mockContext,
mock(FlutterLoader.class),
flutterJNI,
new PlatformViewsController(),
/*dartVmArgs=*/ new String[] {},
/*automaticallyRegisterPlugins=*/ false,
/*waitForRestorationData=*/ false,
mockGroup);

PluginRegistry registry = engineUnderTest.getPlugins();
FlutterPlugin mockPlugin = mock(FlutterPlugin.class);
ArgumentCaptor<FlutterPlugin.FlutterPluginBinding> pluginBindingCaptor =
ArgumentCaptor.forClass(FlutterPlugin.FlutterPluginBinding.class);
registry.add(mockPlugin);
verify(mockPlugin).onAttachedToEngine(pluginBindingCaptor.capture());
assertNotNull(pluginBindingCaptor.getValue());
assertEquals(mockGroup, pluginBindingCaptor.getValue().getEngineGroup());
}
}

0 comments on commit 47a358c

Please sign in to comment.