Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Create FlutterActivity/FlutterFragment using light weight engine with FlutterEngineGroup #36963

Merged
merged 16 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -2314,6 +2314,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/Flutte
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineCache.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineGroup.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineGroupCache.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterOverlaySurface.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java
Expand Down
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ android_java_sources = [
"io/flutter/embedding/engine/FlutterEngineCache.java",
"io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java",
"io/flutter/embedding/engine/FlutterEngineGroup.java",
"io/flutter/embedding/engine/FlutterEngineGroupCache.java",
"io/flutter/embedding/engine/FlutterJNI.java",
"io/flutter/embedding/engine/FlutterOverlaySurface.java",
"io/flutter/embedding/engine/FlutterShellArgs.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DEFAULT_DART_ENTRYPOINT;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DEFAULT_INITIAL_ROUTE;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_BACKGROUND_MODE;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_CACHED_ENGINE_GROUP_ID;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_CACHED_ENGINE_ID;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_DART_ENTRYPOINT;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_DART_ENTRYPOINT_ARGS;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_DESTROY_ENGINE_WITH_ACTIVITY;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_ENABLE_STATE_RESTORATION;
Expand Down Expand Up @@ -276,7 +278,7 @@ public NewEngineIntentBuilder(@NonNull Class<? extends FlutterActivity> activity
}

/**
* The initial route that a Flutter app will render in this {@link FlutterFragment}, defaults to
* The initial route that a Flutter app will render in this {@link FlutterActivity}, defaults to
* "/".
*
* @param initialRoute The route.
Expand Down Expand Up @@ -448,6 +450,149 @@ public Intent build(@NonNull Context context) {
}
}

/**
* Creates a {@link NewEngineInGroupIntentBuilder}, which can be used to configure an {@link
* Intent} to launch a {@code FlutterActivity} by internally creating a FlutterEngine from an
* existing {@link io.flutter.embedding.engine.FlutterEngineGroup} cached in a specified {@link
* io.flutter.embedding.engine.FlutterEngineGroupCache}.
*
* <pre>{@code
* // Create a FlutterEngineGroup, such as in the onCreate method of the Application.
* FlutterEngineGroup engineGroup = new FlutterEngineGroup(this);
* FlutterEngineGroupCache.getInstance().put("my_cached_engine_group_id", engineGroup);
*
* // Start a FlutterActivity with the FlutterEngineGroup by creating an intent with withNewEngineInGroup
* Intent intent = FlutterActivity.withNewEngineInGroup("my_cached_engine_group_id")
* .dartEntrypoint("custom_entrypoint")
* .initialRoute("/custom/route")
* .backgroundMode(BackgroundMode.transparent)
* .build(context);
* startActivity(intent);
* }</pre>
*
* @param engineGroupId A cached engine group ID.
* @return The builder.
*/
public static NewEngineInGroupIntentBuilder withNewEngineInGroup(@NonNull String engineGroupId) {
return new NewEngineInGroupIntentBuilder(FlutterActivity.class, engineGroupId);
}

/**
* Builder to create an {@code Intent} that launches a {@code FlutterActivity} with a new {@link
* FlutterEngine} created by FlutterEngineGroup#createAndRunEngine.
*/
public static class NewEngineInGroupIntentBuilder {
private final Class<? extends FlutterActivity> activityClass;
private final String cachedEngineGroupId;
private String dartEntrypoint = DEFAULT_DART_ENTRYPOINT;
private String initialRoute = DEFAULT_INITIAL_ROUTE;
private String backgroundMode = DEFAULT_BACKGROUND_MODE;

/**
* Constructor that allows this {@code NewEngineInGroupIntentBuilder} to be used by subclasses
* of {@code FlutterActivity}.
*
* <p>Subclasses of {@code FlutterActivity} should provide their own static version of {@link
* #withNewEngineInGroup}, which returns an instance of {@code NewEngineInGroupIntentBuilder}
* constructed with a {@code Class} reference to the {@code FlutterActivity} subclass, e.g.:
*
* <p>{@code return new NewEngineInGroupIntentBuilder(MyFlutterActivity.class,
* cacheedEngineGroupId); }
*
* <pre>{@code
* // Create a FlutterEngineGroup, such as in the onCreate method of the Application.
* FlutterEngineGroup engineGroup = new FlutterEngineGroup(this);
* FlutterEngineGroupCache.getInstance().put("my_cached_engine_group_id", engineGroup);
*
* // Create a NewEngineInGroupIntentBuilder that would build an intent to start my custom FlutterActivity subclass.
* FlutterActivity.NewEngineInGroupIntentBuilder intentBuilder =
* new FlutterActivity.NewEngineInGroupIntentBuilder(
* MyFlutterActivity.class,
* app.engineGroupId);
* intentBuilder.dartEntrypoint("main")
* .initialRoute("/custom/route")
* .backgroundMode(BackgroundMode.transparent);
* startActivity(intentBuilder.build(context));
* }</pre>
*
* @param activityClass A subclass of {@code FlutterActivity}.
* @param engineGroupId The engine group id.
*/
public NewEngineInGroupIntentBuilder(
@NonNull Class<? extends FlutterActivity> activityClass, @NonNull String engineGroupId) {
this.activityClass = activityClass;
this.cachedEngineGroupId = engineGroupId;
}

/**
* The Dart entrypoint that will be executed in the newly created FlutterEngine as soon as the
* Dart snapshot is loaded. Default to "main".
*
* @param dartEntrypoint The dart entrypoint's name
* @return The engine group intent builder
*/
@NonNull
public NewEngineInGroupIntentBuilder dartEntrypoint(@NonNull String dartEntrypoint) {
this.dartEntrypoint = dartEntrypoint;
return this;
}

/**
* The initial route that a Flutter app will render in this {@link FlutterActivity}, defaults to
* "/".
*
* @param initialRoute The route.
* @return The engine group intent builder.
*/
@NonNull
public NewEngineInGroupIntentBuilder initialRoute(@NonNull String initialRoute) {
this.initialRoute = initialRoute;
return this;
}

/**
* The mode of {@code FlutterActivity}'s background, either {@link BackgroundMode#opaque} or
* {@link BackgroundMode#transparent}.
*
* <p>The default background mode is {@link BackgroundMode#opaque}.
*
* <p>Choosing a background mode of {@link BackgroundMode#transparent} will configure the inner
* {@link FlutterView} of this {@code FlutterActivity} to be configured with a {@link
* FlutterTextureView} to support transparency. This choice has a non-trivial performance
* impact. A transparent background should only be used if it is necessary for the app design
* being implemented.
*
* <p>A {@code FlutterActivity} that is configured with a background mode of {@link
* BackgroundMode#transparent} must have a theme applied to it that includes the following
* property: {@code <item name="android:windowIsTranslucent">true</item>}.
*
* @param backgroundMode The background mode.
* @return The engine group intent builder.
*/
@NonNull
public NewEngineInGroupIntentBuilder backgroundMode(@NonNull BackgroundMode backgroundMode) {
this.backgroundMode = backgroundMode.name();
return this;
}

/**
* Creates and returns an {@link Intent} that will launch a {@code FlutterActivity} with the
* desired configuration.
*
* @param context The context. e.g. An Activity.
* @return The intent.
*/
@NonNull
public Intent build(@NonNull Context context) {
return new Intent(context, activityClass)
.putExtra(EXTRA_DART_ENTRYPOINT, dartEntrypoint)
.putExtra(EXTRA_INITIAL_ROUTE, initialRoute)
.putExtra(EXTRA_CACHED_ENGINE_GROUP_ID, cachedEngineGroupId)
.putExtra(EXTRA_BACKGROUND_MODE, backgroundMode)
.putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, true);
}
}

// Delegate that runs all lifecycle and OS hook logic that is common between
// FlutterActivity and FlutterFragment. See the FlutterActivityAndFragmentDelegate
// implementation for details about why it exists.
Expand Down Expand Up @@ -866,6 +1011,17 @@ public String getCachedEngineId() {
return getIntent().getStringExtra(EXTRA_CACHED_ENGINE_ID);
}

/**
* Returns the ID of a statically cached {@link io.flutter.embedding.engine.FlutterEngineGroup} to
* use within this {@code FlutterActivity}, or {@code null} if this {@code FlutterActivity} does
* not want to use a cached {@link io.flutter.embedding.engine.FlutterEngineGroup}.
*/
@Override
@Nullable
public String getCachedEngineGroupId() {
return getIntent().getStringExtra(EXTRA_CACHED_ENGINE_GROUP_ID);
}

/**
* Returns false if the {@link io.flutter.embedding.engine.FlutterEngine} backing this {@code
* FlutterActivity} should outlive this {@code FlutterActivity}, or true to be destroyed when the
Expand All @@ -892,14 +1048,26 @@ public boolean shouldDestroyEngineWithHost() {
/**
* The Dart entrypoint that will be executed as soon as the Dart snapshot is loaded.
*
* <p>This preference can be controlled by setting a {@code <meta-data>} called {@link
* FlutterActivityLaunchConfigs#DART_ENTRYPOINT_META_DATA_KEY} within the Android manifest
* definition for this {@code FlutterActivity}.
* <p>This preference can be controlled with 2 methods:
*
* <ol>
* <li>Pass a boolean as {@link FlutterActivityLaunchConfigs#EXTRA_DART_ENTRYPOINT} with the
* launching {@code Intent}, or
* <li>Set a {@code <meta-data>} called {@link
* FlutterActivityLaunchConfigs#DART_ENTRYPOINT_META_DATA_KEY} within the Android manifest
* definition for this {@code FlutterActivity}
* </ol>
*
* If both preferences are set, the {@code Intent} preference takes priority.
*
* <p>Subclasses may override this method to directly control the Dart entrypoint.
*/
@NonNull
public String getDartEntrypointFunctionName() {
if (getIntent().hasExtra(EXTRA_DART_ENTRYPOINT)) {
return getIntent().getStringExtra(EXTRA_DART_ENTRYPOINT);
}

try {
Bundle metaData = getMetaData();
String desiredDartEntrypoint =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterEngineCache;
import io.flutter.embedding.engine.FlutterEngineGroup;
import io.flutter.embedding.engine.FlutterEngineGroupCache;
import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
Expand Down Expand Up @@ -230,6 +232,10 @@ void onAttach(@NonNull Context context) {
* <p>Second, the {@code host} is given an opportunity to provide a {@link
* io.flutter.embedding.engine.FlutterEngine} via {@link Host#provideFlutterEngine(Context)}.
*
* <p>Third, the {@code host} is asked if it would like to use a cached {@link
* io.flutter.embedding.engine.FlutterEngineGroup} to create a new {@link FlutterEngine} by {@link
* FlutterEngineGroup#createAndRunEngine}
*
* <p>If the {@code host} does not provide a {@link io.flutter.embedding.engine.FlutterEngine},
* then a new {@link FlutterEngine} is instantiated.
*/
Expand Down Expand Up @@ -258,6 +264,34 @@ void onAttach(@NonNull Context context) {
return;
}

// Third, check if the host wants to use a cached FlutterEngineGroup
// and create new FlutterEngine using FlutterEngineGroup#createAndRunEngine
String cachedEngineGroupId = host.getCachedEngineGroupId();
if (cachedEngineGroupId != null) {
FlutterEngineGroup flutterEngineGroup =
FlutterEngineGroupCache.getInstance().get(cachedEngineGroupId);
if (flutterEngineGroup == null) {
throw new IllegalStateException(
"The requested cached FlutterEngineGroup did not exist in the FlutterEngineGroupCache: '"
+ cachedEngineGroupId
+ "'");
}

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());
isFlutterEngineFromHost = false;
return;
}

// Our host did not provide a custom FlutterEngine. Create a FlutterEngine to back our
// FlutterView.
Log.v(
Expand Down Expand Up @@ -915,6 +949,9 @@ private void ensureAlive() {
@Nullable
String getCachedEngineId();

@Nullable
String getCachedEngineGroupId();

/**
* Returns true if the {@link io.flutter.embedding.engine.FlutterEngine} used in this delegate
* should be destroyed when the host/delegate are destroyed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ public class FlutterActivityLaunchConfigs {
/* package */ static final String HANDLE_DEEPLINKING_META_DATA_KEY =
"flutter_deeplinking_enabled";
// Intent extra arguments.
/* package */ static final String EXTRA_DART_ENTRYPOINT = "dart_entrypoint";
/* package */ static final String EXTRA_INITIAL_ROUTE = "route";
/* package */ static final String EXTRA_BACKGROUND_MODE = "background_mode";
/* package */ static final String EXTRA_CACHED_ENGINE_ID = "cached_engine_id";
/* package */ static final String EXTRA_DART_ENTRYPOINT_ARGS = "dart_entrypoint_args";
/* package */ static final String EXTRA_CACHED_ENGINE_GROUP_ID = "cached_engine_group_id";
/* package */ static final String EXTRA_DESTROY_ENGINE_WITH_ACTIVITY =
"destroy_engine_with_activity";
/* package */ static final String EXTRA_ENABLE_STATE_RESTORATION = "enable_state_restoration";
Expand Down
Loading