未验证 提交 23b0e02e 编写于 作者: M Matt Carroll 提交者: GitHub

Android Embedding PR29: Improve FlutterFragment construction API + engine config API. (#8540)

上级 e6c822db
...@@ -60,24 +60,58 @@ import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; ...@@ -60,24 +60,58 @@ import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
public class FlutterFragment extends Fragment { public class FlutterFragment extends Fragment {
private static final String TAG = "FlutterFragment"; private static final String TAG = "FlutterFragment";
private static final String ARG_DART_ENTRYPOINT = "dart_entrypoint"; protected static final String ARG_DART_ENTRYPOINT = "dart_entrypoint";
private static final String ARG_INITIAL_ROUTE = "initial_route"; protected static final String ARG_INITIAL_ROUTE = "initial_route";
private static final String ARG_APP_BUNDLE_PATH = "app_bundle_path"; protected static final String ARG_APP_BUNDLE_PATH = "app_bundle_path";
private static final String ARG_FLUTTER_INITIALIZATION_ARGS = "initialization_args"; protected static final String ARG_FLUTTER_INITIALIZATION_ARGS = "initialization_args";
private static final String ARG_FLUTTERVIEW_RENDER_MODE = "flutterview_render_mode"; protected static final String ARG_FLUTTERVIEW_RENDER_MODE = "flutterview_render_mode";
private static final String ARG_FLUTTERVIEW_TRANSPARENCY_MODE = "flutterview_transparency_mode"; protected static final String ARG_FLUTTERVIEW_TRANSPARENCY_MODE = "flutterview_transparency_mode";
/** /**
* Builder that creates a new {@code FlutterFragment} with {@code arguments} that correspond * Builder that creates a new {@code FlutterFragment} with {@code arguments} that correspond
* to the values set on this {@code Builder}. * to the values set on this {@code Builder}.
* <p> * <p>
* To create a {@code FlutterFragment} with default {@code arguments}, invoke {@code build()} * To create a {@code FlutterFragment} with default {@code arguments}, invoke {@code build()}
* immeidately: * without setting any builder properties:
* {@code * {@code
* FlutterFragment fragment = new FlutterFragment.Builder().build(); * FlutterFragment fragment = new FlutterFragment.Builder().build();
* } * }
* <p>
* Subclasses of {@code FlutterFragment} that do not introduce any new arguments can use this
* {@code Builder} to construct instances of the subclass without subclassing this {@code Builder}.
* {@code
* MyFlutterFragment f = new FlutterFragment.Builder(MyFlutterFragment.class)
* .someProperty(...)
* .someOtherProperty(...)
* .build<MyFlutterFragment>();
* }
* <p>
* Subclasses of {@code FlutterFragment} that introduce new arguments should subclass this
* {@code Builder} to add the new properties:
* <ol>
* <li>Ensure the {@code FlutterFragment} subclass has a no-arg constructor.</li>
* <li>Subclass this {@code Builder}.</li>
* <li>Override the new {@code Builder}'s no-arg constructor and invoke the super constructor
* to set the {@code FlutterFragment} subclass: {@code
* public MyBuilder() {
* super(MyFlutterFragment.class);
* }
* }</li>
* <li>Add appropriate property methods for the new properties.</li>
* <li>Override {@link Builder#createArgs()}, call through to the super method, then add
* the new properties as arguments in the {@link Bundle}.</li>
* </ol>
* Once a {@code Builder} subclass is defined, the {@code FlutterFragment} subclass can be
* instantiated as follows.
* {@code
* MyFlutterFragment f = new MyBuilder()
* .someExistingProperty(...)
* .someNewProperty(...)
* .build<MyFlutterFragment>();
* }
*/ */
public static class Builder { public static class Builder {
private final Class<? extends FlutterFragment> fragmentClass;
private String dartEntrypoint = "main"; private String dartEntrypoint = "main";
private String initialRoute = "/"; private String initialRoute = "/";
private String appBundlePath = null; private String appBundlePath = null;
...@@ -85,6 +119,22 @@ public class FlutterFragment extends Fragment { ...@@ -85,6 +119,22 @@ public class FlutterFragment extends Fragment {
private FlutterView.RenderMode renderMode = FlutterView.RenderMode.surface; private FlutterView.RenderMode renderMode = FlutterView.RenderMode.surface;
private FlutterView.TransparencyMode transparencyMode = FlutterView.TransparencyMode.transparent; private FlutterView.TransparencyMode transparencyMode = FlutterView.TransparencyMode.transparent;
/**
* Constructs a {@code Builder} that is configured to construct an instance of
* {@code FlutterFragment}.
*/
public Builder() {
fragmentClass = FlutterFragment.class;
}
/**
* Constructs a {@code Builder} that is configured to construct an instance of
* {@code subclass}, which extends {@code FlutterFragment}.
*/
public Builder(@NonNull Class<? extends FlutterFragment> subclass) {
fragmentClass = subclass;
}
/** /**
* The name of the initial Dart method to invoke, defaults to "main". * The name of the initial Dart method to invoke, defaults to "main".
*/ */
...@@ -149,79 +199,49 @@ public class FlutterFragment extends Fragment { ...@@ -149,79 +199,49 @@ public class FlutterFragment extends Fragment {
return this; return this;
} }
/**
* Creates a {@link Bundle} of arguments that are assigned to the new {@code FlutterFragment}.
* <p>
* Subclasses should override this method to add new properties to the {@link Bundle}. Subclasses
* must call through to the super method to collect all existing property values.
*/
@NonNull @NonNull
public FlutterFragment build() { protected Bundle createArgs() {
FlutterFragment frag = new FlutterFragment(); Bundle args = new Bundle();
args.putString(ARG_INITIAL_ROUTE, initialRoute);
Bundle args = createArgsBundle( args.putString(ARG_APP_BUNDLE_PATH, appBundlePath);
dartEntrypoint, args.putString(ARG_DART_ENTRYPOINT, dartEntrypoint);
initialRoute, // TODO(mattcarroll): determine if we should have an explicit FlutterTestFragment instead of conflating.
appBundlePath, if (null != shellArgs) {
shellArgs, args.putStringArray(ARG_FLUTTER_INITIALIZATION_ARGS, shellArgs.toArray());
renderMode, }
transparencyMode args.putString(ARG_FLUTTERVIEW_RENDER_MODE, renderMode != null ? renderMode.name() : FlutterView.RenderMode.surface.name());
); args.putString(ARG_FLUTTERVIEW_TRANSPARENCY_MODE, transparencyMode != null ? transparencyMode.name() : FlutterView.TransparencyMode.transparent.name());
frag.setArguments(args); return args;
return frag;
} }
}
/** /**
* Creates a {@link Bundle} of arguments that can be used to configure a {@link FlutterFragment}. * Constructs a new {@code FlutterFragment} (or a subclass) that is configured based on
* This method is exposed so that developers can create subclasses of {@link FlutterFragment}. * properties set on this {@code Builder}.
* Subclasses should declare static factories that use this method to create arguments that will */
* be understood by the base class, and then the subclass can add any additional arguments it @NonNull
* wants to this {@link Bundle}. Example: public <T extends FlutterFragment> T build() {
* <pre>{@code try {
* public static MyFlutterFragment newInstance(String myNewArg) { @SuppressWarnings("unchecked")
* // Create an instance of your subclass Fragment. T frag = (T) fragmentClass.newInstance();
* MyFlutterFragment myFrag = new MyFlutterFragment(); if (frag == null) {
* throw new RuntimeException("The FlutterFragment subclass sent in the constructor ("
* // Create the Bundle or args that FlutterFragment understands. + fragmentClass.getCanonicalName() + ") does not match the expected return type.");
* Bundle args = FlutterFragment.createArgsBundle(...); }
*
* // Add your new args to the bundle. Bundle args = createArgs();
* args.putString(ARG_MY_NEW_ARG, myNewArg); frag.setArguments(args);
*
* // Give the args to your subclass Fragment. return frag;
* myFrag.setArguments(args); } catch (Exception e) {
* throw new RuntimeException("Could not instantiate FlutterFragment subclass (" + fragmentClass.getName() + ")", e);
* // Return the newly created subclass Fragment. }
* return myFrag;
* }
* }</pre>
*
* @param dartEntrypoint the name of the initial Dart method to invoke, defaults to "main"
* @param initialRoute the first route that a Flutter app will render in this {@link FlutterFragment}, defaults to "/"
* @param appBundlePath the path to the app bundle which contains the Dart app to execute
* @param flutterShellArgs any special configuration arguments for the Flutter engine
* @param renderMode render Flutter either as a {@link FlutterView.RenderMode#surface} or a
* {@link FlutterView.RenderMode#texture}. You should use {@code surface} unless
* you have a specific reason to use {@code texture}. {@code texture} comes with
* a significant performance impact, but {@code texture} can be displayed
* beneath other Android {@code View}s and animated, whereas {@code surface}
* cannot.
*
* @return Bundle of arguments that configure a {@link FlutterFragment}
*/
protected static Bundle createArgsBundle(@Nullable String dartEntrypoint,
@Nullable String initialRoute,
@Nullable String appBundlePath,
@Nullable FlutterShellArgs flutterShellArgs,
@Nullable FlutterView.RenderMode renderMode,
@Nullable FlutterView.TransparencyMode transparencyMode) {
Bundle args = new Bundle();
args.putString(ARG_INITIAL_ROUTE, initialRoute);
args.putString(ARG_APP_BUNDLE_PATH, appBundlePath);
args.putString(ARG_DART_ENTRYPOINT, dartEntrypoint);
// TODO(mattcarroll): determine if we should have an explicit FlutterTestFragment instead of conflating.
if (null != flutterShellArgs) {
args.putStringArray(ARG_FLUTTER_INITIALIZATION_ARGS, flutterShellArgs.toArray());
} }
args.putString(ARG_FLUTTERVIEW_RENDER_MODE, renderMode != null ? renderMode.name() : FlutterView.RenderMode.surface.name());
args.putString(ARG_FLUTTERVIEW_TRANSPARENCY_MODE, transparencyMode != null ? transparencyMode.name() : FlutterView.TransparencyMode.transparent.name());
return args;
} }
@Nullable @Nullable
...@@ -296,15 +316,24 @@ public class FlutterFragment extends Fragment { ...@@ -296,15 +316,24 @@ public class FlutterFragment extends Fragment {
/** /**
* Obtains a reference to a FlutterEngine to back this {@code FlutterFragment}. * Obtains a reference to a FlutterEngine to back this {@code FlutterFragment}.
* <p> * <p>
* First, the {@link FragmentActivity} that owns this {@code FlutterFragment} is * First, {@code FlutterFragment} subclasses are given an opportunity to provide a
* {@link FlutterEngine} by overriding {@link #createFlutterEngine(Context)}.
* <p>
* Second, the {@link FragmentActivity} that owns this {@code FlutterFragment} is
* given the opportunity to provide a {@link FlutterEngine} as a {@link FlutterEngineProvider}. * given the opportunity to provide a {@link FlutterEngine} as a {@link FlutterEngineProvider}.
* <p> * <p>
* If the owning {@link FragmentActivity} does not implement {@link FlutterEngineProvider}, or * If subclasses do not provide a {@link FlutterEngine}, and the owning {@link FragmentActivity}
* chooses to return {@code null}, then a new {@link FlutterEngine} is instantiated. Subclasses * does not implement {@link FlutterEngineProvider} or chooses to return {@code null}, then a new
* may override this method to provide a {@link FlutterEngine} of choice. * {@link FlutterEngine} is instantiated.
*/ */
protected void setupFlutterEngine() { private void setupFlutterEngine() {
// First, defer to the FragmentActivity that owns us to see if it wants to provide a // First, defer to subclasses for a custom FlutterEngine.
flutterEngine = createFlutterEngine(getContextCompat());
if (flutterEngine != null) {
return;
}
// Second, defer to the FragmentActivity that owns us to see if it wants to provide a
// FlutterEngine. // FlutterEngine.
FragmentActivity attachedActivity = getActivity(); FragmentActivity attachedActivity = getActivity();
if (attachedActivity instanceof FlutterEngineProvider) { if (attachedActivity instanceof FlutterEngineProvider) {
...@@ -315,18 +344,33 @@ public class FlutterFragment extends Fragment { ...@@ -315,18 +344,33 @@ public class FlutterFragment extends Fragment {
if (flutterEngine != null) { if (flutterEngine != null) {
isFlutterEngineFromActivity = true; isFlutterEngineFromActivity = true;
} }
return;
} }
// If flutterEngine is null then either our Activity is not a FlutterEngineProvider, // Neither our subclass, nor our owning Activity wanted to provide a custom FlutterEngine.
// or our Activity decided that it didn't want to provide a FlutterEngine. Either way, // Create a FlutterEngine to back our FlutterView.
// we will now create a FlutterEngine for this FlutterFragment. Log.d(TAG, "No subclass or our attached Activity provided a custom FlutterEngine. Creating a "
if (flutterEngine == null) { + "new FlutterEngine for this FlutterFragment.");
// Create a FlutterEngine to back our FlutterView. flutterEngine = new FlutterEngine(getContext());
Log.d(TAG, "Our attached Activity did not want to provide a FlutterEngine. Creating a " isFlutterEngineFromActivity = false;
+ "new FlutterEngine for this FlutterFragment."); }
flutterEngine = new FlutterEngine(getContext());
isFlutterEngineFromActivity = false; /**
} * Hook for subclasses to return a {@link FlutterEngine} with whatever configuration
* is desired.
* <p>
* This method takes precedence for creation of a {@link FlutterEngine} over any owning
* {@code Activity} that may implement {@link FlutterEngineProvider}.
* <p>
* Consider returning a cached {@link FlutterEngine} instance from this method to avoid the
* typical warm-up time that a new {@link FlutterEngine} instance requires.
* <p>
* If null is returned then a new default {@link FlutterEngine} will be created to back this
* {@code FlutterFragment}.
*/
@Nullable
protected FlutterEngine createFlutterEngine(@NonNull Context context) {
return null;
} }
@Nullable @Nullable
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册