Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
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 @@ -783,6 +783,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/render
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/DynamicFeatureChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/LocalizationChannel.java
Expand Down
2 changes: 2 additions & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ android_java_sources = [
"io/flutter/embedding/engine/renderer/RenderSurface.java",
"io/flutter/embedding/engine/renderer/SurfaceTextureWrapper.java",
"io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java",
"io/flutter/embedding/engine/systemchannels/DynamicFeatureChannel.java",
"io/flutter/embedding/engine/systemchannels/KeyEventChannel.java",
"io/flutter/embedding/engine/systemchannels/LifecycleChannel.java",
"io/flutter/embedding/engine/systemchannels/LocalizationChannel.java",
Expand Down Expand Up @@ -477,6 +478,7 @@ action("robolectric_tests") {
"test/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorViewTest.java",
"test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java",
"test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java",
"test/io/flutter/embedding/engine/systemchannels/DynamicFeatureChannelTest.java",
"test/io/flutter/embedding/engine/systemchannels/KeyEventChannelTest.java",
"test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java",
"test/io/flutter/embedding/engine/systemchannels/RestorationChannelTest.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.flutter.FlutterInjector;
import io.flutter.Log;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.embedding.engine.plugins.PluginRegistry;
import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface;
Expand All @@ -21,6 +22,7 @@
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.embedding.engine.renderer.RenderSurface;
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
import io.flutter.embedding.engine.systemchannels.DynamicFeatureChannel;
import io.flutter.embedding.engine.systemchannels.KeyEventChannel;
import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
Expand Down Expand Up @@ -81,6 +83,7 @@ public class FlutterEngine {

// System channels.
@NonNull private final AccessibilityChannel accessibilityChannel;
@NonNull private final DynamicFeatureChannel dynamicFeatureChannel;
@NonNull private final KeyEventChannel keyEventChannel;
@NonNull private final LifecycleChannel lifecycleChannel;
@NonNull private final LocalizationChannel localizationChannel;
Expand Down Expand Up @@ -275,7 +278,11 @@ public FlutterEngine(
this.dartExecutor = new DartExecutor(flutterJNI, assetManager);
this.dartExecutor.onAttachedToJNI();

DynamicFeatureManager dynamicFeatureManager =
FlutterInjector.instance().dynamicFeatureManager();

accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI);
dynamicFeatureChannel = new DynamicFeatureChannel(dartExecutor);
keyEventChannel = new KeyEventChannel(dartExecutor);
lifecycleChannel = new LifecycleChannel(dartExecutor);
localizationChannel = new LocalizationChannel(dartExecutor);
Expand All @@ -287,6 +294,10 @@ public FlutterEngine(
systemChannel = new SystemChannel(dartExecutor);
textInputChannel = new TextInputChannel(dartExecutor);

if (dynamicFeatureManager != null) {
dynamicFeatureManager.setDynamicFeatureChannel(dynamicFeatureChannel);
Copy link
Member

Choose a reason for hiding this comment

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

if you create circular connections, best to break them yourself. Especially when there are singletons like JNI involved.

}

this.localizationPlugin = new LocalizationPlugin(context, localizationChannel);

this.flutterJNI = flutterJNI;
Expand Down Expand Up @@ -380,6 +391,7 @@ public void destroy() {
flutterJNI.detachFromNativeAndReleaseResources();
if (FlutterInjector.instance().dynamicFeatureManager() != null) {
FlutterInjector.instance().dynamicFeatureManager().destroy();
dynamicFeatureChannel.setDynamicFeatureManager(null);
}
}

Expand Down Expand Up @@ -484,6 +496,12 @@ public SettingsChannel getSettingsChannel() {
return settingsChannel;
}

/** System channel that allows manual installation and state querying of dynamic features. */
@NonNull
public DynamicFeatureChannel getDynamicFeatureChannel() {
return dynamicFeatureChannel;
}

/** System channel that sends memory pressure warnings from Android to Flutter. */
@NonNull
public SystemChannel getSystemChannel() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,7 @@ public void setDynamicFeatureManager(@Nullable DynamicFeatureManager dynamicFeat
@UiThread
public void requestDartDeferredLibrary(int loadingUnitId) {
if (dynamicFeatureManager != null) {
dynamicFeatureManager.downloadDynamicFeature(loadingUnitId, null);
dynamicFeatureManager.installDynamicFeature(loadingUnitId, null);
} else {
// TODO(garyq): Add link to setup/instructions guide wiki.
Log.e(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package io.flutter.embedding.engine.dynamicfeatures;

import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.systemchannels.DynamicFeatureChannel;

// TODO: add links to external documentation on how to use split aot features.
/**
Expand All @@ -20,14 +21,14 @@
* deferred imported library. See https://dart.dev/guides/language/language-tour#deferred-loading
* This call retrieves a unique identifier called the loading unit id, which is assigned by
* gen_snapshot during compilation. The loading unit id is passed down through the engine and
* invokes downloadDynamicFeature. Once the feature module is downloaded, loadAssets and
* invokes installDynamicFeature. Once the feature module is downloaded, loadAssets and
* loadDartLibrary should be invoked. loadDartLibrary should find shared library .so files for the
* engine to open and pass the .so path to FlutterJNI.loadDartDeferredLibrary. loadAssets should
* typically ensure the new assets are available to the engine's asset manager by passing an updated
* Android AssetManager to the engine via FlutterJNI.updateAssetManager.
*
* <p>The loadAssets and loadDartLibrary methods are separated out because they may also be called
* manually via platform channel messages. A full downloadDynamicFeature implementation should call
* manually via platform channel messages. A full installDynamicFeature implementation should call
* these two methods as needed.
*
* <p>A dynamic feature module is uniquely identified by a module name as defined in
Expand All @@ -46,14 +47,38 @@ public interface DynamicFeatureManager {
*/
public abstract void setJNI(FlutterJNI flutterJNI);

/**
* Sets the DynamicFeatureChannel system channel to handle the framework API to directly call
* methods in DynamicFeatureManager.
*
* <p>A DynamicFeatureChannel is required to handle assets-only dynamic features and manually
* installed dynamic features.
*
* <p>Since this class may be instantiated for injection before the FlutterEngine and System
* Channels are initialized, this method should be called to provide the DynamicFeatureChannel.
Copy link
Member

Choose a reason for hiding this comment

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

We're also telling implementers to now take this and do stuff with it right? Describe what's expected to be sent when on this channel if you subclass.

* Similarly, the {@link DynamicFeatureChannel.setDynamicFeatureManager} method should also be
* called with this DynamicFeatureManager instance to properly forward method invocations.
*
* <p>The {@link DynamicFeatureChannel} passes manual invocations of {@link installDynamicFeature}
* and {@link getDynamicFeatureInstallState} from the method channel to this
* DynamicFeatureManager. Upon completion of the install process, sucessful installations should
* notify the DynamicFeatureChannel by calling {@link
* DynamicFeatureChannel.completeInstallSuccess} while errors and failures should call {@link
* DynamicFeatureChannel.completeInstallError}.
*/
public abstract void setDynamicFeatureChannel(DynamicFeatureChannel channel);

/**
* Request that the feature module be downloaded and installed.
*
* <p>This method begins the download and installation of the specified feature module. For
* example, the Play Store dynamic delivery implementation uses SplitInstallManager to request the
* download of the module. Download is not complete when this method returns. The download process
* should be listened for and upon completion of download, listeners should invoke loadAssets
* first and then loadDartLibrary to complete the dynamic feature load process.
* first and then loadDartLibrary to complete the dynamic feature load process. Assets-only
* dynamic features should also call {@link DynamicFeatureChannel.completeInstallSuccess} or
* {@link DynamicFeatureChannel.completeInstallError} to complete the method channel invocation's
* dart Future.
*
* <p>Both parameters are not always necessary to identify which module to install. Asset-only
* modules do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be passed
Expand All @@ -62,12 +87,21 @@ public interface DynamicFeatureManager {
* one of loadingUnitId or moduleName must be valid or non-null.
*
* <p>Flutter will typically call this method in two ways. When invoked as part of a dart
* loadLibrary() call, a valid loadingUnitId is passed in while the moduleName is null. In this
* `loadLibrary()` call, a valid loadingUnitId is passed in while the moduleName is null. In this
* case, this method is responsible for figuring out what module the loadingUnitId corresponds to.
*
* <p>When invoked manually as part of loading an assets-only module, loadingUnitId is -1
* (invalid) and moduleName is supplied. Without a loadingUnitId, this method just downloads the
* module by name and attempts to load assets via loadAssets.
* module by name and attempts to load assets via loadAssets while loadDartLibrary is skipped,
* even if the dynamic feature module includes valid dart libs. To load dart libs, call
* `loadLibrary()` using the first way described in the previous paragraph as the method channel
* invocation will not load dart shared libraries.
*
* <p>While the Future retuned by either `loadLibary` or the method channel invocation will
* indicate when the code and assets are ready to be used, informational querying of the install
* process' state can be done with {@link getDynamicFeatureInstallState}, though the results of
* this query should not be used to decide if the dynamic feature is ready to use. Only the Future
* completion should be used to do this.
*
* @param loadingUnitId The unique identifier associated with a Dart deferred library. This id is
* assigned by the compiler and can be seen for reference in bundle_config.yaml. This ID is
Expand All @@ -85,7 +119,41 @@ public interface DynamicFeatureManager {
* associated Dart deferred library, loading unit id should a negative value and moduleName
* must be non-null.
*/
public abstract void downloadDynamicFeature(int loadingUnitId, String moduleName);
public abstract void installDynamicFeature(int loadingUnitId, String moduleName);

/**
* Gets the current state of the installation session corresponding to the specified loadingUnitId
* and/or moduleName.
*
* <p>Invocations of {@link installDynamicFeature} typically result in asynchronous downloading
Copy link
Member

Choose a reason for hiding this comment

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

though you can check the completion of that call by just waiting for that future itself no? Explain why they need this

Copy link
Contributor Author

@GaryQian GaryQian Dec 11, 2020

Choose a reason for hiding this comment

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

This is for displaying things like loading bars, status messages, etc. The status here is purely informational and is not necessary for function. I'll add that to the docs.

Copy link
Member

Choose a reason for hiding this comment

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

I thought you added somewhere the redundancy with loadLibrary. Best be explicit about the web that exists everywhere. i.e. if you installDynamicFeature, you don't really have to check this or listen or the channel, if you listen on the channel, you don't really have to wait for the loadLibrary future, if you loadLibrary, it's the same as installDynamicFeature with loading unit etc, and fully create the web and cross reference everything to everything.

i.e. we don't want users guessing there's 3-4 APIs that can more or less do the same thing. What am I missing out if I just use 1 and don't use all of them.

* and other tasks. This method enables querying of the state of the installation. Querying the
* installation state is purely informational and does not impact the installation process. The
* results of this query should not be used to decide if the dynamic feature is ready to use. Upon
* completion of installation, the Future returned by the installation request will complete. Only
* after dart Future completion is it safe to use code and assets from the dynamic feature.
*
* <p>If no dynamic feature has been installed or requested to be installed by the provided
* loadingUnitId or moduleName, then this method will return null.
*
* <p>Depending on the implementation, the returned String may vary. The Play store default
* implementation begins in the "requested" state before transitioning to the "downloading" and
* "installed" states.
*
* <p>Only sucessfully requested modules have state. Modules that are invalid or have not been
* requested with {@link installDynamicFeature} will not have a state. Due to the asynchronous
* nature of the download process, modules may not immediately have a valid state upon return of
* {@link installDynamicFeature}, though valid modules will eventually obtain a state.
*
* <p>Both parameters are not always necessary to identify which module to install. Asset-only
* modules do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be passed
* to query only with moduleName. On the other hand, it can be possible to resolve the moduleName
* based on the loadingUnitId. This resolution is done if moduleName is null. At least one of
* loadingUnitId or moduleName must be valid or non-null.
*
* @param loadingUnitId The unique identifier associated with a Dart deferred library.
* @param moduleName The dynamic feature module name as defined in bundle_config.yaml.
*/
public abstract String getDynamicFeatureInstallState(int loadingUnitId, String moduleName);

/**
* Extract and load any assets and resources from the module for use by Flutter.
Expand All @@ -102,7 +170,7 @@ public interface DynamicFeatureManager {
*
* <p>Assets shoud be loaded before the Dart deferred library is loaded, as successful loading of
* the Dart loading unit indicates the dynamic feature is fully loaded. Implementations of
* downloadDynamicFeature should invoke this after successful download.
* installDynamicFeature should invoke this after successful download.
*
* @param loadingUnitId The unique identifier associated with a Dart deferred library.
* @param moduleName The dynamic feature module name as defined in bundle_config.yaml.
Expand Down
Loading