blob: e505feb8541ae5e8068244cce1c7e886a6b17c9e [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// 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.deferredcomponents;
import io.flutter.embedding.engine.FlutterJNI;
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 deferred components.
*
* <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 PlayStoreDeferredComponentManager.
*
* <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 installDeferredComponent. Once the component 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 installDeferredComponent implementation should
* call these two methods as needed.
*
* <p>A deferred component is uniquely identified by a component name as defined in
* bundle_config.yaml. Each component may contain one or more loading units, uniquely identified by
* the loading unit ID and assets.
*/
public interface DeferredComponentManager {
/**
* Sets the FlutterJNI to be used to communication with the Flutter native engine.
*
* <p>A FlutterJNI is required in order to properly execute loadAssets and loadDartLibrary.
*
* <p>Since this class may be instantiated for injection before the FlutterEngine and FlutterJNI
* is fully initialized, this method should be called to provide the FlutterJNI instance to use
* for use in loadDartLibrary and loadAssets.
*/
public abstract void setJNI(FlutterJNI flutterJNI);
/**
* Sets the DeferredComponentChannel system channel to handle the framework API to directly call
* methods in DeferredComponentManager.
*
* <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 DeferredComponentChannel.
* Similarly, the {@link DeferredComponentChannel.setDeferredComponentManager} method should also
* be called with this DeferredComponentManager instance to properly forward method invocations.
*
* <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, successful
* installations should notify the DeferredComponentChannel by calling {@link
* DeferredComponentChannel.completeInstallSuccess} while errors and failures should call {@link
* DeferredComponentChannel.completeInstallError}.
*/
public abstract void setDeferredComponentChannel(DeferredComponentChannel channel);
/**
* Request that the deferred component be downloaded and installed.
*
* <p>This method begins the download and installation of the specified deferred component. For
* example, the Play Store dynamic delivery implementation uses SplitInstallManager to request the
* download of the component. 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 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 component to install. Asset-only
* components do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be
* passed to download only with componentName. On the other hand, it can be possible to resolve
* the componentName based on the loadingUnitId. This resolution is done if componentName is null.
* At least one of loadingUnitId or componentName must be valid or non-null.
*
* <p>Flutter will typically call this method in two ways. When invoked as part of a dart
* `loadLibrary()` call, a valid loadingUnitId is passed in while the componentName is null. In
* this case, this method is responsible for figuring out what component the loadingUnitId
* corresponds to.
*
* <p>When invoked manually as part of loading an assets-only component, loadingUnitId is -1
* (invalid) and componentName is supplied. Without a loadingUnitId, this method just downloads
* the component by name and attempts to load assets via loadAssets while loadDartLibrary is
* skipped, even if the deferred component 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 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 deferred component is an assets-only component. If a
* negative loadingUnitId is passed, then componentName 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 componentName The deferred component 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 componentName. When loading asset-only or other deferred components without
* an associated Dart deferred library, loading unit id should a negative value and
* componentName must be non-null.
*/
public abstract void installDeferredComponent(int loadingUnitId, String componentName);
/**
* Gets the current state of the installation session corresponding to the specified loadingUnitId
* and/or componentName.
*
* <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 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 deferred component has been installed or requested to be installed by the provided
* loadingUnitId or componentName, then this method will return null.
*
* <p>Depending on the implementation, the returned String may vary. The Play store default
* implementation begins in the "requested" state before transitioning to the "downloading" and
* "installed" states.
*
* <p>Only successfully requested components have state. Modules that are invalid or have not been
* requested with {@link installDeferredComponent} will not have a state. Due to the asynchronous
* nature of the download process, components may not immediately have a valid state upon return
* of {@link installDeferredComponent}, though valid components will eventually obtain a state.
*
* <p>Both parameters are not always necessary to identify which component to install. Asset-only
* components do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be
* passed to query only with componentName. On the other hand, it can be possible to resolve the
* componentName based on the loadingUnitId. This resolution is done if componentName is null. At
* least one of loadingUnitId or componentName must be valid or non-null.
*
* @param loadingUnitId The unique identifier associated with a Dart deferred library.
* @param componentName The deferred component name as defined in bundle_config.yaml.
*/
public abstract String getDeferredComponentInstallState(int loadingUnitId, String componentName);
/**
* Extract and load any assets and resources from the deferred component 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 deferred component, then
* nothing needs to be done.
*
* <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 componentName, 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 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 componentName The deferred component name as defined in bundle_config.yaml.
*/
public abstract void loadAssets(int loadingUnitId, String componentName);
/**
* Load the .so shared library file into the Dart VM.
*
* <p>When the download of a deferred component 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.
*
* <p>Specifically, APKs distributed by Android's app bundle format may vary by device and API
* number, so FlutterJNI's loadDartDeferredLibrary accepts a list of search paths with can include
* paths within APKs that have not been unpacked using the
* `path/to/apk.apk!path/inside/apk/lib.so` format. Each search path will be attempted in order
* until a shared library is found. This allows for the developer to avoid unpacking the apk zip.
*
* <p>Upon successful load of the Dart library, the Dart future from the originating loadLibary()
* call completes and developers are able to use symbols and assets from the deferred component.
*
* @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. Negative loading unit
* ids are considered invalid and this method will result in a no-op.
* @param componentName The deferred component 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 componentName);
/**
* Request that the specified component 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 component to uninstall.
* Asset-only components do not have an associated loadingUnitId. Instead, an invalid ID like -1
* may be passed to download only with componentName. On the other hand, it can be possible to
* resolve the componentName based on the loadingUnitId. This resolution is done if componentName
* is null. At least one of loadingUnitId or componentName 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 componentName The deferred component name as defined in bundle_config.yaml.
*/
public abstract boolean uninstallDeferredComponent(int loadingUnitId, String componentName);
/**
* Cleans up and releases resources. This object is no longer usable after calling this method.
*/
public abstract void destroy();
}