Skip to content

Commit

Permalink
reland: Started using FlutterEngineGroups by default on Android (flut…
Browse files Browse the repository at this point in the history
…ter#38367)

* Revert "Revert "Started using FlutterEngineGroups by default on Android  (flutter#37822)" (flutter#38351)"

This reverts commit 13ae6eb.

* fixed the entrypoint test

* updated old test

* coldpalelight's suggestion

* added entrypoint args
  • Loading branch information
gaaclarke authored and loic-sharma committed Jan 3, 2023
1 parent 475a4e5 commit e97c315
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 27 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,13 @@ public void onFlutterUiNoLongerDisplayed() {
};

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

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

/**
Expand Down Expand Up @@ -220,6 +226,21 @@ void onAttach(@NonNull Context context) {
return activity;
}

private FlutterEngineGroup.Options addEntrypointOptions(FlutterEngineGroup.Options options) {
String appBundlePathOverride = host.getAppBundlePath();
if (appBundlePathOverride == null || appBundlePathOverride.isEmpty()) {
appBundlePathOverride = FlutterInjector.instance().flutterLoader().findAppBundlePath();
}

DartExecutor.DartEntrypoint dartEntrypoint =
new DartExecutor.DartEntrypoint(
appBundlePathOverride, host.getDartEntrypointFunctionName());
return options
.setDartEntrypoint(dartEntrypoint)
.setInitialRoute(host.getInitialRoute())
.setDartEntrypointArgs(host.getDartEntrypointArgs());
}

/**
* Obtains a reference to a FlutterEngine to back this delegate and its {@code host}.
*
Expand Down Expand Up @@ -277,17 +298,9 @@ void onAttach(@NonNull Context context) {
+ "'");
}

String appBundlePathOverride = host.getAppBundlePath();
if (appBundlePathOverride == null || appBundlePathOverride.isEmpty()) {
appBundlePathOverride = FlutterInjector.instance().flutterLoader().findAppBundlePath();
}

DartExecutor.DartEntrypoint dartEntrypoint =
new DartExecutor.DartEntrypoint(
appBundlePathOverride, host.getDartEntrypointFunctionName());
flutterEngine =
flutterEngineGroup.createAndRunEngine(
host.getContext(), dartEntrypoint, host.getInitialRoute());
addEntrypointOptions(new FlutterEngineGroup.Options(host.getContext())));
isFlutterEngineFromHost = false;
return;
}
Expand All @@ -298,12 +311,17 @@ 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(
addEntrypointOptions(
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 @@ -247,10 +247,14 @@ public void itUsesNewEngineInGroupWhenProvided() {
FlutterEngineGroup flutterEngineGroup = mock(FlutterEngineGroup.class);
FlutterEngineGroupCache.getInstance().put("my_flutter_engine_group", flutterEngineGroup);

List<String> entryPointArgs = new ArrayList<>();
entryPointArgs.add("entrypoint-arg");

// Adjust fake host to request cached engine group.
when(mockHost.getCachedEngineGroupId()).thenReturn("my_flutter_engine_group");
when(mockHost.provideFlutterEngine(any(Context.class))).thenReturn(null);
when(mockHost.shouldAttachEngineToActivity()).thenReturn(false);
when(mockHost.getDartEntrypointArgs()).thenReturn(entryPointArgs);

// Create the real object that we're testing.
FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost);
Expand All @@ -263,8 +267,15 @@ public void itUsesNewEngineInGroupWhenProvided() {
// event.
// Note: "/fake/path" and "main" come from `setUp()`.
DartExecutor.DartEntrypoint entrypoint = new DartExecutor.DartEntrypoint("/fake/path", "main");
verify(flutterEngineGroup, times(1))
.createAndRunEngine(mockHost.getContext(), entrypoint, mockHost.getInitialRoute());
ArgumentCaptor<FlutterEngineGroup.Options> optionsCaptor =
ArgumentCaptor.forClass(FlutterEngineGroup.Options.class);
verify(flutterEngineGroup, times(1)).createAndRunEngine(optionsCaptor.capture());
assertEquals(mockHost.getContext(), optionsCaptor.getValue().getContext());
assertEquals(entrypoint, optionsCaptor.getValue().getDartEntrypoint());
assertEquals(mockHost.getInitialRoute(), optionsCaptor.getValue().getInitialRoute());
assertNotNull(optionsCaptor.getValue().getDartEntrypointArgs());
assertEquals(1, optionsCaptor.getValue().getDartEntrypointArgs().size());
assertEquals("entrypoint-arg", optionsCaptor.getValue().getDartEntrypointArgs().get(0));
}

@Test(expected = IllegalStateException.class)
Expand Down Expand Up @@ -1159,6 +1170,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 e97c315

Please sign in to comment.