mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Rename DynamicFeature->DeferredComponent and impl uninstall DeferredComponents (flutter/engine#23224)
This commit is contained in:
parent
4ed7eb1c38
commit
e4fa163b51
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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. */
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
@ -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() {
|
||||
@ -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();
|
||||
}
|
||||
@ -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),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user