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

Commit b22a8c6

Browse files
authored
Let FlutterActivity/Fragment/FragmentActivity have an app bundle path override instead of eager resolving during construction (#20769)
1 parent 367c6db commit b22a8c6

File tree

6 files changed

+57
-20
lines changed

6 files changed

+57
-20
lines changed

shell/platform/android/io/flutter/FlutterInjector.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public final class FlutterInjector {
2828
* <p>This can only be called at the beginning of the program before the {@link #instance()} is
2929
* accessed.
3030
*/
31+
@VisibleForTesting
3132
public static void setInstance(@NonNull FlutterInjector injector) {
3233
if (accessed) {
3334
throw new IllegalStateException(

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface;
4545
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister;
4646
import io.flutter.plugin.platform.PlatformPlugin;
47-
import io.flutter.view.FlutterMain;
4847

4948
/**
5049
* {@code Activity} which displays a fullscreen Flutter UI.
@@ -754,12 +753,14 @@ public String getInitialRoute() {
754753
}
755754

756755
/**
757-
* The path to the bundle that contains this Flutter app's resources, e.g., Dart code snapshots.
756+
* A custom path to the bundle that contains this Flutter app's resources, e.g., Dart code
757+
* snapshots.
758758
*
759759
* <p>When this {@code FlutterActivity} is run by Flutter tooling and a data String is included in
760760
* the launching {@code Intent}, that data String is interpreted as an app bundle path.
761761
*
762-
* <p>By default, the app bundle path is obtained from {@link FlutterMain#findAppBundlePath()}.
762+
* <p>When otherwise unspecified, the value is null, which defaults to the app bundle path defined
763+
* in {@link FlutterLoader#findAppBundlePath()}.
763764
*
764765
* <p>Subclasses may override this method to return a custom app bundle path.
765766
*/
@@ -776,9 +777,7 @@ public String getAppBundlePath() {
776777
}
777778
}
778779

779-
// Return the default app bundle path.
780-
// TODO(mattcarroll): move app bundle resolution into an appropriately named class.
781-
return FlutterMain.findAppBundlePath();
780+
return null;
782781
}
783782

784783
/**

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import androidx.annotation.Nullable;
1919
import androidx.annotation.VisibleForTesting;
2020
import androidx.lifecycle.Lifecycle;
21+
import io.flutter.FlutterInjector;
2122
import io.flutter.Log;
2223
import io.flutter.app.FlutterActivity;
2324
import io.flutter.embedding.engine.FlutterEngine;
@@ -366,10 +367,15 @@ private void doInitialFlutterViewRun() {
366367
flutterEngine.getNavigationChannel().setInitialRoute(host.getInitialRoute());
367368
}
368369

370+
String appBundlePathOverride = host.getAppBundlePath();
371+
if (appBundlePathOverride == null || appBundlePathOverride.isEmpty()) {
372+
appBundlePathOverride = FlutterInjector.instance().flutterLoader().findAppBundlePath();
373+
}
374+
369375
// Configure the Dart entrypoint and execute it.
370376
DartExecutor.DartEntrypoint entrypoint =
371377
new DartExecutor.DartEntrypoint(
372-
host.getAppBundlePath(), host.getDartEntrypointFunctionName());
378+
appBundlePathOverride, host.getDartEntrypointFunctionName());
373379
flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint);
374380
}
375381

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import io.flutter.embedding.engine.FlutterShellArgs;
2323
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
2424
import io.flutter.plugin.platform.PlatformPlugin;
25-
import io.flutter.view.FlutterMain;
2625

2726
/**
2827
* {@code Fragment} which displays a Flutter UI that takes up all available {@code Fragment} space.
@@ -226,8 +225,8 @@ public NewEngineFragmentBuilder initialRoute(@NonNull String initialRoute) {
226225
}
227226

228227
/**
229-
* The path to the app bundle which contains the Dart app to execute, defaults to {@link
230-
* FlutterMain#findAppBundlePath()}
228+
* The path to the app bundle which contains the Dart app to execute. Null when unspecified,
229+
* which defaults to {@link FlutterLoader#findAppBundlePath()}
231230
*/
232231
@NonNull
233232
public NewEngineFragmentBuilder appBundlePath(@NonNull String appBundlePath) {
@@ -800,16 +799,18 @@ public String getDartEntrypointFunctionName() {
800799
}
801800

802801
/**
803-
* Returns the file path to the desired Flutter app's bundle of code.
802+
* A custom path to the bundle that contains this Flutter app's resources, e.g., Dart code
803+
* snapshots.
804804
*
805-
* <p>Defaults to {@link FlutterMain#findAppBundlePath()}.
805+
* <p>When unspecified, the value is null, which defaults to the app bundle path defined in {@link
806+
* FlutterLoader#findAppBundlePath()}.
806807
*
807808
* <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
808809
*/
809810
@Override
810811
@NonNull
811812
public String getAppBundlePath() {
812-
return getArguments().getString(ARG_APP_BUNDLE_PATH, FlutterMain.findAppBundlePath());
813+
return getArguments().getString(ARG_APP_BUNDLE_PATH);
813814
}
814815

815816
/**

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
import io.flutter.embedding.engine.FlutterShellArgs;
4242
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister;
4343
import io.flutter.plugin.platform.PlatformPlugin;
44-
import io.flutter.view.FlutterMain;
4544

4645
/**
4746
* A Flutter {@code Activity} that is based upon {@link FragmentActivity}.
@@ -581,14 +580,15 @@ public void cleanUpFlutterEngine(@NonNull FlutterEngine flutterEngine) {
581580
}
582581

583582
/**
584-
* The path to the bundle that contains this Flutter app's resources, e.g., Dart code snapshots.
583+
* A custom path to the bundle that contains this Flutter app's resources, e.g., Dart code
584+
* snapshots.
585585
*
586586
* <p>When this {@code FlutterFragmentActivity} is run by Flutter tooling and a data String is
587587
* included in the launching {@code Intent}, that data String is interpreted as an app bundle
588588
* path.
589589
*
590-
* <p>By default, the app bundle path is obtained from {@link
591-
* FlutterMain#findAppBundlePath(Context)}.
590+
* <p>When otherwise unspecified, the value is null, which defaults to the app bundle path defined
591+
* in {@link FlutterLoader#findAppBundlePath()}.
592592
*
593593
* <p>Subclasses may override this method to return a custom app bundle path.
594594
*/
@@ -605,9 +605,7 @@ protected String getAppBundlePath() {
605605
}
606606
}
607607

608-
// Return the default app bundle path.
609-
// TODO(mattcarroll): move app bundle resolution into an appropriately named class.
610-
return FlutterMain.findAppBundlePath();
608+
return null;
611609
}
612610

613611
/**

shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
import android.content.Intent;
1818
import androidx.annotation.NonNull;
1919
import androidx.lifecycle.Lifecycle;
20+
import io.flutter.FlutterInjector;
2021
import io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.Host;
2122
import io.flutter.embedding.engine.FlutterEngine;
2223
import io.flutter.embedding.engine.FlutterEngineCache;
2324
import io.flutter.embedding.engine.FlutterShellArgs;
2425
import io.flutter.embedding.engine.dart.DartExecutor;
26+
import io.flutter.embedding.engine.loader.FlutterLoader;
2527
import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface;
2628
import io.flutter.embedding.engine.renderer.FlutterRenderer;
2729
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
@@ -51,6 +53,7 @@ public class FlutterActivityAndFragmentDelegateTest {
5153

5254
@Before
5355
public void setup() {
56+
FlutterInjector.reset();
5457
// Create a mocked FlutterEngine for the various interactions required by the delegate
5558
// being tested.
5659
mockFlutterEngine = mockFlutterEngine();
@@ -307,6 +310,35 @@ public void itExecutesDartEntrypointProvidedByHost() {
307310
verify(mockFlutterEngine.getDartExecutor(), times(1)).executeDartEntrypoint(eq(dartEntrypoint));
308311
}
309312

313+
@Test
314+
public void itUsesDefaultFlutterLoaderAppBundlePathWhenUnspecified() {
315+
// ---- Test setup ----
316+
FlutterLoader mockFlutterLoader = mock(FlutterLoader.class);
317+
when(mockFlutterLoader.findAppBundlePath()).thenReturn("default_flutter_assets/path");
318+
FlutterInjector.setInstance(
319+
new FlutterInjector.Builder().setFlutterLoader(mockFlutterLoader).build());
320+
321+
// Set Dart entrypoint parameters on fake host.
322+
when(mockHost.getAppBundlePath()).thenReturn(null);
323+
when(mockHost.getDartEntrypointFunctionName()).thenReturn("myEntrypoint");
324+
325+
// Create the DartEntrypoint that we expect to be executed.
326+
DartExecutor.DartEntrypoint dartEntrypoint =
327+
new DartExecutor.DartEntrypoint("default_flutter_assets/path", "myEntrypoint");
328+
329+
// Create the real object that we're testing.
330+
FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost);
331+
332+
// --- Execute the behavior under test ---
333+
// Dart is executed in onStart().
334+
delegate.onAttach(RuntimeEnvironment.application);
335+
delegate.onCreateView(null, null, null);
336+
delegate.onStart();
337+
338+
// Verify that the host's Dart entrypoint was used.
339+
verify(mockFlutterEngine.getDartExecutor(), times(1)).executeDartEntrypoint(eq(dartEntrypoint));
340+
}
341+
310342
// "Attaching" to the surrounding Activity refers to Flutter being able to control
311343
// system chrome and other Activity-level details. If Flutter is not attached to the
312344
// surrounding Activity, it cannot control those details. This includes plugins.

0 commit comments

Comments
 (0)