Rename DynamicFeature->DeferredComponent and impl uninstall DeferredComponents (flutter/engine#23224)

This commit is contained in:
Gary Qian 2021-01-07 22:02:05 -07:00 committed by GitHub
parent 4ed7eb1c38
commit e4fa163b51
15 changed files with 406 additions and 341 deletions

View File

@ -757,8 +757,8 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/Flutte
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/DeferredComponentManager.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/ApplicationInfoLoader.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterApplicationInfo.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
@ -788,7 +788,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/DeferredComponentChannel.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

View File

@ -157,8 +157,8 @@ android_java_sources = [
"io/flutter/embedding/engine/dart/DartExecutor.java",
"io/flutter/embedding/engine/dart/DartMessenger.java",
"io/flutter/embedding/engine/dart/PlatformMessageHandler.java",
"io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java",
"io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java",
"io/flutter/embedding/engine/deferredcomponents/DeferredComponentManager.java",
"io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java",
"io/flutter/embedding/engine/loader/ApplicationInfoLoader.java",
"io/flutter/embedding/engine/loader/FlutterApplicationInfo.java",
"io/flutter/embedding/engine/loader/FlutterLoader.java",
@ -188,7 +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/DeferredComponentChannel.java",
"io/flutter/embedding/engine/systemchannels/KeyEventChannel.java",
"io/flutter/embedding/engine/systemchannels/LifecycleChannel.java",
"io/flutter/embedding/engine/systemchannels/LocalizationChannel.java",
@ -472,13 +472,13 @@ action("robolectric_tests") {
"test/io/flutter/embedding/engine/RenderingComponentTest.java",
"test/io/flutter/embedding/engine/dart/DartExecutorTest.java",
"test/io/flutter/embedding/engine/dart/DartMessengerTest.java",
"test/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManagerTest.java",
"test/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManagerTest.java",
"test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java",
"test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java",
"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/DeferredComponentChannelTest.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",

View File

@ -7,7 +7,7 @@ package io.flutter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager;
import io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager;
import io.flutter.embedding.engine.loader.FlutterLoader;
/**
@ -65,13 +65,13 @@ public final class FlutterInjector {
}
private FlutterInjector(
@NonNull FlutterLoader flutterLoader, DynamicFeatureManager dynamicFeatureManager) {
@NonNull FlutterLoader flutterLoader, DeferredComponentManager deferredComponentManager) {
this.flutterLoader = flutterLoader;
this.dynamicFeatureManager = dynamicFeatureManager;
this.deferredComponentManager = deferredComponentManager;
}
private FlutterLoader flutterLoader;
private DynamicFeatureManager dynamicFeatureManager;
private DeferredComponentManager deferredComponentManager;
/** Returns the {@link FlutterLoader} instance to use for the Flutter Android engine embedding. */
@NonNull
@ -80,12 +80,12 @@ public final class FlutterInjector {
}
/**
* Returns the {@link DynamicFeatureManager} instance to use for the Flutter Android engine
* Returns the {@link DeferredComponentManager} instance to use for the Flutter Android engine
* embedding.
*/
@Nullable
public DynamicFeatureManager dynamicFeatureManager() {
return dynamicFeatureManager;
public DeferredComponentManager deferredComponentManager() {
return deferredComponentManager;
}
/**
@ -96,7 +96,7 @@ public final class FlutterInjector {
*/
public static final class Builder {
private FlutterLoader flutterLoader;
private DynamicFeatureManager dynamicFeatureManager;
private DeferredComponentManager deferredComponentManager;
/**
* Sets a {@link FlutterLoader} override.
*
@ -107,8 +107,9 @@ public final class FlutterInjector {
return this;
}
public Builder setDynamicFeatureManager(@Nullable DynamicFeatureManager dynamicFeatureManager) {
this.dynamicFeatureManager = dynamicFeatureManager;
public Builder setDeferredComponentManager(
@Nullable DeferredComponentManager deferredComponentManager) {
this.deferredComponentManager = deferredComponentManager;
return this;
}
@ -116,7 +117,7 @@ public final class FlutterInjector {
if (flutterLoader == null) {
flutterLoader = new FlutterLoader();
}
// DynamicFeatureManager's intended default is null.
// DeferredComponentManager's intended default is null.
}
/**
@ -126,7 +127,7 @@ public final class FlutterInjector {
public FlutterInjector build() {
fillDefaults();
return new FlutterInjector(flutterLoader, dynamicFeatureManager);
return new FlutterInjector(flutterLoader, deferredComponentManager);
}
}
}

View File

@ -7,11 +7,11 @@ package io.flutter.app;
import androidx.annotation.CallSuper;
import com.google.android.play.core.splitcompat.SplitCompatApplication;
import io.flutter.FlutterInjector;
import io.flutter.embedding.engine.dynamicfeatures.PlayStoreDynamicFeatureManager;
import io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager;
/**
* Flutter's extension of {@link SplitCompatApplication} that injects a {@link
* PlayStoreDynamicFeatureManager} with {@link FlutterInjector} to enable Split AOT Flutter apps.
* PlayStoreDeferredComponentManager} with {@link FlutterInjector} to enable Split AOT Flutter apps.
*
* <p>To use this class, either have your custom application class extend
* FlutterPlayStoreSplitApplication or use it directly in the app's AndroidManifest.xml by adding
@ -29,11 +29,11 @@ import io.flutter.embedding.engine.dynamicfeatures.PlayStoreDynamicFeatureManage
*
* This class is meant to be used with the Google Play store. Custom non-play store applications do
* not need to extend SplitCompatApplication and should inject a custom {@link
* io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager} implementation like so:
* io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager} implementation like so:
*
* <pre>{@code
* FlutterInjector.setInstance(
* new FlutterInjector.Builder().setDynamicFeatureManager(yourCustomManager).build());
* new FlutterInjector.Builder().setDeferredComponentManager(yourCustomManager).build());
* }</pre>
*/
public class FlutterPlayStoreSplitApplication extends SplitCompatApplication {
@ -41,11 +41,13 @@ public class FlutterPlayStoreSplitApplication extends SplitCompatApplication {
@CallSuper
public void onCreate() {
super.onCreate();
// Create and inject a PlayStoreDynamicFeatureManager, which is the default manager for
// Create and inject a PlayStoreDeferredComponentManager, which is the default manager for
// interacting with the Google Play Store.
PlayStoreDynamicFeatureManager dynamicFeatureManager =
new PlayStoreDynamicFeatureManager(this, null);
PlayStoreDeferredComponentManager deferredComponentManager =
new PlayStoreDeferredComponentManager(this, null);
FlutterInjector.setInstance(
new FlutterInjector.Builder().setDynamicFeatureManager(dynamicFeatureManager).build());
new FlutterInjector.Builder()
.setDeferredComponentManager(deferredComponentManager)
.build());
}
}

View File

@ -12,7 +12,7 @@ import androidx.annotation.Nullable;
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.deferredcomponents.DeferredComponentManager;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.embedding.engine.plugins.PluginRegistry;
import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface;
@ -22,7 +22,7 @@ import io.flutter.embedding.engine.plugins.service.ServiceControlSurface;
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.DeferredComponentChannel;
import io.flutter.embedding.engine.systemchannels.KeyEventChannel;
import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
import io.flutter.embedding.engine.systemchannels.LocalizationChannel;
@ -83,7 +83,7 @@ public class FlutterEngine {
// System channels.
@NonNull private final AccessibilityChannel accessibilityChannel;
@NonNull private final DynamicFeatureChannel dynamicFeatureChannel;
@NonNull private final DeferredComponentChannel deferredComponentChannel;
@NonNull private final KeyEventChannel keyEventChannel;
@NonNull private final LifecycleChannel lifecycleChannel;
@NonNull private final LocalizationChannel localizationChannel;
@ -278,11 +278,11 @@ public class FlutterEngine {
this.dartExecutor = new DartExecutor(flutterJNI, assetManager);
this.dartExecutor.onAttachedToJNI();
DynamicFeatureManager dynamicFeatureManager =
FlutterInjector.instance().dynamicFeatureManager();
DeferredComponentManager deferredComponentManager =
FlutterInjector.instance().deferredComponentManager();
accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI);
dynamicFeatureChannel = new DynamicFeatureChannel(dartExecutor);
deferredComponentChannel = new DeferredComponentChannel(dartExecutor);
keyEventChannel = new KeyEventChannel(dartExecutor);
lifecycleChannel = new LifecycleChannel(dartExecutor);
localizationChannel = new LocalizationChannel(dartExecutor);
@ -294,8 +294,8 @@ public class FlutterEngine {
systemChannel = new SystemChannel(dartExecutor);
textInputChannel = new TextInputChannel(dartExecutor);
if (dynamicFeatureManager != null) {
dynamicFeatureManager.setDynamicFeatureChannel(dynamicFeatureChannel);
if (deferredComponentManager != null) {
deferredComponentManager.setDeferredComponentChannel(deferredComponentChannel);
}
this.localizationPlugin = new LocalizationPlugin(context, localizationChannel);
@ -310,7 +310,7 @@ public class FlutterEngine {
flutterJNI.addEngineLifecycleListener(engineLifecycleListener);
flutterJNI.setPlatformViewsController(platformViewsController);
flutterJNI.setLocalizationPlugin(localizationPlugin);
flutterJNI.setDynamicFeatureManager(FlutterInjector.instance().dynamicFeatureManager());
flutterJNI.setDeferredComponentManager(FlutterInjector.instance().deferredComponentManager());
attachToJni();
@ -387,11 +387,11 @@ public class FlutterEngine {
platformViewsController.onDetachedFromJNI();
dartExecutor.onDetachedFromJNI();
flutterJNI.removeEngineLifecycleListener(engineLifecycleListener);
flutterJNI.setDynamicFeatureManager(null);
flutterJNI.setDeferredComponentManager(null);
flutterJNI.detachFromNativeAndReleaseResources();
if (FlutterInjector.instance().dynamicFeatureManager() != null) {
FlutterInjector.instance().dynamicFeatureManager().destroy();
dynamicFeatureChannel.setDynamicFeatureManager(null);
if (FlutterInjector.instance().deferredComponentManager() != null) {
FlutterInjector.instance().deferredComponentManager().destroy();
deferredComponentChannel.setDeferredComponentManager(null);
}
}
@ -496,10 +496,10 @@ public class FlutterEngine {
return settingsChannel;
}
/** System channel that allows manual installation and state querying of dynamic features. */
/** System channel that allows manual installation and state querying of deferred components. */
@NonNull
public DynamicFeatureChannel getDynamicFeatureChannel() {
return dynamicFeatureChannel;
public DeferredComponentChannel getDeferredComponentChannel() {
return deferredComponentChannel;
}
/** System channel that sends memory pressure warnings from Android to Flutter. */

View File

@ -20,7 +20,7 @@ import androidx.annotation.VisibleForTesting;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener;
import io.flutter.embedding.engine.dart.PlatformMessageHandler;
import io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager;
import io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager;
import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorsStack;
import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
import io.flutter.embedding.engine.renderer.RenderSurface;
@ -226,7 +226,7 @@ public class FlutterJNI {
@Nullable private LocalizationPlugin localizationPlugin;
@Nullable private PlatformViewsController platformViewsController;
@Nullable private DynamicFeatureManager dynamicFeatureManager;
@Nullable private DeferredComponentManager deferredComponentManager;
@NonNull
private final Set<EngineLifecycleListener> engineLifecycleListeners = new CopyOnWriteArraySet<>();
@ -984,15 +984,16 @@ public class FlutterJNI {
// ----- End Localization Support ----
// ----- Start Dynamic Features Support ----
// ----- Start Deferred Components Support ----
/** Sets the dynamic feature manager that is used to download and install split features. */
/** Sets the deferred component manager that is used to download and install split features. */
@UiThread
public void setDynamicFeatureManager(@Nullable DynamicFeatureManager dynamicFeatureManager) {
public void setDeferredComponentManager(
@Nullable DeferredComponentManager deferredComponentManager) {
ensureRunningOnMainThread();
this.dynamicFeatureManager = dynamicFeatureManager;
if (dynamicFeatureManager != null) {
dynamicFeatureManager.setJNI(this);
this.deferredComponentManager = deferredComponentManager;
if (deferredComponentManager != null) {
deferredComponentManager.setJNI(this);
}
}
@ -1000,7 +1001,7 @@ public class FlutterJNI {
* Called by dart to request that a Dart deferred library corresponding to loadingUnitId be
* downloaded (if necessary) and loaded into the dart vm.
*
* <p>This method delegates the task to DynamicFeatureManager, which handles the download and
* <p>This method delegates the task to DeferredComponentManager, which handles the download and
* loading of the dart library and any assets.
*
* @param loadingUnitId The loadingUnitId is assigned during compile time by gen_snapshot and is
@ -1009,13 +1010,13 @@ public class FlutterJNI {
@SuppressWarnings("unused")
@UiThread
public void requestDartDeferredLibrary(int loadingUnitId) {
if (dynamicFeatureManager != null) {
dynamicFeatureManager.installDynamicFeature(loadingUnitId, null);
if (deferredComponentManager != null) {
deferredComponentManager.installDeferredComponent(loadingUnitId, null);
} else {
// TODO(garyq): Add link to setup/instructions guide wiki.
Log.e(
TAG,
"No DynamicFeatureManager found. Android setup must be completed before using split AOT dynamic features.");
"No DeferredComponentManager found. Android setup must be completed before using split AOT deferred components.");
}
}
@ -1048,8 +1049,8 @@ public class FlutterJNI {
/**
* Adds the specified AssetManager as an APKAssetResolver in the Flutter Engine's AssetManager.
*
* <p>This may be used to update the engine AssetManager when a new dynamic feature is installed
* and a new Android AssetManager is created with access to new assets.
* <p>This may be used to update the engine AssetManager when a new deferred component is
* installed and a new Android AssetManager is created with access to new assets.
*
* @param assetManager An android AssetManager that is able to access the newly downloaded assets.
* @param assetBundlePath The subdirectory that the flutter assets are stored in. The typical
@ -1071,7 +1072,7 @@ public class FlutterJNI {
/**
* Indicates that a failure was encountered during the Android portion of downloading a dynamic
* feature module and loading a dart deferred library, which is typically done by
* DynamicFeatureManager.
* DeferredComponentManager.
*
* <p>This will inform dart that the future returned by loadLibrary() should complete with an
* error.
@ -1084,16 +1085,16 @@ public class FlutterJNI {
*/
@SuppressWarnings("unused")
@UiThread
public void dynamicFeatureInstallFailure(
public void deferredComponentInstallFailure(
int loadingUnitId, @NonNull String error, boolean isTransient) {
ensureRunningOnMainThread();
nativeDynamicFeatureInstallFailure(loadingUnitId, error, isTransient);
nativeDeferredComponentInstallFailure(loadingUnitId, error, isTransient);
}
private native void nativeDynamicFeatureInstallFailure(
private native void nativeDeferredComponentInstallFailure(
int loadingUnitId, @NonNull String error, boolean isTransient);
// ----- End Dynamic Features Support ----
// ----- End Deferred Components Support ----
// @SuppressWarnings("unused")
@UiThread

View File

@ -2,40 +2,40 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.embedding.engine.dynamicfeatures;
package io.flutter.embedding.engine.deferredcomponents;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.systemchannels.DynamicFeatureChannel;
import io.flutter.embedding.engine.systemchannels.DeferredComponentChannel;
// TODO: add links to external documentation on how to use split aot features.
/**
* Basic interface that handles downloading and loading of dynamic features.
* Basic interface that handles downloading and loading of deferred components.
*
* <p>Flutter dynamic feature support is still in early developer preview and should not be used in
* production apps yet.
* <p>Flutter deferred component support is still in early developer preview and should not be used
* in production apps yet.
*
* <p>The Flutter default implementation is PlayStoreDynamicFeatureManager.
* <p>The Flutter default implementation is PlayStoreDeferredComponentManager.
*
* <p>DynamicFeatureManager handles the embedder/Android level tasks of downloading, installing, and
* loading Dart deferred libraries. A typical code-flow begins with a Dart call to loadLibrary() on
* deferred imported library. See https://dart.dev/guides/language/language-tour#deferred-loading
* <p>DeferredComponentManager handles the embedder/Android level tasks of downloading, installing,
* and loading Dart deferred libraries. A typical code-flow begins with a Dart call to loadLibrary()
* on 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 installDynamicFeature. Once the feature module is downloaded, loadAssets and
* invokes installDeferredComponent. 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 installDynamicFeature implementation should call
* these two methods as needed.
* manually via platform channel messages. A full installDeferredComponent implementation should
* call these two methods as needed.
*
* <p>A dynamic feature module is uniquely identified by a module name as defined in
* <p>A deferred component module is uniquely identified by a module name as defined in
* bundle_config.yaml. Each feature module may contain one or more loading units, uniquely
* identified by the loading unit ID and assets.
*/
public interface DynamicFeatureManager {
public interface DeferredComponentManager {
/**
* Sets the FlutterJNI to be used to communication with the Flutter native engine.
*
@ -48,25 +48,25 @@ 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.
* Sets the DeferredComponentChannel system channel to handle the framework API to directly call
* methods in DeferredComponentManager.
*
* <p>A DynamicFeatureChannel is required to handle assets-only dynamic features and manually
* installed dynamic features.
* <p>A DeferredComponentChannel is required to handle assets-only deferred components and
* manually installed deferred components.
*
* <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.
* Similarly, the {@link DynamicFeatureChannel.setDynamicFeatureManager} method should also be
* called with this DynamicFeatureManager instance to properly forward method invocations.
* Channels are initialized, this method should be called to provide the DeferredComponentChannel.
* Similarly, the {@link DeferredComponentChannel.setDeferredComponentManager} method should also
* be called with this DeferredComponentManager 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}.
* <p>The {@link DeferredComponentChannel} passes manual invocations of {@link
* installDeferredComponent} and {@link getDeferredComponentInstallState} from the method channel
* to this DeferredComponentManager. Upon completion of the install process, sucessful
* installations should notify the DeferredComponentChannel by calling {@link
* DeferredComponentChannel.completeInstallSuccess} while errors and failures should call {@link
* DeferredComponentChannel.completeInstallError}.
*/
public abstract void setDynamicFeatureChannel(DynamicFeatureChannel channel);
public abstract void setDeferredComponentChannel(DeferredComponentChannel channel);
/**
* Request that the feature module be downloaded and installed.
@ -75,10 +75,10 @@ public interface DynamicFeatureManager {
* 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. Assets-only
* dynamic features should also call {@link DynamicFeatureChannel.completeInstallSuccess} or
* {@link DynamicFeatureChannel.completeInstallError} to complete the method channel invocation's
* dart Future.
* first and then loadDartLibrary to complete the deferred component load process. Assets-only
* deferred components should also call {@link DeferredComponentChannel.completeInstallSuccess} or
* {@link DeferredComponentChannel.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
@ -93,46 +93,47 @@ public interface DynamicFeatureManager {
* <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 while loadDartLibrary is skipped,
* even if the dynamic feature module includes valid dart libs. To load dart libs, call
* even if the deferred component 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.
* process' state can be done with {@link getDeferredComponentInstallState}, though the results of
* this query should not be used to decide if the deferred component 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
* primarily used in loadDartLibrary to indicate to Dart which Dart library is being loaded.
* Loading unit ids range from 0 to the number existing loading units. Passing a negative
* loading unit id indicates that no Dart deferred library should be loaded after download
* completes. This is the case when the dynamic feature module is an assets-only module. If a
* negative loadingUnitId is passed, then moduleName must not be null. Passing a loadingUnitId
* larger than the highest valid loading unit's id will cause the Dart loadLibrary() to
* complete with a failure.
* @param moduleName The dynamic feature module name as defined in bundle_config.yaml. This may be
* null if the dynamic feature to be loaded is associated with a loading unit/deferred dart
* library. In this case, it is this method's responsibility to map the loadingUnitId to its
* corresponding moduleName. When loading asset-only or other dynamic features without an
* associated Dart deferred library, loading unit id should a negative value and moduleName
* completes. This is the case when the deferred component module is an assets-only module. If
* a negative loadingUnitId is passed, then moduleName must not be null. Passing a
* loadingUnitId larger than the highest valid loading unit's id will cause the Dart
* loadLibrary() to complete with a failure.
* @param moduleName The deferred component module name as defined in bundle_config.yaml. This may
* be null if the deferred component to be loaded is associated with a loading unit/deferred
* dart library. In this case, it is this method's responsibility to map the loadingUnitId to
* its corresponding moduleName. When loading asset-only or other deferred components without
* an associated Dart deferred library, loading unit id should a negative value and moduleName
* must be non-null.
*/
public abstract void installDynamicFeature(int loadingUnitId, String moduleName);
public abstract void installDeferredComponent(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
* <p>Invocations of {@link installDeferredComponent} typically result in asynchronous downloading
* 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.
* results of this query should not be used to decide if the deferred component 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 deferred
* component.
*
* <p>If no dynamic feature has been installed or requested to be installed by the provided
* <p>If no deferred component 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
@ -140,9 +141,9 @@ public interface DynamicFeatureManager {
* "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
* requested with {@link installDeferredComponent} 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.
* {@link installDeferredComponent}, 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
@ -151,36 +152,36 @@ public interface DynamicFeatureManager {
* 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.
* @param moduleName The deferred component module name as defined in bundle_config.yaml.
*/
public abstract String getDynamicFeatureInstallState(int loadingUnitId, String moduleName);
public abstract String getDeferredComponentInstallState(int loadingUnitId, String moduleName);
/**
* Extract and load any assets and resources from the module for use by Flutter.
*
* <p>This method should provide a refreshed AssetManager to FlutterJNI.updateAssetManager that
* can access the new assets. If no assets are included as part of the dynamic feature, then
* can access the new assets. If no assets are included as part of the deferred component, then
* nothing needs to be done.
*
* <p>If using the Play Store dynamic feature delivery, refresh the context via: {@code
* <p>If using the Play Store deferred component delivery, refresh the context via: {@code
* context.createPackageContext(context.getPackageName(), 0);} This returns a new context, from
* which an updated asset manager may be obtained and passed to updateAssetManager in FlutterJNI.
* This process does not require loadingUnitId or moduleName, however, the two parameters are
* still present for custom implementations that store assets outside of Android's native system.
*
* <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
* installDynamicFeature should invoke this after successful download.
* the Dart loading unit indicates the deferred component is fully loaded. Implementations of
* installDeferredComponent 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.
* @param moduleName The deferred component module name as defined in bundle_config.yaml.
*/
public abstract void loadAssets(int loadingUnitId, String moduleName);
/**
* Load the .so shared library file into the Dart VM.
*
* <p>When the download of a dynamic feature module completes, this method should be called to
* <p>When the download of a deferred component module completes, this method should be called to
* find the path .so library file. The path(s) should then be passed to
* FlutterJNI.loadDartDeferredLibrary to be dlopen-ed and loaded into the Dart VM.
*
@ -198,22 +199,35 @@ public interface DynamicFeatureManager {
* primarily used in loadDartLibrary to indicate to Dart which Dart library is being loaded.
* Loading unit ids range from 0 to the number existing loading units. Negative loading unit
* ids are considered invalid and this method will result in a no-op.
* @param moduleName The dynamic feature module name as defined in bundle_config.yaml. If using
* Play Store dynamic feature delivery, this name corresponds to the root name on the
* @param moduleName The deferred component module name as defined in bundle_config.yaml. If using
* Play Store deferred component delivery, this name corresponds to the root name on the
* installed APKs in which to search for the desired shared library .so file.
*/
public abstract void loadDartLibrary(int loadingUnitId, String moduleName);
/**
* Uninstall the specified feature module.
* Request that the specified feature module be uninstalled.
*
* <p>Since uninstallation requires significant disk i/o, this method only signals the intent to
* uninstall. Actual uninstallation (eg, removal of assets and files) may occur at a later time.
* However, once uninstallation is requested, the deferred component should not be used anymore
* until {@link installDeferredComponent} is called again.
*
* <p>Uninstallation, once complete, removes downloaded files and will require redownloading to
* install again.
*
* <p>Both parameters are not always necessary to identify which module to uninstall. Asset-only
* modules do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be passed
* to download 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.
*
* @return false if no deferred component was found matching the input, true if an uninstall was
* successfully requested.
* @param loadingUnitId The unique identifier associated with a Dart deferred library.
* @param moduleName The deferred component module name as defined in bundle_config.yaml.
*/
public abstract void uninstallFeature(int loadingUnitId, String moduleName);
public abstract boolean uninstallDeferredComponent(int loadingUnitId, String moduleName);
/**
* Cleans up and releases resources. This object is no longer usable after calling this method.

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.embedding.engine.dynamicfeatures;
package io.flutter.embedding.engine.deferredcomponents;
import android.annotation.SuppressLint;
import android.content.Context;
@ -23,7 +23,7 @@ import com.google.android.play.core.splitinstall.model.SplitInstallErrorCode;
import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.systemchannels.DynamicFeatureChannel;
import io.flutter.embedding.engine.systemchannels.DeferredComponentChannel;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
@ -33,15 +33,15 @@ import java.util.Map;
import java.util.Queue;
/**
* Flutter default implementation of DynamicFeatureManager that downloads dynamic feature modules
* from the Google Play store.
* Flutter default implementation of DeferredComponentManager that downloads deferred component
* modules from the Google Play store.
*/
public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
private static final String TAG = "PlayStoreDynamicFeatureManager";
public class PlayStoreDeferredComponentManager implements DeferredComponentManager {
private static final String TAG = "PlayStoreDeferredComponentManager";
private @NonNull SplitInstallManager splitInstallManager;
private @Nullable FlutterJNI flutterJNI;
private @Nullable DynamicFeatureChannel channel;
private @Nullable DeferredComponentChannel channel;
private @NonNull Context context;
// Each request to install a feature module gets a session ID. These maps associate
// the session ID with the loading unit and module name that was requested.
@ -65,13 +65,14 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
String.format(
"Module \"%s\" (sessionId %d) install failed with: %s",
sessionIdToName.get(sessionId), sessionId, state.errorCode()));
flutterJNI.dynamicFeatureInstallFailure(
flutterJNI.deferredComponentInstallFailure(
sessionIdToLoadingUnitId.get(sessionId),
"Module install failed with " + state.errorCode(),
true);
if (channel != null) {
channel.completeInstallError(
sessionIdToName.get(sessionId), "Android Dynamic Feature failed to install.");
sessionIdToName.get(sessionId),
"Android Deferred Component failed to install.");
}
sessionIdToName.delete(sessionId);
sessionIdToLoadingUnitId.delete(sessionId);
@ -87,7 +88,7 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
sessionIdToName.get(sessionId), sessionId));
loadAssets(sessionIdToLoadingUnitId.get(sessionId), sessionIdToName.get(sessionId));
// We only load Dart shared lib for the loading unit id requested. Other loading units
// (if present) in the dynamic feature module are not loaded, but can be loaded by
// (if present) in the deferred component module are not loaded, but can be loaded by
// calling again with their loading unit id. If no valid loadingUnitId was included in
// the installation request such as for an asset only feature, then we can skip this.
if (sessionIdToLoadingUnitId.get(sessionId) > 0) {
@ -112,7 +113,7 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
if (channel != null) {
channel.completeInstallError(
sessionIdToName.get(sessionId),
"Android Dynamic Feature installation canceled.");
"Android Deferred Component installation canceled.");
}
sessionIdToName.delete(sessionId);
sessionIdToLoadingUnitId.delete(sessionId);
@ -146,7 +147,7 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
String.format(
"Module \"%s\" (sessionId %d) install requires user confirmation.",
sessionIdToName.get(sessionId), sessionId));
sessionIdToState.put(sessionId, "requires_user_confirmation");
sessionIdToState.put(sessionId, "requiresUserConfirmation");
break;
}
case SplitInstallSessionStatus.DOWNLOADING:
@ -186,7 +187,8 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
}
}
public PlayStoreDynamicFeatureManager(@NonNull Context context, @Nullable FlutterJNI flutterJNI) {
public PlayStoreDeferredComponentManager(
@NonNull Context context, @Nullable FlutterJNI flutterJNI) {
this.context = context;
this.flutterJNI = flutterJNI;
splitInstallManager = SplitInstallManagerFactory.create(context);
@ -206,13 +208,13 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
if (flutterJNI == null) {
Log.e(
TAG,
"No FlutterJNI provided. `setJNI` must be called on the DynamicFeatureManager before attempting to load dart libraries or invoking with platform channels.");
"No FlutterJNI provided. `setJNI` must be called on the DeferredComponentManager before attempting to load dart libraries or invoking with platform channels.");
return false;
}
return true;
}
public void setDynamicFeatureChannel(DynamicFeatureChannel channel) {
public void setDeferredComponentChannel(DeferredComponentChannel channel) {
this.channel = channel;
}
@ -226,13 +228,13 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
return context.getResources().getString(moduleNameIdentifier);
}
public void installDynamicFeature(int loadingUnitId, String moduleName) {
public void installDeferredComponent(int loadingUnitId, String moduleName) {
String resolvedModuleName =
moduleName != null ? moduleName : loadingUnitIdToModuleName(loadingUnitId);
if (resolvedModuleName == null) {
Log.e(
TAG,
"Dynamic feature module name was null and could not be resolved from loading unit id.");
"Deferred component module name was null and could not be resolved from loading unit id.");
return;
}
@ -260,26 +262,26 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
exception -> {
switch (((SplitInstallException) exception).getErrorCode()) {
case SplitInstallErrorCode.NETWORK_ERROR:
flutterJNI.dynamicFeatureInstallFailure(
flutterJNI.deferredComponentInstallFailure(
loadingUnitId,
String.format(
"Install of dynamic feature module \"%s\" failed with a network error",
"Install of deferred component module \"%s\" failed with a network error",
moduleName),
true);
break;
case SplitInstallErrorCode.MODULE_UNAVAILABLE:
flutterJNI.dynamicFeatureInstallFailure(
flutterJNI.deferredComponentInstallFailure(
loadingUnitId,
String.format(
"Install of dynamic feature module \"%s\" failed as it is unavailable",
"Install of deferred component module \"%s\" failed as it is unavailable",
moduleName),
false);
break;
default:
flutterJNI.dynamicFeatureInstallFailure(
flutterJNI.deferredComponentInstallFailure(
loadingUnitId,
String.format(
"Install of dynamic feature module \"%s\" failed with error %d: %s",
"Install of deferred component module \"%s\" failed with error %d: %s",
moduleName,
((SplitInstallException) exception).getErrorCode(),
((SplitInstallException) exception).getMessage()),
@ -289,17 +291,20 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
});
}
public String getDynamicFeatureInstallState(int loadingUnitId, String moduleName) {
public String getDeferredComponentInstallState(int loadingUnitId, String moduleName) {
String resolvedModuleName =
moduleName != null ? moduleName : loadingUnitIdToModuleName(loadingUnitId);
if (resolvedModuleName == null) {
Log.e(
TAG,
"Dynamic feature module name was null and could not be resolved from loading unit id.");
return null;
"Deferred component module name was null and could not be resolved from loading unit id.");
return "unknown";
}
if (!nameToSessionId.containsKey(resolvedModuleName)) {
return null;
if (splitInstallManager.getInstalledModules().contains(resolvedModuleName)) {
return "installedPendingLoad";
}
return "unknown";
}
int sessionId = nameToSessionId.get(resolvedModuleName);
return sessionIdToState.get(sessionId);
@ -309,7 +314,7 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
if (!verifyJNI()) {
return;
}
// Since android dynamic feature asset manager is handled through
// Since android deferred component asset manager is handled through
// context, neither parameter is used here. Assets are stored in
// the apk's `assets` directory allowing them to be accessed by
// Android's AssetManager directly.
@ -386,8 +391,20 @@ public class PlayStoreDynamicFeatureManager implements DynamicFeatureManager {
loadingUnitId, searchPaths.toArray(new String[apkPaths.size()]));
}
public void uninstallFeature(int loadingUnitId, String moduleName) {
// TODO(garyq): support uninstalling.
public boolean uninstallDeferredComponent(int loadingUnitId, String moduleName) {
String resolvedModuleName =
moduleName != null ? moduleName : loadingUnitIdToModuleName(loadingUnitId);
if (resolvedModuleName == null) {
Log.e(
TAG,
"Deferred component module name was null and could not be resolved from loading unit id.");
return false;
}
List<String> modulesToUninstall = new ArrayList<>();
modulesToUninstall.add(resolvedModuleName);
splitInstallManager.deferredUninstall(modulesToUninstall);
sessionIdToState.delete(nameToSessionId.get(resolvedModuleName));
return true;
}
public void destroy() {

View File

@ -10,7 +10,7 @@ import androidx.annotation.VisibleForTesting;
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.deferredcomponents.DeferredComponentManager;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.StandardMethodCodec;
@ -21,15 +21,15 @@ import java.util.Map;
/**
* Method channel that handles manual installation requests and queries for installation state for
* dynamic feature modules.
* deferred component modules.
*
* <p>This channel is able to handle multiple simultaneous installation requests
*/
public class DynamicFeatureChannel {
private static final String TAG = "DynamicFeatureChannel";
public class DeferredComponentChannel {
private static final String TAG = "DeferredComponentChannel";
@NonNull private final MethodChannel channel;
@Nullable private DynamicFeatureManager dynamicFeatureManager;
@Nullable private DeferredComponentManager deferredComponentManager;
// Track the Result objects to be able to handle multiple install requests of
// the same module at a time. When installation enters a terminal state, either
// completeInstallSuccess or completeInstallError can be called.
@ -40,8 +40,8 @@ public class DynamicFeatureChannel {
new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
if (dynamicFeatureManager == null) {
// If no DynamicFeatureManager has been injected, then this channel is a no-op.
if (deferredComponentManager == null) {
// If no DeferredComponentManager has been injected, then this channel is a no-op.
return;
}
String method = call.method;
@ -50,16 +50,21 @@ public class DynamicFeatureChannel {
final int loadingUnitId = (int) args.get("loadingUnitId");
final String moduleName = (String) args.get("moduleName");
switch (method) {
case "installDynamicFeature":
dynamicFeatureManager.installDynamicFeature(loadingUnitId, moduleName);
case "installDeferredComponent":
deferredComponentManager.installDeferredComponent(loadingUnitId, moduleName);
if (!moduleNameToResults.containsKey(moduleName)) {
moduleNameToResults.put(moduleName, new ArrayList<>());
}
moduleNameToResults.get(moduleName).add(result);
break;
case "getDynamicFeatureInstallState":
case "getDeferredComponentInstallState":
result.success(
dynamicFeatureManager.getDynamicFeatureInstallState(loadingUnitId, moduleName));
deferredComponentManager.getDeferredComponentInstallState(
loadingUnitId, moduleName));
break;
case "uninstallDeferredComponent":
deferredComponentManager.uninstallDeferredComponent(loadingUnitId, moduleName);
result.success(null);
break;
default:
result.notImplemented();
@ -69,36 +74,38 @@ public class DynamicFeatureChannel {
};
/**
* Constructs a {@code DynamicFeatureChannel} that connects Android to the Dart code running in
* Constructs a {@code DeferredComponentChannel} that connects Android to the Dart code running in
* {@code dartExecutor}.
*
* <p>The given {@code dartExecutor} is permitted to be idle or executing code.
*
* <p>See {@link DartExecutor}.
*/
public DynamicFeatureChannel(@NonNull DartExecutor dartExecutor) {
public DeferredComponentChannel(@NonNull DartExecutor dartExecutor) {
this.channel =
new MethodChannel(dartExecutor, "flutter/dynamicfeature", StandardMethodCodec.INSTANCE);
new MethodChannel(dartExecutor, "flutter/deferredcomponent", StandardMethodCodec.INSTANCE);
channel.setMethodCallHandler(parsingMethodHandler);
dynamicFeatureManager = FlutterInjector.instance().dynamicFeatureManager();
deferredComponentManager = FlutterInjector.instance().deferredComponentManager();
moduleNameToResults = new HashMap<>();
}
/**
* Sets the DynamicFeatureManager to exectue method channel calls with.
* Sets the DeferredComponentManager to exectue method channel calls with.
*
* @param dynamicFeatureManager the DynamicFeatureManager to use.
* @param deferredComponentManager the DeferredComponentManager to use.
*/
@VisibleForTesting
public void setDynamicFeatureManager(@Nullable DynamicFeatureManager dynamicFeatureManager) {
this.dynamicFeatureManager = dynamicFeatureManager;
public void setDeferredComponentManager(
@Nullable DeferredComponentManager deferredComponentManager) {
this.deferredComponentManager = deferredComponentManager;
}
/**
* Finishes the `installDynamicFeature` method channel call for the specified moduleName with a
* Finishes the `installDeferredComponent` method channel call for the specified moduleName with a
* success.
*
* @param moduleName The name of the android dynamic feature module install request to complete.
* @param moduleName The name of the android deferred component module install request to
* complete.
*/
public void completeInstallSuccess(String moduleName) {
if (moduleNameToResults.containsKey(moduleName)) {
@ -111,16 +118,17 @@ public class DynamicFeatureChannel {
}
/**
* Finishes the `installDynamicFeature` method channel call for the specified moduleName with an
* error/failure.
* Finishes the `installDeferredComponent` method channel call for the specified moduleName with
* an error/failure.
*
* @param moduleName The name of the android dynamic feature module install request to complete.
* @param moduleName The name of the android deferred component module install request to
* complete.
* @param errorMessage The error message to display to complete the future with.
*/
public void completeInstallError(String moduleName, String errorMessage) {
if (moduleNameToResults.containsKey(moduleName)) {
for (MethodChannel.Result result : moduleNameToResults.get(moduleName)) {
result.error("DynamicFeature Install failure", errorMessage, null);
result.error("DeferredComponent Install failure", errorMessage, null);
}
moduleNameToResults.get(moduleName).clear();
}

View File

@ -522,11 +522,11 @@ static void LoadLoadingUnitFailure(intptr_t loading_unit_id,
// TODO(garyq): Implement
}
static void DynamicFeatureInstallFailure(JNIEnv* env,
jobject obj,
jint jLoadingUnitId,
jstring jError,
jboolean jTransient) {
static void DeferredComponentInstallFailure(JNIEnv* env,
jobject obj,
jint jLoadingUnitId,
jstring jError,
jboolean jTransient) {
LoadLoadingUnitFailure(static_cast<intptr_t>(jLoadingUnitId),
fml::jni::JavaStringToString(env, jError),
static_cast<bool>(jTransient));
@ -754,9 +754,9 @@ bool RegisterApi(JNIEnv* env) {
.fnPtr = reinterpret_cast<void*>(&UpdateJavaAssetManager),
},
{
.name = "nativeDynamicFeatureInstallFailure",
.name = "nativeDeferredComponentInstallFailure",
.signature = "(ILjava/lang/String;Z)V",
.fnPtr = reinterpret_cast<void*>(&DynamicFeatureInstallFailure),
.fnPtr = reinterpret_cast<void*>(&DeferredComponentInstallFailure),
},
};

View File

@ -9,7 +9,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import io.flutter.embedding.engine.dynamicfeatures.PlayStoreDynamicFeatureManager;
import io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManager;
import io.flutter.embedding.engine.loader.FlutterLoader;
import org.junit.Before;
import org.junit.Test;
@ -23,7 +23,7 @@ import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
public class FlutterInjectorTest {
@Mock FlutterLoader mockFlutterLoader;
@Mock PlayStoreDynamicFeatureManager mockDynamicFeatureManager;
@Mock PlayStoreDeferredComponentManager mockDeferredComponentManager;
@Before
public void setUp() {
@ -37,7 +37,7 @@ public class FlutterInjectorTest {
// Implicitly builds when first accessed.
FlutterInjector injector = FlutterInjector.instance();
assertNotNull(injector.flutterLoader());
assertNull(injector.dynamicFeatureManager());
assertNull(injector.deferredComponentManager());
}
@Test
@ -49,11 +49,13 @@ public class FlutterInjectorTest {
}
@Test
public void canInjectDynamicFeatureManager() {
public void canInjectDeferredComponentManager() {
FlutterInjector.setInstance(
new FlutterInjector.Builder().setDynamicFeatureManager(mockDynamicFeatureManager).build());
new FlutterInjector.Builder()
.setDeferredComponentManager(mockDeferredComponentManager)
.build());
FlutterInjector injector = FlutterInjector.instance();
assertEquals(injector.dynamicFeatureManager(), mockDynamicFeatureManager);
assertEquals(injector.deferredComponentManager(), mockDeferredComponentManager);
}
@Test()

View File

@ -18,12 +18,13 @@ import io.flutter.embedding.engine.LocalizationPluginTest;
import io.flutter.embedding.engine.RenderingComponentTest;
import io.flutter.embedding.engine.dart.DartExecutorTest;
import io.flutter.embedding.engine.dart.DartMessengerTest;
import io.flutter.embedding.engine.dynamicfeatures.PlayStoreDynamicFeatureManagerTest;
import io.flutter.embedding.engine.deferredcomponents.PlayStoreDeferredComponentManagerTest;
import io.flutter.embedding.engine.loader.ApplicationInfoLoaderTest;
import io.flutter.embedding.engine.loader.FlutterLoaderTest;
import io.flutter.embedding.engine.mutatorsstack.FlutterMutatorViewTest;
import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistryTest;
import io.flutter.embedding.engine.renderer.FlutterRendererTest;
import io.flutter.embedding.engine.systemchannels.DeferredComponentChannelTest;
import io.flutter.embedding.engine.systemchannels.KeyEventChannelTest;
import io.flutter.embedding.engine.systemchannels.PlatformChannelTest;
import io.flutter.embedding.engine.systemchannels.RestorationChannelTest;
@ -71,6 +72,7 @@ import test.io.flutter.embedding.engine.PluginComponentTest;
FlutterShellArgsTest.class,
FlutterViewTest.class,
InputConnectionAdaptorTest.class,
DeferredComponentChannelTest.class,
KeyEventChannelTest.class,
ListenableEditingStateTest.class,
LocalizationPluginTest.class,
@ -78,7 +80,7 @@ import test.io.flutter.embedding.engine.PluginComponentTest;
PlatformChannelTest.class,
PlatformPluginTest.class,
PlatformViewsControllerTest.class,
PlayStoreDynamicFeatureManagerTest.class,
PlayStoreDeferredComponentManagerTest.class,
PluginComponentTest.class,
PreconditionsTest.class,
RenderingComponentTest.class,

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.embedding.engine.dynamicfeatures;
package io.flutter.embedding.engine.deferredcomponents;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
@ -25,11 +25,11 @@ import org.robolectric.annotation.Config;
@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class PlayStoreDynamicFeatureManagerTest {
public class PlayStoreDeferredComponentManagerTest {
private class TestFlutterJNI extends FlutterJNI {
public int loadDartDeferredLibraryCalled = 0;
public int updateAssetManagerCalled = 0;
public int dynamicFeatureInstallFailureCalled = 0;
public int deferredComponentInstallFailureCalled = 0;
public String[] searchPaths;
public int loadingUnitId;
public AssetManager assetManager;
@ -52,20 +52,20 @@ public class PlayStoreDynamicFeatureManagerTest {
}
@Override
public void dynamicFeatureInstallFailure(
public void deferredComponentInstallFailure(
int loadingUnitId, @NonNull String error, boolean isTransient) {
dynamicFeatureInstallFailureCalled++;
deferredComponentInstallFailureCalled++;
}
}
// Skips the download process to directly call the loadAssets and loadDartLibrary methods.
private class TestPlayStoreDynamicFeatureManager extends PlayStoreDynamicFeatureManager {
public TestPlayStoreDynamicFeatureManager(Context context, FlutterJNI jni) {
private class TestPlayStoreDeferredComponentManager extends PlayStoreDeferredComponentManager {
public TestPlayStoreDeferredComponentManager(Context context, FlutterJNI jni) {
super(context, jni);
}
@Override
public void installDynamicFeature(int loadingUnitId, String moduleName) {
public void installDeferredComponent(int loadingUnitId, String moduleName) {
// Override this to skip the online SplitInstallManager portion.
loadAssets(loadingUnitId, moduleName);
loadDartLibrary(loadingUnitId, moduleName);
@ -80,15 +80,15 @@ public class PlayStoreDynamicFeatureManagerTest {
doReturn(null).when(spyContext).getAssets();
String soTestPath = "test/path/app.so-123.part.so";
doReturn(new File(soTestPath)).when(spyContext).getFilesDir();
TestPlayStoreDynamicFeatureManager playStoreManager =
new TestPlayStoreDynamicFeatureManager(spyContext, jni);
jni.setDynamicFeatureManager(playStoreManager);
TestPlayStoreDeferredComponentManager playStoreManager =
new TestPlayStoreDeferredComponentManager(spyContext, jni);
jni.setDeferredComponentManager(playStoreManager);
assertEquals(jni.loadingUnitId, 0);
playStoreManager.installDynamicFeature(123, "TestModuleName");
playStoreManager.installDeferredComponent(123, "TestModuleName");
assertEquals(jni.loadDartDeferredLibraryCalled, 1);
assertEquals(jni.updateAssetManagerCalled, 1);
assertEquals(jni.dynamicFeatureInstallFailureCalled, 0);
assertEquals(jni.deferredComponentInstallFailureCalled, 0);
assertTrue(jni.searchPaths[0].endsWith(soTestPath));
assertEquals(jni.searchPaths.length, 1);
@ -103,16 +103,16 @@ public class PlayStoreDynamicFeatureManagerTest {
doReturn(null).when(spyContext).getAssets();
String apkTestPath = "test/path/TestModuleName_armeabi_v7a.apk";
doReturn(new File(apkTestPath)).when(spyContext).getFilesDir();
TestPlayStoreDynamicFeatureManager playStoreManager =
new TestPlayStoreDynamicFeatureManager(spyContext, jni);
jni.setDynamicFeatureManager(playStoreManager);
TestPlayStoreDeferredComponentManager playStoreManager =
new TestPlayStoreDeferredComponentManager(spyContext, jni);
jni.setDeferredComponentManager(playStoreManager);
assertEquals(jni.loadingUnitId, 0);
playStoreManager.installDynamicFeature(123, "TestModuleName");
playStoreManager.installDeferredComponent(123, "TestModuleName");
assertEquals(jni.loadDartDeferredLibraryCalled, 1);
assertEquals(jni.updateAssetManagerCalled, 1);
assertEquals(jni.dynamicFeatureInstallFailureCalled, 0);
assertEquals(jni.deferredComponentInstallFailureCalled, 0);
assertTrue(jni.searchPaths[0].endsWith(apkTestPath + "!lib/armeabi-v7a/app.so-123.part.so"));
assertEquals(jni.searchPaths.length, 1);
@ -127,16 +127,16 @@ public class PlayStoreDynamicFeatureManagerTest {
doReturn(null).when(spyContext).getAssets();
String apkTestPath = "test/path/invalidpath.apk";
doReturn(new File(apkTestPath)).when(spyContext).getFilesDir();
TestPlayStoreDynamicFeatureManager playStoreManager =
new TestPlayStoreDynamicFeatureManager(spyContext, jni);
jni.setDynamicFeatureManager(playStoreManager);
TestPlayStoreDeferredComponentManager playStoreManager =
new TestPlayStoreDeferredComponentManager(spyContext, jni);
jni.setDeferredComponentManager(playStoreManager);
assertEquals(jni.loadingUnitId, 0);
playStoreManager.installDynamicFeature(123, "TestModuleName");
playStoreManager.installDeferredComponent(123, "TestModuleName");
assertEquals(jni.loadDartDeferredLibraryCalled, 1);
assertEquals(jni.updateAssetManagerCalled, 1);
assertEquals(jni.dynamicFeatureInstallFailureCalled, 0);
assertEquals(jni.deferredComponentInstallFailureCalled, 0);
assertEquals(jni.searchPaths.length, 0);
assertEquals(jni.loadingUnitId, 123);
@ -150,17 +150,27 @@ public class PlayStoreDynamicFeatureManagerTest {
AssetManager assetManager = spyContext.getAssets();
String apkTestPath = "blah doesn't matter here";
doReturn(new File(apkTestPath)).when(spyContext).getFilesDir();
TestPlayStoreDynamicFeatureManager playStoreManager =
new TestPlayStoreDynamicFeatureManager(spyContext, jni);
jni.setDynamicFeatureManager(playStoreManager);
TestPlayStoreDeferredComponentManager playStoreManager =
new TestPlayStoreDeferredComponentManager(spyContext, jni);
jni.setDeferredComponentManager(playStoreManager);
assertEquals(jni.loadingUnitId, 0);
playStoreManager.installDynamicFeature(123, "TestModuleName");
playStoreManager.installDeferredComponent(123, "TestModuleName");
assertEquals(jni.loadDartDeferredLibraryCalled, 1);
assertEquals(jni.updateAssetManagerCalled, 1);
assertEquals(jni.dynamicFeatureInstallFailureCalled, 0);
assertEquals(jni.deferredComponentInstallFailureCalled, 0);
assertEquals(jni.assetManager, assetManager);
}
@Test
public void stateGetterReturnsUnknowByDefault() throws NameNotFoundException {
TestFlutterJNI jni = new TestFlutterJNI();
Context spyContext = spy(RuntimeEnvironment.systemContext);
doReturn(spyContext).when(spyContext).createPackageContext(any(), anyInt());
TestPlayStoreDeferredComponentManager playStoreManager =
new TestPlayStoreDeferredComponentManager(spyContext, jni);
assertEquals(playStoreManager.getDeferredComponentInstallState(-1, "invalidName"), "unknown");
}
}

View File

@ -0,0 +1,123 @@
package io.flutter.embedding.engine.systemchannels;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.content.res.AssetManager;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
class TestDeferredComponentManager implements DeferredComponentManager {
DeferredComponentChannel channel;
String moduleName;
public void setJNI(FlutterJNI flutterJNI) {}
public void setDeferredComponentChannel(DeferredComponentChannel channel) {
this.channel = channel;
}
public void installDeferredComponent(int loadingUnitId, String moduleName) {
this.moduleName = moduleName;
}
public void completeInstall() {
channel.completeInstallSuccess(moduleName);
}
public String getDeferredComponentInstallState(int loadingUnitId, String moduleName) {
return "installed";
}
public void loadAssets(int loadingUnitId, String moduleName) {}
public void loadDartLibrary(int loadingUnitId, String moduleName) {}
public boolean uninstallDeferredComponent(int loadingUnitId, String moduleName) {
return true;
}
public void destroy() {}
}
@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class DeferredComponentChannelTest {
@Test
public void deferredComponentChannel_installCompletesResults() {
MethodChannel rawChannel = mock(MethodChannel.class);
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class));
TestDeferredComponentManager testDeferredComponentManager = new TestDeferredComponentManager();
DeferredComponentChannel fakeDeferredComponentChannel =
new DeferredComponentChannel(dartExecutor);
fakeDeferredComponentChannel.setDeferredComponentManager(testDeferredComponentManager);
testDeferredComponentManager.setDeferredComponentChannel(fakeDeferredComponentChannel);
Map<String, Object> args = new HashMap<>();
args.put("loadingUnitId", -1);
args.put("moduleName", "hello");
MethodCall methodCall = new MethodCall("installDeferredComponent", args);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
fakeDeferredComponentChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult);
testDeferredComponentManager.completeInstall();
verify(mockResult).success(null);
}
@Test
public void deferredComponentChannel_installCompletesMultipleResults() {
MethodChannel rawChannel = mock(MethodChannel.class);
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class));
TestDeferredComponentManager testDeferredComponentManager = new TestDeferredComponentManager();
DeferredComponentChannel fakeDeferredComponentChannel =
new DeferredComponentChannel(dartExecutor);
fakeDeferredComponentChannel.setDeferredComponentManager(testDeferredComponentManager);
testDeferredComponentManager.setDeferredComponentChannel(fakeDeferredComponentChannel);
Map<String, Object> args = new HashMap<>();
args.put("loadingUnitId", -1);
args.put("moduleName", "hello");
MethodCall methodCall = new MethodCall("installDeferredComponent", args);
MethodChannel.Result mockResult1 = mock(MethodChannel.Result.class);
MethodChannel.Result mockResult2 = mock(MethodChannel.Result.class);
fakeDeferredComponentChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult1);
fakeDeferredComponentChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult2);
testDeferredComponentManager.completeInstall();
verify(mockResult1).success(null);
verify(mockResult2).success(null);
}
@Test
public void deferredComponentChannel_getInstallState() {
MethodChannel rawChannel = mock(MethodChannel.class);
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class));
TestDeferredComponentManager testDeferredComponentManager = new TestDeferredComponentManager();
DeferredComponentChannel fakeDeferredComponentChannel =
new DeferredComponentChannel(dartExecutor);
fakeDeferredComponentChannel.setDeferredComponentManager(testDeferredComponentManager);
testDeferredComponentManager.setDeferredComponentChannel(fakeDeferredComponentChannel);
Map<String, Object> args = new HashMap<>();
args.put("loadingUnitId", -1);
args.put("moduleName", "hello");
MethodCall methodCall = new MethodCall("getDeferredComponentInstallState", args);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
fakeDeferredComponentChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult);
testDeferredComponentManager.completeInstall();
verify(mockResult).success("installed");
}
}

View File

@ -1,115 +0,0 @@
package io.flutter.embedding.engine.systemchannels;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.content.res.AssetManager;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
class TestDynamicFeatureManager implements DynamicFeatureManager {
DynamicFeatureChannel channel;
String moduleName;
public void setJNI(FlutterJNI flutterJNI) {}
public void setDynamicFeatureChannel(DynamicFeatureChannel channel) {
this.channel = channel;
}
public void installDynamicFeature(int loadingUnitId, String moduleName) {
this.moduleName = moduleName;
}
public void completeInstall() {
channel.completeInstallSuccess(moduleName);
}
public String getDynamicFeatureInstallState(int loadingUnitId, String moduleName) {
return "installed";
}
public void loadAssets(int loadingUnitId, String moduleName) {}
public void loadDartLibrary(int loadingUnitId, String moduleName) {}
public void uninstallFeature(int loadingUnitId, String moduleName) {}
public void destroy() {}
}
@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class DynamicFeatureChannelTest {
@Test
public void dynamicFeatureChannel_installCompletesResults() {
MethodChannel rawChannel = mock(MethodChannel.class);
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class));
TestDynamicFeatureManager testDynamicFeatureManager = new TestDynamicFeatureManager();
DynamicFeatureChannel fakeDynamicFeatureChannel = new DynamicFeatureChannel(dartExecutor);
fakeDynamicFeatureChannel.setDynamicFeatureManager(testDynamicFeatureManager);
Map<String, Object> args = new HashMap<>();
args.put("loadingUnitId", -1);
args.put("moduleName", "hello");
MethodCall methodCall = new MethodCall("installDynamicFeature", args);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
fakeDynamicFeatureChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult);
testDynamicFeatureManager.completeInstall();
verify(mockResult).success(null);
}
@Test
public void dynamicFeatureChannel_installCompletesMultipleResults() {
MethodChannel rawChannel = mock(MethodChannel.class);
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class));
TestDynamicFeatureManager testDynamicFeatureManager = new TestDynamicFeatureManager();
DynamicFeatureChannel fakeDynamicFeatureChannel = new DynamicFeatureChannel(dartExecutor);
fakeDynamicFeatureChannel.setDynamicFeatureManager(testDynamicFeatureManager);
Map<String, Object> args = new HashMap<>();
args.put("loadingUnitId", -1);
args.put("moduleName", "hello");
MethodCall methodCall = new MethodCall("installDynamicFeature", args);
MethodChannel.Result mockResult1 = mock(MethodChannel.Result.class);
MethodChannel.Result mockResult2 = mock(MethodChannel.Result.class);
fakeDynamicFeatureChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult1);
fakeDynamicFeatureChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult2);
testDynamicFeatureManager.completeInstall();
verify(mockResult1).success(null);
verify(mockResult2).success(null);
}
@Test
public void dynamicFeatureChannel_getInstallState() {
MethodChannel rawChannel = mock(MethodChannel.class);
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class));
TestDynamicFeatureManager testDynamicFeatureManager = new TestDynamicFeatureManager();
DynamicFeatureChannel fakeDynamicFeatureChannel = new DynamicFeatureChannel(dartExecutor);
fakeDynamicFeatureChannel.setDynamicFeatureManager(testDynamicFeatureManager);
Map<String, Object> args = new HashMap<>();
args.put("loadingUnitId", -1);
args.put("moduleName", "hello");
MethodCall methodCall = new MethodCall("getDynamicFeatureInstallState", args);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
fakeDynamicFeatureChannel.parsingMethodHandler.onMethodCall(methodCall, mockResult);
testDynamicFeatureManager.completeInstall();
verify(mockResult).success("installed");
}
}