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

[WIP]add the feature that can create a new engine with FlutterEngineGroup … #29293

Closed
wants to merge 7 commits into from
Closed
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 @@ -840,6 +840,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 @@ -187,6 +187,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 @@ -9,7 +9,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_DESTROY_ENGINE_WITH_ACTIVITY;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_ENABLE_STATE_RESTORATION;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_INITIAL_ROUTE;
Expand Down Expand Up @@ -269,7 +271,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 @@ -421,6 +423,151 @@ 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}, and creates a new {@link
* io.flutter.embedding.engine.FlutterEngine} by FlutterEngineGroup#createAndRunEngine
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can remove the last sentence

*
* <pre>{@code
* // Create a FlutterEngineGroup, usually we could create it in onCreate method of Application.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
*
* // use the intent that build by withNewEngineInGroup to start FlutterActivity
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a code snippet that demonstrates a correct use of this method?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

* @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} by FlutterEngineGroup#createAndRunEngine.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment: an example would be helpful

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

* #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, usually we could create it in onCreate method of Application.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 NewEngineInGroupIntentBuilder, and start my custom FlutterActivity with the intent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a NewEngineInGroupIntentBuilder that would build an intent to start my custom FlutterActivity subclass.

* // that build by NewEngineInGroupIntentBuilder.
* 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>}.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in app/src/main/res/values/styles.xml right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, in app/src/main/res/values/styles.xml, we can define custom theme for activity, and set the background of activity.

*
* @param backgroundMode The background mode.
* @return The engine group intent builder.
*/
@NonNull
public NewEngineInGroupIntentBuilder backgroundMode(@NonNull BackgroundMode backgroundMode) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than copying all this stuff again, can't we have them both be in the same builder class? Just have 2 constructors. One with an engine id and one with a engine group id.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes , we can have them both be in the same builder class. But this break Single Responsibility Principle.

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 @@ -775,6 +922,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 @@ -801,14 +959,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 @@ -213,6 +215,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 given an FluttenEngineGroup Id, then used a cached {@link
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this statement doesn't match the code below? The host is asked whether it would like to provide a cached FlutterEngineGroup id rather than being given one

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, you are right. I will update

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* <p>Third, the {@code host} is given an FluttenEngineGroup Id, then used a cached {@link
* <p>Third, the {@code host} is asked if it would like to use a cached
* {@link io.flutter.embedding.engine.FlutterEngineGroup}. If so, the cached
* {@link io.flutter.embedding.engine.FlutterEngineGroup} is used to create a new
* {@link io.flutter.embedding.engine.FlutterEngine}.

* 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 @@ -241,6 +247,34 @@ void onAttach(@NonNull Context context) {
return;
}

// Third, check if the host wants to use a cached FlutterEngineGroup
// and create new FlutterEngine by FlutterEngineGroup#createAndRunEngine
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// and create new FlutterEngine by FlutterEngineGroup#createAndRunEngine
// 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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this block seems unrelated to the proposed change and is a scope creep right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because the Constructor of DartEntrypoint need 2 params. One is AppBundlePath, this block provide it from Host or FlutterLoader

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 @@ -876,6 +910,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 @@ -19,9 +19,11 @@ 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_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