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

Commit 80a15a4

Browse files
authored
Create FlutterActivity/FlutterFragment using light weight engine with FlutterEngineGroup (#36963)
1 parent 5545ccf commit 80a15a4

15 files changed

+925
-4
lines changed

ci/licenses_golden/licenses_flutter

+1
Original file line numberDiff line numberDiff line change
@@ -2314,6 +2314,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/Flutte
23142314
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineCache.java
23152315
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java
23162316
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineGroup.java
2317+
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineGroupCache.java
23172318
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
23182319
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterOverlaySurface.java
23192320
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterShellArgs.java

shell/platform/android/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ android_java_sources = [
203203
"io/flutter/embedding/engine/FlutterEngineCache.java",
204204
"io/flutter/embedding/engine/FlutterEngineConnectionRegistry.java",
205205
"io/flutter/embedding/engine/FlutterEngineGroup.java",
206+
"io/flutter/embedding/engine/FlutterEngineGroupCache.java",
206207
"io/flutter/embedding/engine/FlutterJNI.java",
207208
"io/flutter/embedding/engine/FlutterOverlaySurface.java",
208209
"io/flutter/embedding/engine/FlutterShellArgs.java",

shell/platform/android/io/flutter/embedding/android/FlutterActivity.java

+172-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DEFAULT_DART_ENTRYPOINT;
1111
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DEFAULT_INITIAL_ROUTE;
1212
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_BACKGROUND_MODE;
13+
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_CACHED_ENGINE_GROUP_ID;
1314
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_CACHED_ENGINE_ID;
15+
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_DART_ENTRYPOINT;
1416
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_DART_ENTRYPOINT_ARGS;
1517
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_DESTROY_ENGINE_WITH_ACTIVITY;
1618
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.EXTRA_ENABLE_STATE_RESTORATION;
@@ -276,7 +278,7 @@ public NewEngineIntentBuilder(@NonNull Class<? extends FlutterActivity> activity
276278
}
277279

278280
/**
279-
* The initial route that a Flutter app will render in this {@link FlutterFragment}, defaults to
281+
* The initial route that a Flutter app will render in this {@link FlutterActivity}, defaults to
280282
* "/".
281283
*
282284
* @param initialRoute The route.
@@ -448,6 +450,149 @@ public Intent build(@NonNull Context context) {
448450
}
449451
}
450452

453+
/**
454+
* Creates a {@link NewEngineInGroupIntentBuilder}, which can be used to configure an {@link
455+
* Intent} to launch a {@code FlutterActivity} by internally creating a FlutterEngine from an
456+
* existing {@link io.flutter.embedding.engine.FlutterEngineGroup} cached in a specified {@link
457+
* io.flutter.embedding.engine.FlutterEngineGroupCache}.
458+
*
459+
* <pre>{@code
460+
* // Create a FlutterEngineGroup, such as in the onCreate method of the Application.
461+
* FlutterEngineGroup engineGroup = new FlutterEngineGroup(this);
462+
* FlutterEngineGroupCache.getInstance().put("my_cached_engine_group_id", engineGroup);
463+
*
464+
* // Start a FlutterActivity with the FlutterEngineGroup by creating an intent with withNewEngineInGroup
465+
* Intent intent = FlutterActivity.withNewEngineInGroup("my_cached_engine_group_id")
466+
* .dartEntrypoint("custom_entrypoint")
467+
* .initialRoute("/custom/route")
468+
* .backgroundMode(BackgroundMode.transparent)
469+
* .build(context);
470+
* startActivity(intent);
471+
* }</pre>
472+
*
473+
* @param engineGroupId A cached engine group ID.
474+
* @return The builder.
475+
*/
476+
public static NewEngineInGroupIntentBuilder withNewEngineInGroup(@NonNull String engineGroupId) {
477+
return new NewEngineInGroupIntentBuilder(FlutterActivity.class, engineGroupId);
478+
}
479+
480+
/**
481+
* Builder to create an {@code Intent} that launches a {@code FlutterActivity} with a new {@link
482+
* FlutterEngine} created by FlutterEngineGroup#createAndRunEngine.
483+
*/
484+
public static class NewEngineInGroupIntentBuilder {
485+
private final Class<? extends FlutterActivity> activityClass;
486+
private final String cachedEngineGroupId;
487+
private String dartEntrypoint = DEFAULT_DART_ENTRYPOINT;
488+
private String initialRoute = DEFAULT_INITIAL_ROUTE;
489+
private String backgroundMode = DEFAULT_BACKGROUND_MODE;
490+
491+
/**
492+
* Constructor that allows this {@code NewEngineInGroupIntentBuilder} to be used by subclasses
493+
* of {@code FlutterActivity}.
494+
*
495+
* <p>Subclasses of {@code FlutterActivity} should provide their own static version of {@link
496+
* #withNewEngineInGroup}, which returns an instance of {@code NewEngineInGroupIntentBuilder}
497+
* constructed with a {@code Class} reference to the {@code FlutterActivity} subclass, e.g.:
498+
*
499+
* <p>{@code return new NewEngineInGroupIntentBuilder(MyFlutterActivity.class,
500+
* cacheedEngineGroupId); }
501+
*
502+
* <pre>{@code
503+
* // Create a FlutterEngineGroup, such as in the onCreate method of the Application.
504+
* FlutterEngineGroup engineGroup = new FlutterEngineGroup(this);
505+
* FlutterEngineGroupCache.getInstance().put("my_cached_engine_group_id", engineGroup);
506+
*
507+
* // Create a NewEngineInGroupIntentBuilder that would build an intent to start my custom FlutterActivity subclass.
508+
* FlutterActivity.NewEngineInGroupIntentBuilder intentBuilder =
509+
* new FlutterActivity.NewEngineInGroupIntentBuilder(
510+
* MyFlutterActivity.class,
511+
* app.engineGroupId);
512+
* intentBuilder.dartEntrypoint("main")
513+
* .initialRoute("/custom/route")
514+
* .backgroundMode(BackgroundMode.transparent);
515+
* startActivity(intentBuilder.build(context));
516+
* }</pre>
517+
*
518+
* @param activityClass A subclass of {@code FlutterActivity}.
519+
* @param engineGroupId The engine group id.
520+
*/
521+
public NewEngineInGroupIntentBuilder(
522+
@NonNull Class<? extends FlutterActivity> activityClass, @NonNull String engineGroupId) {
523+
this.activityClass = activityClass;
524+
this.cachedEngineGroupId = engineGroupId;
525+
}
526+
527+
/**
528+
* The Dart entrypoint that will be executed in the newly created FlutterEngine as soon as the
529+
* Dart snapshot is loaded. Default to "main".
530+
*
531+
* @param dartEntrypoint The dart entrypoint's name
532+
* @return The engine group intent builder
533+
*/
534+
@NonNull
535+
public NewEngineInGroupIntentBuilder dartEntrypoint(@NonNull String dartEntrypoint) {
536+
this.dartEntrypoint = dartEntrypoint;
537+
return this;
538+
}
539+
540+
/**
541+
* The initial route that a Flutter app will render in this {@link FlutterActivity}, defaults to
542+
* "/".
543+
*
544+
* @param initialRoute The route.
545+
* @return The engine group intent builder.
546+
*/
547+
@NonNull
548+
public NewEngineInGroupIntentBuilder initialRoute(@NonNull String initialRoute) {
549+
this.initialRoute = initialRoute;
550+
return this;
551+
}
552+
553+
/**
554+
* The mode of {@code FlutterActivity}'s background, either {@link BackgroundMode#opaque} or
555+
* {@link BackgroundMode#transparent}.
556+
*
557+
* <p>The default background mode is {@link BackgroundMode#opaque}.
558+
*
559+
* <p>Choosing a background mode of {@link BackgroundMode#transparent} will configure the inner
560+
* {@link FlutterView} of this {@code FlutterActivity} to be configured with a {@link
561+
* FlutterTextureView} to support transparency. This choice has a non-trivial performance
562+
* impact. A transparent background should only be used if it is necessary for the app design
563+
* being implemented.
564+
*
565+
* <p>A {@code FlutterActivity} that is configured with a background mode of {@link
566+
* BackgroundMode#transparent} must have a theme applied to it that includes the following
567+
* property: {@code <item name="android:windowIsTranslucent">true</item>}.
568+
*
569+
* @param backgroundMode The background mode.
570+
* @return The engine group intent builder.
571+
*/
572+
@NonNull
573+
public NewEngineInGroupIntentBuilder backgroundMode(@NonNull BackgroundMode backgroundMode) {
574+
this.backgroundMode = backgroundMode.name();
575+
return this;
576+
}
577+
578+
/**
579+
* Creates and returns an {@link Intent} that will launch a {@code FlutterActivity} with the
580+
* desired configuration.
581+
*
582+
* @param context The context. e.g. An Activity.
583+
* @return The intent.
584+
*/
585+
@NonNull
586+
public Intent build(@NonNull Context context) {
587+
return new Intent(context, activityClass)
588+
.putExtra(EXTRA_DART_ENTRYPOINT, dartEntrypoint)
589+
.putExtra(EXTRA_INITIAL_ROUTE, initialRoute)
590+
.putExtra(EXTRA_CACHED_ENGINE_GROUP_ID, cachedEngineGroupId)
591+
.putExtra(EXTRA_BACKGROUND_MODE, backgroundMode)
592+
.putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, true);
593+
}
594+
}
595+
451596
// Delegate that runs all lifecycle and OS hook logic that is common between
452597
// FlutterActivity and FlutterFragment. See the FlutterActivityAndFragmentDelegate
453598
// implementation for details about why it exists.
@@ -866,6 +1011,17 @@ public String getCachedEngineId() {
8661011
return getIntent().getStringExtra(EXTRA_CACHED_ENGINE_ID);
8671012
}
8681013

1014+
/**
1015+
* Returns the ID of a statically cached {@link io.flutter.embedding.engine.FlutterEngineGroup} to
1016+
* use within this {@code FlutterActivity}, or {@code null} if this {@code FlutterActivity} does
1017+
* not want to use a cached {@link io.flutter.embedding.engine.FlutterEngineGroup}.
1018+
*/
1019+
@Override
1020+
@Nullable
1021+
public String getCachedEngineGroupId() {
1022+
return getIntent().getStringExtra(EXTRA_CACHED_ENGINE_GROUP_ID);
1023+
}
1024+
8691025
/**
8701026
* Returns false if the {@link io.flutter.embedding.engine.FlutterEngine} backing this {@code
8711027
* FlutterActivity} should outlive this {@code FlutterActivity}, or true to be destroyed when the
@@ -892,14 +1048,26 @@ public boolean shouldDestroyEngineWithHost() {
8921048
/**
8931049
* The Dart entrypoint that will be executed as soon as the Dart snapshot is loaded.
8941050
*
895-
* <p>This preference can be controlled by setting a {@code <meta-data>} called {@link
896-
* FlutterActivityLaunchConfigs#DART_ENTRYPOINT_META_DATA_KEY} within the Android manifest
897-
* definition for this {@code FlutterActivity}.
1051+
* <p>This preference can be controlled with 2 methods:
1052+
*
1053+
* <ol>
1054+
* <li>Pass a boolean as {@link FlutterActivityLaunchConfigs#EXTRA_DART_ENTRYPOINT} with the
1055+
* launching {@code Intent}, or
1056+
* <li>Set a {@code <meta-data>} called {@link
1057+
* FlutterActivityLaunchConfigs#DART_ENTRYPOINT_META_DATA_KEY} within the Android manifest
1058+
* definition for this {@code FlutterActivity}
1059+
* </ol>
1060+
*
1061+
* If both preferences are set, the {@code Intent} preference takes priority.
8981062
*
8991063
* <p>Subclasses may override this method to directly control the Dart entrypoint.
9001064
*/
9011065
@NonNull
9021066
public String getDartEntrypointFunctionName() {
1067+
if (getIntent().hasExtra(EXTRA_DART_ENTRYPOINT)) {
1068+
return getIntent().getStringExtra(EXTRA_DART_ENTRYPOINT);
1069+
}
1070+
9031071
try {
9041072
Bundle metaData = getMetaData();
9051073
String desiredDartEntrypoint =

shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java

+37
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import io.flutter.Log;
2525
import io.flutter.embedding.engine.FlutterEngine;
2626
import io.flutter.embedding.engine.FlutterEngineCache;
27+
import io.flutter.embedding.engine.FlutterEngineGroup;
28+
import io.flutter.embedding.engine.FlutterEngineGroupCache;
2729
import io.flutter.embedding.engine.FlutterShellArgs;
2830
import io.flutter.embedding.engine.dart.DartExecutor;
2931
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
@@ -230,6 +232,10 @@ void onAttach(@NonNull Context context) {
230232
* <p>Second, the {@code host} is given an opportunity to provide a {@link
231233
* io.flutter.embedding.engine.FlutterEngine} via {@link Host#provideFlutterEngine(Context)}.
232234
*
235+
* <p>Third, the {@code host} is asked if it would like to use a cached {@link
236+
* io.flutter.embedding.engine.FlutterEngineGroup} to create a new {@link FlutterEngine} by {@link
237+
* FlutterEngineGroup#createAndRunEngine}
238+
*
233239
* <p>If the {@code host} does not provide a {@link io.flutter.embedding.engine.FlutterEngine},
234240
* then a new {@link FlutterEngine} is instantiated.
235241
*/
@@ -258,6 +264,34 @@ void onAttach(@NonNull Context context) {
258264
return;
259265
}
260266

267+
// Third, check if the host wants to use a cached FlutterEngineGroup
268+
// and create new FlutterEngine using FlutterEngineGroup#createAndRunEngine
269+
String cachedEngineGroupId = host.getCachedEngineGroupId();
270+
if (cachedEngineGroupId != null) {
271+
FlutterEngineGroup flutterEngineGroup =
272+
FlutterEngineGroupCache.getInstance().get(cachedEngineGroupId);
273+
if (flutterEngineGroup == null) {
274+
throw new IllegalStateException(
275+
"The requested cached FlutterEngineGroup did not exist in the FlutterEngineGroupCache: '"
276+
+ cachedEngineGroupId
277+
+ "'");
278+
}
279+
280+
String appBundlePathOverride = host.getAppBundlePath();
281+
if (appBundlePathOverride == null || appBundlePathOverride.isEmpty()) {
282+
appBundlePathOverride = FlutterInjector.instance().flutterLoader().findAppBundlePath();
283+
}
284+
285+
DartExecutor.DartEntrypoint dartEntrypoint =
286+
new DartExecutor.DartEntrypoint(
287+
appBundlePathOverride, host.getDartEntrypointFunctionName());
288+
flutterEngine =
289+
flutterEngineGroup.createAndRunEngine(
290+
host.getContext(), dartEntrypoint, host.getInitialRoute());
291+
isFlutterEngineFromHost = false;
292+
return;
293+
}
294+
261295
// Our host did not provide a custom FlutterEngine. Create a FlutterEngine to back our
262296
// FlutterView.
263297
Log.v(
@@ -915,6 +949,9 @@ private void ensureAlive() {
915949
@Nullable
916950
String getCachedEngineId();
917951

952+
@Nullable
953+
String getCachedEngineGroupId();
954+
918955
/**
919956
* Returns true if the {@link io.flutter.embedding.engine.FlutterEngine} used in this delegate
920957
* should be destroyed when the host/delegate are destroyed.

shell/platform/android/io/flutter/embedding/android/FlutterActivityLaunchConfigs.java

+2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ public class FlutterActivityLaunchConfigs {
2020
/* package */ static final String HANDLE_DEEPLINKING_META_DATA_KEY =
2121
"flutter_deeplinking_enabled";
2222
// Intent extra arguments.
23+
/* package */ static final String EXTRA_DART_ENTRYPOINT = "dart_entrypoint";
2324
/* package */ static final String EXTRA_INITIAL_ROUTE = "route";
2425
/* package */ static final String EXTRA_BACKGROUND_MODE = "background_mode";
2526
/* package */ static final String EXTRA_CACHED_ENGINE_ID = "cached_engine_id";
2627
/* package */ static final String EXTRA_DART_ENTRYPOINT_ARGS = "dart_entrypoint_args";
28+
/* package */ static final String EXTRA_CACHED_ENGINE_GROUP_ID = "cached_engine_group_id";
2729
/* package */ static final String EXTRA_DESTROY_ENGINE_WITH_ACTIVITY =
2830
"destroy_engine_with_activity";
2931
/* package */ static final String EXTRA_ENABLE_STATE_RESTORATION = "enable_state_restoration";

0 commit comments

Comments
 (0)